题目描述:一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
1. 分析
- 哈希表:用哈希表保存数组中的每个元素,key:数组中的每个元素;val:每种元素出现的次数。之后再遍历一遍哈希表即可。
- 异或:当只有一个数出现一次时,我们把数组中所有的数,依次异或运算,最后剩下的就是落单的数,因为成对儿出现的都抵消了。 依照这个思路,我们来看两个数(我们假设是AB)出现一次的数组。我们首先还是先异或,剩下的数字肯定是A、B异或的结果,这个结果的二进制中的1,表现的是A和B的不同的位。我们就取第一个1所在的位数,假设是第3位,接着把原数组分成两组,分组标准是第3位是否为1。如此,相同的数肯定在一个组,因为相同数字所有位都相同,而不同的数,肯定不在一组。然后把这两个组按照最开始的思路,依次异或,剩余的两个结果就是这两个只出现一次的数字。 注:0与任何数异或均是原数。
2. 用C++写出逻辑:
// 哈希表法:
vector<int> FindNumsAppearOnce(vector<int>& array) {
// write code here
unordered_map<int, int> umap;
int n = array.size();
vector<int>res;
if(n == 0) return res;
for(int i = 0; i < n; ++i){
umap[array[i]]++;
}
for(int i = 0; i < n; ++i){
if(umap[array[i]] == 1)
res.push_back(array[i]);
}
return res;
}
// 异或:
void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
if (data.size() < 2) return;
int totalNum = 0;
for (int i = 0; i < data.size(); i++) {
totalNum ^= data[i];//所有数异或,结果为不同的两个数字的异或
}
int sign = 0;//标志位,记录totalNum中的第一个1出现的位置
for (; sign < data.size(); sign++) {
if ((totalNum & (1 << sign)) != 0) { //左移 sign 位,将所有数字进行左移sign位,而低位补上0
break;
}
}
cout << sign << endl;
num1[0] = 0;
num2[0] = 0;
for (int i = 0; i < data.size(); i++) {
if ((data[i] & (1 << sign)) == 0) {//标志位为0的为一组,异或后必得到一个数字(这里注意==的优先级高于&,需在前面加())
num1[0] ^= data[i];
cout << "0 "<<data[i] << " " << (1<<sign) << endl;
}
else {
num2[0] ^= data[i];//标志位为1的为一组
cout << "1 " << data[i] << " " << (1 << sign) << endl;
}
}
cout << num1[0] << num2[0] << endl;
}