查找一组数据中单独出现的三个数字
一组数据中只有三个数字单独出现,其余数字都是成对出现的,请找出这三个数字。要解决这个问题,我们先来看一下下面这两个问题:
- 一组数据中只有一个数字单独出现,其余数字都是成对出现,请找出这个数字。
根据异或的性质:相同为0,相异为1,可得自己异或自己结果为0。一组数据中只有一个数字单独出现,其余数字都成对出现,将所有的数据进行异或,成对出现的数字异或结果都为0,故这组数据异或的结果就是单独出现的数字,代码如下:
void find_one_data(int arr[],int count)
{
int key = arr[0]; //所有数字的异或值,自己与自己异或为0
int i = 1;
assert(arr);
for(;i<count;i++)
{
key ^= arr[i];
}
printf("单独出现的数字为:%d\n",key);
}
- 一组数据中只有两个数字单独出现,其余数字都是成对出现,请找出这两个数字。
假设单独出现的数字为a和b,将所有数字进行异或得到的结果为x,成对出现的数字异或结果为0,那么x=a^b。由于a和b是两个不同的数字,所以x不为0。x不为0,那么x中至少有一个比特位为1,根据x中为1的最低比特位(设为pos)可将这组数据分为两组,一组数据的pos位为1,一组为0,再将其进行异或就可得到单独出现的其中一个数字,代码如下:
int find_last_bit1(int x)
{
//返回为1的最低bit位
return x & ~(x-1);
}
void find_two_data(int arr[],int count)
{
int a = 0; //单独出现的数字
int b = 0; //单独出现的数字
int x = arr[0]; //存放所有数据的异或值
int i = 1;
int ret = 0;
assert(arr);
for(;i<count;i++)
{
x ^= arr[i];
}
ret = find_last_bit1(x); //找到x中为1的最低bit位
for(i=0;i<count;i++)
{
if(ret & arr[i]) //根据x中为1的最低bit位将数据分为两组,为1的进行异或得到单独出现的一个值
{
a ^= arr[i];
}
}
b = a^x; //x为a^b的值
printf("单独出现的数字为:%d %d\n",a,b);
}
-
一组数据中只有三个数字单独出现,其余数字都是成对出现,请找出这三个数字。
根据前两个问题的求解,会想到同样用异或的方式求解该问题,三个数字单独出现的情况比较复杂。
- 假设单独出现的三个数字为a、b、c,对所有数据进行异或得到的结果为x,即x = a ^ b ^ c。三个数字各不相同,不能保证x不为0,但是x肯定不是a、b、c中的任一个数。反证法:假设x=a,那么x = a ^ b ^ c就可表示为a = a ^ b ^ c,那么b^c的结果就为0,即b=c,这与三个单独出现的数字相矛盾,故x≠a。同理可得,x≠b、x≠c。
- x与a、b、c都不相等,那么x ^ a 、x ^ b、x ^ c的值也不相等,且都不为0,其结果中至少有一个比特位为1。
- 假设函数f(x)返回x中为1的最低比特位,令y=f (x ^ a)^ f (x ^ b)^ f (x ^ c),由于x ^ a 、x ^ b、x ^ c的值都不为0,且f (x ^ a)、 f (x ^ b)、 f (x ^ c)都有一个比特位为1,所以y≠0。
f(x^a) | f(x^b) | f(x^c) | y |
---|---|---|---|
0 | 0 | 0 | 0 |
1 | 0 | 0 | 1 |
1 | 1 | 0 | 0 |
1 | 1 | 1 | 1 |
- 如表所示,当y≠0时,f(x ^ a) 、f(x ^ b)、f(x ^ c)至少有一位为1。假设y为1的最低比特位为pos,那么x ^ a 、x ^ b、x ^ c的pos位有一个为1或者三个为1。
如果x ^ a 、x ^ b、x ^ c的pos位都为1,那么a、b、c的第pos位都与x不同,且a、b、c的第pos位都相同。假设a、b、c的第pos位都为0,那么x= a ^ b ^ c在pos位的值为0,与a、b、c的第pos位都与x不同相矛盾;假设a、b、c的第pos位都为1,那么x= a ^ b ^ c在pos位的值为1,与a、b、c的第pos位都与x不同相矛盾。故即x ^ a 、x ^ b、x ^ c的pos位只有一个为1,这样我们就可以找到其中的一个数了,根据问题1、2就可以找到剩余的两个数了。代码如下所示:
void find_three_data(int arr[],int count)
{
int i = 0;
int x = arr[0];
int y = 0;
int ret = 0;
int a = 0;
int b = 0;
int c = 0;
for(i=1;i<count;i++)
{
x ^= arr[i]; //x为所有数据的异或值
}
for(i=0;i<count;i++)
{
y ^= find_last_bit1(x^arr[i]); //y为f(x^a)^f(x^b)^f(x^c)的值,由于a,b,c不同,x与这三个数也不相同,可得y中至少有一个比特位为1
}
ret = find_last_bit1(y);
for(i=0;i<count;i++)
{
if(ret & arr[i])
{
a ^= arr[i]; //先找到单独出现的其中一个
}
}
x ^= a; //x为剩余单独出现的两个数的异或值
ret = find_last_bit1(x);
for(i=0;i<count;i++)
{
if(ret & arr[i])
{
b ^= arr[i];
}
}
b ^= a; //由于数组中a出现了一次,与a异或后即可得剩于两者中的一个
c = x^b;
printf("单独出现的数字为:%d %d %d\n",a,b,c);
}