剑指Offer系列-面试题40:数组中只出现一次的数字

来源:互联网 发布:自然语言处理算法面试 编辑:程序博客网 时间:2024/05/16 17:41

题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度为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;}


0 0