题目描述
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
方法一:哈希法
很显然的方法,遍历一遍数组,用map记录出现的次数,然后再遍历一遍数组,找出出现1次的数字。
class Solution {
public:
void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
unordered_map<int, int> mp;
for (const int k : data) ++mp[k];
vector<int> ret;
for (const int k : data) {
if (mp[k] == 1) {
ret.push_back(k);
}
}
*num1 = ret[0];
*num2 = ret[1];
}
};
时间复杂度:O(n)
空间复杂度:O(n)
方法二:位运算
前提知识:
异或运算:如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。
- n^0 = n;
- n^n = 0;
- n^n^m = n^(n^m) 满***换律
所以,我们可以让数组中的每一个数异或一下,最后会得到一个结果ret,就是两个出现一次的数字的异或结果这个结果肯定是由两个不同数字异或而来,因此我们找ret二进制中为1的位置i,因为1一定是由0,1异或而来,因此要求得两个数中,一定有一个数的二进制中的第i个位置为1, 一个为0.
那么可以通过第i个位置为1或0将数组分为两个子数组,而两个只出现一次的数字就会分别落到两个子数组中,在每个子数组中再次进行异或,得到的结果即为只出现一次的数字
class Solution {
public:
int FindFirstOne(int ret){
int res = 0;
if ((ret & 1) == 0){
ret = ret >> 1;
res += 1;
}
return res;
}
void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
int ret = 0;
for (int i=0; i<data.size(); i++)
ret ^= k;
int first_one_pos = FindFirstOne(int ret);
*num1 = 0, *num2 = 0;
for (int j=0; j<data.size(); j++) {
if((data[j] >> first_one_pos) & 1)
*num1 ^= k;
else
*num2 ^= k;
}
}
};
时间复杂度:O(n)
空间复杂度:O(1)