首页 > 代码库 > 位运算的巧用

位运算的巧用

最近逛技术论坛时看到一个网友出了一道题,大致是这样:有一个集合,里面包含N个整数,除了一个整数只出现一次之外,其他都出现3次,怎么样最快找出只出现一次的那个数?

作者的解法有点忘记了,但是这个题突然让我想起之前《编程之美》里的一道题, 和这个题的区别是其他都出现2次,只有一个是出现一次。 它的解法非常巧妙,就是把所有整数进行异或运算,最后的结果就是只出现一次的那个整数。因为异或会把相同的消除掉。但是这个题是出现3次,异或已经解决不了,第一想法是空间换时间,所有数中取最大值Max,然后定义一个数组int hashArr[Max],运用hash的思想,遍历所有整数进行hashArr[i]++运算,最后再找出hashArr[i]为1的,下标值就是所求的数。这个解法同样适用于其他出现N次的情况!解法时间复杂度为O(N),空间额外开销为S(MAX)

但是我们可以继续深入分析,以32bit整数为例,如果把所有数的第i个比特位相加,假如和为sum。则会有如下等式:3(x1 + x2 + ... + xn) + x0 = sum (x1代表第一个数的第i个比特位的值 X0代表只出现一次整数的第i个比特位的值) 从这个等式中可以看出x0的值为sum/3的余数,因为x0不是1就是0,肯定小于3。所以我们可以有如下的代码:

int arr[N] = {};
int _tmain(int argc, _TCHAR* argv[])
{
	int nMask = 1;
	int nBitSum = 0;
	int nDst = 0;
	for (int i = 0; i < 32; ++i)	//依次判断32位
	{
		for (int j = 0; j < N; ++j)	 //对N个数的第i位求和
		{
			if (arr[j] & nMask)
			{
				nBitSum++;
			}
		}

		if (nBitSum%3 != 0)		//余数不是1就是0
		{
			nDst |= nMask;
		}

		nMask <<= 1;
		nBitSum = 0;
	}

	return nDst;
}


这样就把只出现一次的数求出来了,而且如果其他数出现的次数是M次的话,mod M就可以了,时间复杂度是O(32*N),  空间额外开销就是几个成员变量。


相比较而言还是下面的算法更好一些,当然也是具体情况而定。

位运算的巧用