查找一组数据中单独出现的三个数字

通过异或操作解决查找一组数据中单独出现的三个数字的问题。首先,利用异或性质找到单独出现的数字,然后分析异或结果确定三个数字的特征,并通过比特位判断找到这些数字。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

查找一组数据中单独出现的三个数字

一组数据中只有三个数字单独出现,其余数字都是成对出现的,请找出这三个数字。要解决这个问题,我们先来看一下下面这两个问题:

  1. 一组数据中只有一个数字单独出现,其余数字都是成对出现,请找出这个数字。
    根据异或的性质:相同为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);
}
  1. 一组数据中只有两个数字单独出现,其余数字都是成对出现,请找出这两个数字。
    假设单独出现的数字为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);
}
  1. 一组数据中只有三个数字单独出现,其余数字都是成对出现,请找出这三个数字。

    根据前两个问题的求解,会想到同样用异或的方式求解该问题,三个数字单独出现的情况比较复杂。

  • 假设单独出现的三个数字为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
0000
1001
1100
1111
  • 如表所示,当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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值