解决方案1:
先排序,后比较,时间复杂度为O(nlogn+n)
解决方案2:
采用位运算。
因为 0^ a=a, a^a=0;
所以只有1个不同时,循环异或一遍,结果便是。
只有两个不同时,循环异或一遍,结果便是x=a^b,因为a!=b,所以x中必存在某一位为1,假设为第k位。那么a,b的第k位一个比为0,另一个必为1。
这样我们就可以根据第k位为0,还是1,将数组中所以元素划分为两个阵营,一个是第k为0,为a,c,c,d,d,.另一个是第k位为1,为b,e,e,f,f,..。这样我们就转化为第一个的问题了。
在每个阵营中循环异或一遍便得到相应的结果。
只有三个不同时,循环异或一遍,结果便是x=a^b^c,我们想可不可以转化为前面的问题。但是我们发现不能直接采用第二个问题的思路。
因为:1。如果x中存在某一位为1,假设为第k位。那么a,b,c的第k位为1,00或者111这两种形式。对于第一种可以采用划分的思路,将三个阵营缩小为两个阵营,即转化为第二个问题和第一个问题。对于第二种形式无法划分成两个阵营。
2。x中必存在某位为1吗?答案为可能x的结果就是0
所以该思路不行。
我们可以这样,设数组为a,b,c,d,d,e,e,f,f,..先循环异或一遍,结果便是x=a^b^c,然后用x去异或数组中每个元素。即x^a,x^b,x^c,x^d,x^d...
即把原先数组为现在的数组A,B,C,D,D,E,E,F,F其中A=b^c, B=a^c, C=a^b,且A!=0,B!=0,C!=0(因为a,b,c 不相同)
然后对现在数组再异或一遍,结果为y=A^B^C=0
这时候我们可以把A^B看做一个整体,因为异或结果为0,则数必相同,即A^B=C。
由于C!=0,则C必存在某一位为1,假设为第k位。即A^B的第k位为1,则A,B的第k为要么为0要么为1,这样我们就可以把A,B,C,D,D,E,E,F,F数组中元素根据第k位为0还是为1划分为两个阵营,一个阵营中第k为1,有两个不同,一个阵营第k为0,有一个不同,直接分别使用我们前面的一个不同,两个不同的方法,我们就可以获得A,B,C,
由A,B,C获得a,b,c 这个就比较简单了。a=A^x,b=B^x,c=C^x
思路没问题了,但是在编程中,如果找到C,这是个问题。下面是小小的技巧使用。
我们定义一个函数f(n),它的结果是保留数字n的二进制表示中的最后一位1,而把其他所有位都变成0。比如十进制6表示成二进制是0110,因此f(6)的结果为2(二进制为0010)。f(x^a)、f(x^b)、f(x^c)的结果均不等于0。
但是我们知道A,B,C中的f(A), f(B), f(C)的值必有两个相同,z=f(A)^f(B)^f(C) 我们就可以把数组A,B,C,D,D,E,E的函数值即f(A), f(B), f(C),f(D), f(D), f(E), f(E),根据他们的值和z相不相同进行划分。
#include<stdio.h>
int findOneNumber(int arr[],int n, int *num)
{
int i;
*num=0;
for( i=0; i<n; i++)
{
*num=arr[i] ^ *num;
}
return 0;
}
int findFirstBitIs1(int num)
{
int count=0;
while( (num & 1)==0 && count <32)
{
num=num >> 1;
count++;
}
return count;
}
int bitkIs1(int num, int k)
{
while(k)
{
num=num >> 1;
k--;
}
if( (num & 1)==1)
return 1;
else
return 0;
}
int findTwoNumber(int arr[], int n, int *num1, int *num2)
{
int i;
int temp=0;
for(i=0; i<n ;i++)
temp=arr[i] ^ temp;
int bitk=findFirstBitIs1(temp);
int array1[n];
int array2[n];
int k1=0;
int k2=0;
for(i=0; i< n; i++)
{
if(bitkIs1(arr[i], bitk))
{
array1[k1++]=arr[i];
}
else
{
array2[k2++]=arr[i];
}
}
findOneNumber(array1, k1, num1);
findOneNumber(array2, k2, num2);
return 0;
}
int findThreeNumber(int arr[], int n, int *num1, int *num2, int *num3)
{
int temp=0;
int i;
for(i=0; i<n; i++)
temp=temp ^ arr[i];
//相当于构造了一个新的数组.原数组相当于a,b,c,d,d,e,e,现在为A,B,C,D,D,E,E
for(i=0; i<n ;i++)
arr[i]=arr[i] ^ temp;
int flip=0;
for(i=0; i<n; i++)
flip=flip ^ findFirstBitIs1(arr[i]);
int array1[n];
int array2[n];
int k1=0;
int k2=0;
//划分为两个部分,其实也可以优化空间使用情况,但是代码可读性差。
for(i=0; i< n; i++)
{
if( findFirstBitIs1(arr[i])==flip )
{
array1[k1++]=arr[i];
}
else
{
array2[k2++]=arr[i];
}
}
if(k1 % 2==0)
{
findTwoNumber(array1, k1, num1, num2);
findOneNumber(array2, k2, num3);
}
else
{
findOneNumber(array1, k1, num1);
findTwoNumber(array2, k2, num2, num3);
}
*num1 ^=temp;
*num2 ^=temp;
*num3 ^=temp;
return 0;
}
int main()
{
int a[]={2, 1, 2, 1, 5, 1, 5, 1, 4};
int len=sizeof(a)/sizeof(int);
int num1,num2,num3;
findOneNumber(a, len, &num1);
printf("number is %d \n",num1);
int b[]={2, 4, 2, 2, 2, 2, 4, 4};
len=sizeof(b)/sizeof(int);
findTwoNumber(b, len, &num1, &num2);
printf("num1: %d num2: %d\n",num1, num2);
int c[]={1, 4, 2, 4, 4, 5, 5};
len=sizeof(c)/sizeof(int);
findThreeNumber(c, len, &num1, &num2, &num3);
printf("num1: %d num2: %d num3: %d\n", num1, num2, num3);
//printf("%dth bit is 1 \n" ,findFirstBitIs1(4));
return 0;
}