有一个数组,各种数组出现次数的神组合……

本文介绍了三种场景下找出数组中单次出现的数字的方法:基本款为一次出现的数,使用异或;进阶款一为一次出现的数,其余出现三次,采用位操作;进阶款二为两个数各出现一次,其余出现两次,通过异或和位筛选实现。

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

1. 基本款:有一个数出现了一次。其余的数都出现了两次

异或,结果就是只出现一次的数。


2.  进阶款1  有一个数出现了一次。其余的数都出现了三次
基本思想: 假设是32位的整数,定义一种新的bit operation 。 每位操作如下: 1^0=1, 0^1=1, 0^0=0, 1^1=2, 2^1=0...每次按位操作,一直到最后。 
每次操作32次。 复杂度32n, 还是O(n)的  (感慨以下,李博好聪明,果然是MS的人)
(可以用一个长度为32的数组,每一个value存储对应bit的运算结果)

careercup上的解法,太牛了 

for( i=0; i< 10; i++ )
{
x = B[i];
twos |= ones & x ;
ones ^= x ;
not_threes = ~(ones & twos) ;
ones &= not_threes ;
twos &= not_threes ;
}

printf("\n unique element = %d \n", ones );
return 0;

思想:one用来存储只出现一次bit,two用来存储出现两次的bit。
twos |= ones & x 
由于one只存储了出现一次的bit,因此此时two保留了出现两次的bit,并且可能含有出现三次的bit

ones ^= x ;
包含了出现一次和三次的bit信息,

not_threes = ~(ones & twos) ;
ones &= not_threes ;
twos &= not_threes ;
删除one two中出现三次的bit信息。


3. 进阶款2    有两个数出现了一次。其余的数都出现了两次

参考基本款,如果能够把原数组分为两个子数组。在每个子数组中,包含一个只出现一次的数字,而其他数字都出现两次。如果能够这样拆分原数组,按照前面的办法就是分别求出这两个只出现一次的数字了。

我们还是从头到尾依次异或数组中的每一个数字,那么最终得到的结果就是两个只出现一次的数字的异或结果。因为其他数字都出现了两次,在异或中全部抵消掉了。由于这两个数字肯定不一样,那么这个异或结果肯定不为0,也就是说在这个结果数字的二进制表示中至少就有一位为1。我们在结果数字中找到任一个为1的位的位置,记为第N位。现在我们以第N位是不是1为标准把原数组中的数字分成两个子数组,第一个子数组中每个数字的第N位都为1,而第二个子数组的每个数字的第N位都为0

现在我们已经把原数组分成了两个子数组,每个子数组都包含一个只出现一次的数字,而其他数字都出现了两次。因此到此为止,所有的问题我们都已经解决。

int*  twoTwice(int * A, int size){
	int *  result = new int[2];
	result[0] = 0;
	result[1] = 0;

	for(int index=0; index<n; index++)
		result[0] ^= A[index];

	int mask = 1;
	while (result[0] & mask)	
		mask = mask<<1;
	result[0] = 0;
	for(int index=0; index<n; index++) {
		if (A[index] & mask == 0)
			result[0] ^= A[index];
		else
			result[1] ^= A[index];
	}

	return result;
}

程序牵扯几个小问题:
a.  返回数组时,返回指针。另外,数组内别忘了new memory
b.  如何寻找某一位为1,并产生mask(一步操作即可,简单)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值