对于一个数组其中每个数出现了两次,只有一个数出现了一次,找出这个只出现了一次的一个数,这是一个经典的面试题,把数组中的数进行异或运算,出现两次的数异或的结果为0,最终异或的结果就为只出现了一次的一个数。
例如:数组元素为3,4,4,3(011)异或4(100)结果为111,111再接着异或4(100),结果为011,即为3。
void FindOnlyoneInArray(int a[],int length){ if (a==NULL||length<2) { return; } int result = 0; for (int i = 0; i < length;++i) { result ^= a[i]; } cout << result << endl; }
对于查找数组中只出现一次的一个数我们已经知道怎么来做了,那么对于查找数组中两个只出现一次的数,也是同样的道理,只需要把数组分成两个子数组,每个里面包含一个只出现一次的数,而其他数字都成对出现两次,但是怎么来拆分原数组呢?
还是异或数组中的每一个数字,最终得到的结果就是两个只出现一次的数字的异或结果,以为这两个数不一样,最终异或的结果也不为0,那么这个结果的二进制表示中至少有一位为1,我们找到这个1的位置,记为一个标记,当做分割的标准,第一个子数组中每个数字的标记为都为1,第二个的都为0,因为相同的数字,标记位一定相同,不同的数字,标记位不同,这样就达到了我们的要求。
例如:数组元素为2,4,3,6,3,2,5,5,异或的结果为0010,也就是倒数第二位为1,也就是倒数第二位为标记位来进行拆分,那么第一个子数组就为{2,3,6,3,2},他们的倒数第二位为1,第二个子数组为{4,5,5}倒数第二位为0,对这两个子数组分别再异或,最终找到4和6。
void FindAppearOnce(int data[], int length, int *num1, int *num2){ if (data == NULL || length < 2){ return; } int result = 0; for (int i = 0; i < length;++i) { result ^= data[i]; } unsigned int indexOf1 = FindFirstBitIs1(result); *num1 = *num2 = 0; for (int j = 0; j < length;++j) { if (IsBit1(data[j],indexOf1)) { *num1 ^= data[j]; } else{ *num2 ^= data[j]; } } } unsigned int FindFirstBitIs1(int result){ int indexBit1 = 0; while (((result&1)==0)&&(indexBit1<8*sizeof(int))) { result = result >> 1; ++indexBit1; } return indexBit1; } bool IsBit1(int num,unsigned int indexBit1){ num = num >> indexBit1; return (num&1); }