题目描述:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
异或的方法。
先考虑一个简单一点的问题,数组里除了一个数字以外,其它都出现两次,找到这个数字。
这个题之前遇到过,做法是把数组里所有数字异或,最后得到的数字就是只出现一次的那一个数字。为什么是这样呢?因为异或运算有一个性质:任何一个数字异或它自己都等于0.所以将数组里的数字从头到尾异或,最后得到的一定是落单的那一个数字。
那么两个出现一次的问题怎么解决呢?如果可以把一个数组分成两半,每一半里都只有一个只出现一次的数,那就又变成了上面的问题。所以问题变成了我们怎么样可以把数组分成符合条件的两半?
由于相同的数异或得零,如果把数组里所有数异或,得到的将是这两个落单的数的异或值。由于这两个数肯定不相等,所以这两个数的异或值也不会为0。从右向左找到这个异或值不为零的一位,比如说是第N位,那么根据第N位是否为0把原数组分为两个部分。(异或的概念:两个数对应为相同为0,不同为1),也就是说,如果第N位异或结果为1,这两个数字的第N位一定不同。所以按照这个标准将数组分成两部分,将实现每一部分里只有一个落单的数。
代码:
class Solution {
public:
void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
if(data.size() < 2)
return;
int resOR = 0;
for(int i = 0;i < data.size();i++){
resOR ^= data[i];
}
unsigned int indexof1 = FindFirstBitIs1(resOR);
*num1 = *num2 = 0;
for(int i = 0;i < data.size();i++){
if(IsBit1(data[i],indexof1))
*num1 ^= data[i];
else
*num2 ^= data[i];
}
}
//找到num的二进制表示中找到最右边是1的位
unsigned int FindFirstBitIs1(int num){
int indexBit = 0;
while(((num & 1)==0) && indexBit < 8 * sizeof(int)){
num = num >> 1;
indexBit++;
}
return indexBit;
}
//判断在num的二进制表示中从右边数起的第indexBit位是不是1
bool IsBit1(int num,unsigned int indexBit){
num = num >> indexBit;
return (num & 1);
}
};
tips:取某数字的二进制数字的某一位的方法
int num;
num >> n;
num & 1;//得到的就是num从右边数第n位的值。