题目:一个数组中有三个数字a、b、c只出现一次,其他数字都出现了两次。请找出三个只出现一次的数字。
分析:如果我们把数组中所有数字都异或起来,那最终的结果(记为x)就是a、b、c三个数字的异或结果(x=a^b^c)。其他出现了两次的数字在异或运算中相互抵消了。
我们可以证明异或的结果x不可能是a、b、c三个互不相同的数字中的任何一个。
由于x与a、b、c都各不相同,因此x^a、x^b、x^c都不等于0。
我们定义一个函数f(n),它的结果是保留数字n的二进制表示中的最后一位1,而把其他所有位都变成0。f(x^a)、f(x^b)、f(x^c)的结果均不等于0。
f(x^a)^f(x^b)^f(x^c)的结果的二进制中至少有一位是1。假设最后一位是1的位是第m位。那么x^a、x^b、x^c的结果中,有一个或者三个数字的第m位是1。
可以证明x^a、x^b、x^c的三个结果第m位不可能都是1。
因此x^a、x^b、x^c三个数字中,只有一个数字的第m位是1。于是我们找到了能够区分a、b、c三个数字的标准。这三个数字中,只有一个数字满足这个标准,而另外两个数字不满足。一旦这个满足标准数字找出来之后,另外两个数字也就可以找出来了。
#include<stdio.h>
static int lastBitOf1(int x)
{
return x&-x;
}
static void getTwoUnique(int a[],int n,int b[])
{
int xorResult=0;
for(int i=0;i<n;++i)
xorResult^=a[i];
int diff=lastBitOf1(xorResult);
int first=0;
int second=0;
for(int i=0;i<n;++i)
{
if(diff&a[i])
first^=a[i];
else
second^=a[i];
}
b[1]=first;
b[2]=second;
}
void getThreeUnique(int a[],int n,int b[])
{
if(n<3)
return ;
int xorResult=0;
for(int i=0;i<n;++i)
{
xorResult^=a[i];
}
int flags=0;
for(int i=0;i<n;++i)
{
flags^=lastBitOf1(xorResult^a[i]);
}
flags=lastBitOf1(flags);
int first=0;
for(int i=0;i<n;++i)
{
if(flags==lastBitOf1(xorResult^a[i]))
{
first^=a[i];
}
}
b[0]=first;
for(int i=0;i<n;++i)
{
if(a[i]==first)
{
int t=a[i];
a[i]=a[n-1];//数组的最后一个元素
a[n-1]=t;
break;
}
}
getTwoUnique(a,n-1,b);
}
int main(int argc, char *argv[])
{
int a[]={2,2,3,3,4,4, 5,6,7, 8,8};
int b[3];
getThreeUnique(a,11,b);
for(int i=0;i<3;++i){
printf("%d",b[i]);
if(i+1!=3)
printf(" ");
}
printf("\n");
return 0;
}
结果是正确的