Leetcode 136: 只出现一次的数字
题目描述
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
我的代码
解法1:先进行sort排序
class Solution {
static bool cmp(const int &a, const int &b){
return(a<b);
}
public:
int singleNumber(vector<int>& nums){
sort(nums.begin(), nums.end(), cmp);
int i;
for(i=0; i<nums.size() - 1; i+=2){
if(nums[i] != nums[i+1]){return nums[i];}
}
return nums[i];
}
};
解法2:利用map(但是我的思路不对,代码见下)
我的想法是把nums容器中的元素作为键存进哈希表,在每一个新元素存进来之前判断一下哈希表中有没有键等于这个元素的值,如果有的话则删除这个键,没有的话把这个值作为键存进来。最后得到的是唯一一个键值对,然后返回该值。
class Solution {
public:
int singleNumber(vector<int>& nums) {
unordered_map<int, int> m;
for(int i=0; i<nums.size(); i++){
auto it = m.find(nums[i]);
if(it == m.end()){
m[nums[i]] = nums[i];
}
else{
m.erase(it);
}
}
return m[0];
}
};
程序运行结果:
测试容器是nums={2,2,1},执行结果是输出为0。
分析:代码中最后的返回不对,m[0]的意思是取键为0的值,并不是取出此关联容器map中的第一个元素,它并不是序列容器。当然这里并未存放等于0的键,因此0键对应的值就为默认初始化的0。
解法2:正确地使用map(运行时间和占用内存是硬伤)
若第一次出现,将此值存为键,值为1
第二次出现,将此键对应的值改为2
最后for循环查找值为1的键
class Solution {
public:
int singleNumber(vector<int>& nums) {
map<int,int> n;
int ans = 0;
for(int i = 0; i < nums.size(); ++ i){
n[nums[i]] == 1? n[nums[i]] = 2: n[nums[i]] = 1;
}
for(int j = 0; j < nums.size(); ++ j){
if(n[nums[j]] == 1) ans = nums[j];
}
return ans;
}
};
解法3:用set
若第一次出现,插入哈希集
第二次出现,冲哈希集内删除
最后剩下的就是那个只出现一次的数字
class Solution {
public:
int singleNumber(vector<int>& nums) {
unordered_set<int> s;
int a;
for(auto i: nums){
if(s.count(i)){s.erase(i);}
else{s.insert(i);}
}
for(auto i: s){a = i;}
return a;
}
};
解法4: 黑科技——异或
任何一个数字异或它自己都等于0。也就是说,如果我们从头到尾依次异或数组中的每一个数字,那么最终的结果刚好是那个只出现一次的数字,因为那些出现两次的数字全部在异或中抵消掉了。并且异或操作不受先后顺序的影响。
class Solution {
public:
int singleNumber(vector<int>& nums) {
int a = nums[0];
for(int i = 1; i<nums.size(); i++){a = a ^ nums[i];}
return a;
}
};