题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度为O(n),空间复杂度为O(1)。
思路:利用异或的特点,一个数的二进制异或自己,结果为0,既然数组中只有两个数字出现一次,其他出现两次,那么首先考虑这样一个问题:一个数组中,只有一个数字出现一次,其他数字出现两次,那么只需要全部异或,既然一个数异或自己为0,而0异或任何数都为这个数本身,那么全部异或后,最终的结果就是要找的数。
现在考虑本题的情况,既然有两个数只出现一次,那么如果我们把数组分为两部分,一部分中只包含一个只出现一次的数字,那么问题就迎刃而解了。这种情况下,我们把整个数组异或一遍,会得到一个数字,由于只出现一次的数字有两个,那么异或一遍等于是将这两个数字异或,如6和4,异或是0110^0100=0010,即至少有一位为1,不可能全为0,那么我们就找到了他们的差异,从右往左第一个为1的位,可以当做他们的特征,即6的二进制倒数第二位是1,这样就可以将整个数组分为两部分了,只要二进制倒数第二位是1的数字,我们把他分到6所在的那一个数组,不是的话分到4所在的那个数组,这样就找到了了两个数。
见代码,有注释。
代码:
public void FindNumsAppearOnce(int [] array, int num1[] , int num2[]) {
if(array == null || array.length < 2) {
return;
}
int result = 0;
for (int i : array) {
result ^= i;
}
int indexOf1 = FindFirstBitIs1(result);
num1[0] = 0;
num2[0] = 0;
for (int i : array) {
if (isBit1(i, indexOf1)) { // 如果属于第一个数组
num1[0] ^= i;
} else {
num2[0] ^= i;
}
}
}
// 寻找从右往左的第一个为1的位,即特征位
private int FindFirstBitIs1(int result) {
int index = 0;
while ((result & 1) == 0 && index < 8 * 4) {
result = result >>> 1;
index++;
}
return index;
}
// 判断一个数字的二进制从右到左数第n位是否为1,用来分两个数组
private boolean isBit1(int num, int indexBit) {
num = num >>> indexBit;
return ((num & 1) == 0) ? false : true;
}