题目描述:
一个整型数组里除了两个数字之外,其他的数字都出现了偶数次。请写程序找出这两个只出现一次的数字。
思路:
1. 如果是数组中只有一个数字出现了奇数次,其他数字都出现偶数次,可以采用异或的方法,因为相同的数字异或结果是0,而0异或任何数字等于该数字本身。因此可以将数组中所有数字进行异或,最后剩下的就是只出现奇数次的数字。
2. 如果是找数组中只出现一次的数字,也可以用哈希表记录所有数字,参考第一个只出现一次的字符,区别是字符是有限个的256,所以空间效率是O(1)。而对于数字,空间效率是O(n),所以对于数字可以用异或更高效。
现在有这样的两个数字,参考上述思想,应该要将数组分为两个子数组,保证这两个数字分别在两个数组中,而其他数字都成对出现。这样就把问题分解为两个子问题,只要对两个子数组分别进行一次上述异或操作就可以了。
现在的关键是如何划分子数组。我们还是先将数组中所有元素异或,得到的结果就是要找的两个数字异或的结果,因为这两个数字不一样,因此结果肯定不为0,也即结果的二进制表示中至少有一个为1,从低往高找到第一个为1的位置n,我们就按照这一位是1还是0来对数组划分。第一个子数组中的数字第n位都为1,第二个子数组中的数字第n位都为0。因为相同数字的第n位也相同,所以相同的元素肯定会被划分到同一个子数组,而要找的两个数字的第n位肯定不相同(因为这一位的异或结果为1),所以两个数字会被分别划分到两个数组中,这样就达到了目的。
参考代码:
在线测试
AC代码
class Solution {
public:
void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
int result=0;//初始化为0
for(int i=0;i<data.size();i++)
{
result=result^data[i];//计算所有数的异或
}
int n=0;//找到第一个为1的数位
while((result&1)==0)
{
n++;
result=result>>1;
}
*num1=0;
*num2=0;
for(int i=0;i<data.size();i++)
{
if(nIs1(data[i],n))
*num1=(*num1)^data[i];
else
*num2=(*num2)^data[i];
}
}
//判断第n位是不是1
bool nIs1(int num, int n)
{
num=num>>n;//直接右移n位
return (num&1);
}
};