题目: 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明: 你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例:
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
一、哈希表 O(N) S(N)
这个思路很简单:
- 先利用哈希表存储数字和数字出现次数,<key,value>,key保存数值,value保存该数字出现次数;每次hash[nums[i]]++表示出现次数。
- 遍历哈希表,可以选择迭代器遍历,或者使用auto变量即可以保存任何变量类型的变量来保存hash<key,value>,判断value的值是否等于1,i.first得到key,i.second得到value,如果value==1,那么返回key即为答案。
int singleNumber(vector<int>& nums)
{
unordered_map<int,int>hash;
for(int i=0;i<nums.size();i++)
{
hash[nums[i]]++;
}
for(auto i:hash)//遍历哈希表
{
if(i.second==1)//判断出现次数
{
return i.first;
}
}
return 0;
}
但是这个解法使用了额外的空间,所以虽然可以通过但不是最优的。
二、排序 O(NlogN)
不使用额外空间的前提下,我们可以对数组进行排序:
- 利用C++自带函数对数组排序,那么相同的数字就会在集中在一起。
- 两两判断是否相等,相等表示出现了2次,否则返回nums[i]即可,表示它是出现一次的。
- 注意,防止最后一个数字为出现一次的,要进行对i的判断,判断它是否为最后一个数字,是表示出现了一次则返回即可。
int singleNumber(vector<int>& nums)
{
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size();i+=2)
{
if( i==nums.size()-1||nums[i]!=nums[i+1] )//最后一个数字防止越界
{
return nums[i];
}
}
return 0;
}
排序最快的时间复杂度为O(logN),虽然可以通过并不是线性时间复杂度。
三、最佳算法:位运算 O(N) S(1)
一定要记住,如果题目对时间,空间做出要求,利用一般解法无法做出,那么要想到位运算,特别是异或。我们先看一下异或的性质:
- 交换律:p⊕q=q⊕p
- 结合律:p⊕(q⊕r)=(p⊕q)⊕r
- 恒等率:p⊕0=p
- 归零率:p⊕p=0
那这个题目,只有一个数字出现了1次,其他都是2次,我们可以利用结合律,将出现偶数次数的数字异或,那么结果为0,如1⊕1=0;再和出现一次的⊕,那么0⊕2=2,我们便可以找到这个重复的数字。记住,我们不用自己去调用结合律,系统自己会处理,如:数字【2,1,2】找出现一次的:
代码如下:
//位运算异或交换律:1^1=0,1^0=1,1^2^1=1^1^2=2这样就可以找到只出现一个的那个
int singleNumber(vector<int>& nums)
{
int sum=nums[0];;
for(int i=1;i<nums.size();i++)
{
sum=nums[i]^sum;
}
return sum;
}
这个就是最佳解法,要牢记⊕的特性。
加油哦!💪。