1、数组A[n],除数字X之外,其他的数字都出现了三次。X只出现了一次。什么方法可以很快的求出X。
思路:假设数组为A[6] = {1,5,5,1,1,5}; 此时数组中所有的数字都出现了三次。我们考察一下这些数字的二进制存储形式。
A[0] = 1 : 0 0 1
A[1] = 5 : 1 0 1
A[2] = 5 : 1 0 1
A[3] = 1 : 0 0 1
A[4] = 1 : 0 0 1
A[5] = 5 : 1 0 1
可以发现将所有数字的某一个bit位对应的 1 的个数正好可以整除3.最低位1的个数为6,最高位1的个数为3,中间1的个数为0. 6、3、0除以3都可以整除。
此时,若数组中添加任意另外一个不同的数Y,Y的二进制形式必然与当前数字的二进制形式不同,会导致某些位置的 1 的个数不能再整除 3,因此我们就可以
知道Y在哪些位置出现了1.由此可以反推Y的值。
解法分为三个部分:(1) 计算当前所有数组元素二进制位置 i[1,32]上1的个数。(2)若i位置1的个数不能整除3,则X的二进制对应的此位置应当为1.
(3)计算X。
代码:
#include<stdio.h> #include<stdlib.h> int main(char *argv[], int argc) { int i = 0; int j = 0; int k = 0; int A[7] = {12,12,12,13,13,13,17}; int Ones[32] = {0}; int ResultX = 0; for(i=0; i<7; i++) for(j=0; j<32; j++) { k = (A[i]>>j) & 1; Ones[j] += k; } for (i=0; i<32 ;i++ ) { printf("%3d",Ones[i]%3); if(Ones[i]%3 != 0) ResultX += (1<<i); } printf("X Is :%d ",ResultX); system("PAUSE"); return 0; }
2、 倘若此时把题意换一下:数组A[n],除X外其他的数都出现两次,X出现一次。求X。怎么做呢?
其实此时的题目变得更加简单了。看一个操作:异或。
7 : 1 1 1
异或
7 : 1 1 1
异或
5: 1 0 1
最终的结果是5: 101。所以出现两次的数都被自身的异或抵消没了,剩下的都是指出现一次或者奇数次的数。受此启发,我们不难写出这样的代码来计算
一个数组中只出现了一次的X:
int X = 0; for (int i=0; i<n ; i++ ){ X = X ^ A[i]; } printf("Unique Number X is : %d", X);
3、再考虑一个问题:若果数组A[n]中有两个不同的数X,Y。除X,Y之外,其他的数字都出现了两次,求X和Y。
参考(2)中的讨论,倘若我们可以把X和Y分到两个子数组中,而且两个子数组都满足:除X外其他的数都出现两次,X出现一次。那么我们使用(2)中的方法
就可以把X和Y求出来的。
代码如下:
int firstOnePos(int data) { int k = 0; int i = 0; for (i=0; i<32; i++) { k = (data >> i) & 1; if (k == 1) return i; } return -1; } int testBit(int data,int pos) { return (data>>pos) & 1; } int main(char *argv[], int argc) { int i = 0; int j = 0; int pos = 0; int A[6] = {12,12,15,13,13,17}; int Ones[32] = {0}; int Z = 0; int X = 0; int Y = 0; for (i=0; i<6 ;i++ ) { Z = Z ^ A[i]; } pos = firstOnePos(Z); if(pos == -1) return 0; for(i=0; i<6; i++) { if(testBit(A[i],pos)) X = X ^ A[i]; else Y = Y ^ A[i]; } printf("X Is :%d ",X); printf("Y Is :%d ",Y); system("PAUSE"); return 0; }