给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
方法 1:集合操作
算法
遍历nums 中的每一个元素
如果某个nums 中的数字是新出现的,则将它添加到集合中
如果某个数字已经在集合中,删除它
#include<iostream>
#include<set>
#include<iterator>
using namespace std;
set<int>st;
int main(){
int num;
set<int>::iterator it= st.begin();
while (cin>>num){
it= st.find(num);
if(it==st.end())
st.insert(num);
else
st.erase(it);
}
for (it = st.begin(); it != st.end(); it++)
cout << *it << " ";
}
关于Set 的STL 知识点:https://www.cnblogs.com/BlairGrowing/p/12709247.html
复杂度分析
时间复杂度:O(n2) 我们遍历nums 花费 O(n)的时间。我们还要在列表中遍历判断是否存在这个数字,花费 O(n)的时间,所以总循环时间为 O(n2)
空间复杂度:O(n) 。我们需要一个大小为 n 的列表保存所有的 nums 中元素。
方法 2:哈希表
算法
我们用哈希表避免每次查找元素是否存在需要的 O(n)时间。
遍历 nums 中的所有元素并建立键/值对。
返回只出现一次的元素。
int singleNumber(vector<int>& nums) {
map<int,int> m;
for(int i=0; i<nums.size();i++){
if(m.find(nums[i]) != m.end()){
m.erase(m.find(nums[i]));
}else{
m[nums[i]] = 1;
}
}
return m.begin()->first;
}
int main(){
int n;
int arr[] = {4,1,2,1,2};
vector<int> nums(arr, arr + sizeof(arr) / sizeof(int));
cout<<singleNumber(nums);
}
复杂度分析
时间复杂度:O(n⋅1)=O(n) 。for 循环的时间复杂度是 O(n) 的。Python 中哈希表的 pop 操作时间复杂度为O(1)。
空间复杂度: O(n) 。hash_table 需要的空间与 nums 中元素个数相等。
方法 3:数学
概念
2 * (a + b + c) - (a + a + b + b + c) =c
set<int>st;
int sum=0;
int arr[] = {4,1,2,1,2};
set<int>::iterator it;
st.insert(arr,arr+5);
for (it = st.begin(); it != st.end(); it++)
sum+=*it;
sum*=2;
for (int i = 0; i < 5; i++)
{
sum-=arr[i];
}
cout<<sum<<endl;
方法 4:按位异或
按位异或(xor)操作符
按位操作符的字面意思很好理解,即对值的二进制格式进行处理的操作符。而异或的作用为:假设有值甲、乙,当甲乙值相等时,异或操作后结果为不等(False,0),反之,为相等(True,1)。所以按位异或操作符的释义便显而易见了:对某值的每个位上的值(0或1)进行异或操作。
举一例例子:
甲:0 0 0 0 1 1 0 0 (值为12)
乙:0 0 0 0 0 1 1 1 (值为7)
甲和乙进行按位异或操作得到新值丙
丙:0 0 0 0 1 0 1 1 (值为11)
可见,当两值的某一位的值不同时,按位异或操作后所得新值某一位的值将为 1 (如从右到左第一位),反之为 0 (如从右到左第三位)。
既然有异或操作符,那么很可能会有同按位的操作符,但我就不再一一介绍了,毕竟我介绍按位异或操作符只是为显得我写的题解内容翔(空)实(洞)。
逻辑思路
有了按位异或操作符,我们就可以依照其独特的性质,敲除一段极其华丽的代码。
但题解是题解,不是我擅长的搬砖,我得先把逻辑思路捋清。
有一个数组,含有值:12,7,12。并含有一个变量甲,其值为0。(先不要纠结为何要这么定义,这是为了了解按位异或操作符的神奇性质)
对数组进行遍历,且遍历所得的每个值都和变量甲进行一次按位异或操作。
——————第一次:
12:0 0 0 0 1 1 0 0
甲:0 0 0 0 0 0 0 0
异或
甲:0 0 0 0 1 1 0 0(值为12)
——————第二次
7:0 0 0 0 0 1 1 1
甲:0 0 0 0 1 1 0 0
异或
甲:0 0 0 0 1 0 1 1(值为11)
——————第三次
12:0 0 0 0 1 1 0 0
甲:0 0 0 0 1 0 1 1
异或
甲:0 0 0 0 0 1 1 1(值为7)
——————遍历结束
返回值:甲。甲值为 7 ,数组中唯一元素为 7。
代码:
int main(){
int n;
set<int>st;
int ans=0;
int arr[] = {4,1,2,1,2};
for(int i=0;i<5;i++){
ans^=arr[i];
}
cout<<ans<<endl;
}