首页 > 代码库 > 位运算总结

位运算总结

情形一:数组中所有数都出现两次,只有一个数出现一次


情形二:数组中所有数都出现两次,只有两个数出现一次

情形一二很多地方都有说明,这里就不啰嗦了,有一点需要注意:当知道原始数据时,可以使用解方程的方法,这样可以把上面的情形无线推广,具体见编程之美


情形三:数组中所有数都出现三次,只有一个数出现一次

方法一:如果数组中的元素都是三个三个出现的,那么从二进制表示的角度,每个位上的1加起来,应该可以整除3。如果有一个数x只出现一次,会是什么情况呢?

如果某个特定位上的1加起来,可以被3整除,说明对应x的那位是0,因为如果是1,不可能被3整除

如果某个特定位上的1加起来,不可以被3整除,说明对应x的那位是1

根据上面的描述,我们可以开辟一个大小为32的数组,第0个元素表示,A中所有元素的二进制表示的最低位的和,依次类推。最后,再转换为十进制数即可。这里要说明的是,用一个大小为32的整数数组表示,同样空间是O(1)的。

int FindNumber(int a[], int n)  
{  
	int bits[32];  
	int i, j;  
	// 累加数组中所有数字的二进制位  
	memset(bits, 0, 32 * sizeof(int));  
	for (i = 0; i < n; i++) 
	{
		for (j = 0; j < 32; j++)  
		{
			bits[j] += ((a[i] >> j) & 1);   
		}
	}
	int result = 0;  
	for (j = 0; j < 32; j++) 
	{
		if (bits[j] % 3 != 0) result += (1 << j);  
	}
	return result;  
} 
方法二:参考这里

Single Number的本质,就是用一个数记录每个bit出现的次数,如果一个bit出现两次就归0,这种运算采用二进制底下的位操作^是很自然的。Single Number II中,如果能定义三进制底下的某种位操作,也可以达到相同的效果,但是这个东西没有现成的可用。
我们换个思路,Single Number II中想要记录每个bit出现的次数,一个数搞不定就加两个数,用ones来记录只出现过一次的bits,用twos来记录只出现过两次的bits,ones&twos实际上就记录了出现过三次的bits,这时候我们来模拟进行出现3次就抵消为0的操作,抹去ones和twos中都为1的bits。

int singleNumber(vector<int>& data)
{
	int ones = 0,twos = 0,threes = 0,length = data.size(),i;
	for (i = 0;i < length;i++)
	{
		twos |= ones & data[i];			//记录只出现过2次的bits,要在更新ones前面更新twos
		ones ^= data[i];				//记录只出现过1次的bits
		threes = ones & twos;			//ones和twos中都为1即出现了3次
		ones &= ~threes;				//抹去出现了3次的bits,相当于模3运算
		twos &= ~threes;
	}
	return ones;//返回只出现一次的
}

情形四:数组中所有数都出现三次,只有一个数出现两次

按照情形三的方法二,只要返回twos即可,原理是一样的

位运算总结