位运算的巧用

最近逛技术论坛时看到一个网友出了一道题,大致是这样:有一个集合,里面包含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),  空间额外开销就是几个成员变量。

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

时间: 2024-10-06 10:31:40

位运算的巧用的相关文章

巧用位运算

1.用一个表达式,判断一个数X是否是2的N次方(2,4,8,16.....),不可用循环语句. 解析:X:2,4,8,16转化成二进制是10,100,1000,10000.如果减1则变成01,011,0111,01111.两者做按位与运算,结果如果为0,则X是2的N次方. 答案:!(X&(X-1)) 2.统计一个整数的二进制中1的个数 int CountNumberOfOne(int number) { int counter = 0; while (number) { number &=

编程思想:巧用位运算重构代码

开篇 在一门编程语言中,往往会提供大量的运算符.按功能来分的话,有算术运算符.赋值运算符.关系运算符.逻辑运算符.位运算符等.这些对于大家来说都不陌生.但是,本期的主角『位运算』符相对而言是比较少去使用的.因为位运算符主要针对两个二进制数进行位运算. 巧用位运算能极大的精简代码和提高程序效率.所以,在一些优秀的开源代码中,经常能出现位运算.所以,把位运算这种思想迁移到业务代码里,有时候往往能起到柳暗花明般的重构. 位运算 在 JAVA 语言中,定义了诸多的位运算符,如下所示: 运算符 描述 &

位运算巧用

位运算是指按二进制位进行运算,运算符有一下几种 “&”按位与,“|”按位或,“-”取反,“^”按位异或 其真值表为: a1 a2 & | ^ 1 1 1 1 0 1 0 0 1 1 0 1 0 1 1 0 0 0 0 1 a1 ~ 1 0 0 1 巧用异或 两个数异或的结果再与其中一个数异或,会得到另外一个数. 巧用按位与 按位与只有同时为1的时候才为1,所以可以用0000 1111来清零高字节,保留低字节.用1111 0000来清零低字节,保留高字节. <<按位左移 >

巧用c语言的位运算代替部分求余%计算

网上看到一个文章,里面讲述了四种高效c语言执行的方法, 1.使用空间代替时间 2.使用数学优化,而不是死计算,笨计算 3.使用位运算 4.使用嵌入汇编 前三点比较容易掌握,第四点基本上需要有比较深厚的汇编基础. 这里讲讲位运算, 1.无符号数据中, 左移1位,等价于除以2,右移1位,等价于乘以2,,切记不可溢出. 2.求余数, J = 456 % 32; ——456 - (456 >> 5 << 5);k = 456 % 64; ——456 - (456 >> 6 &l

【位运算生成枚举序列】

如果遇到这样的枚举情况,一个数组a[6],  每个值只能取0或者1,共有64种情况,我们需要一次枚举出来. 方法1:从0到63一次转换成二进制填充进去(麻烦). 方法2:位运算实现 代码: int main(void) { int cnt=0; for(int k=0; k<64; k++){ //枚举0---63 for(int j=0; j<6; j++) //6个数组位 if(k&(1<<j)) printf("1 "); //可以在此处将1填充到

Single Number II位运算解析

本题最机巧的O(n)解法最早由1337c0d3r于2013.11.26发布在leetcode.之后看到类似的,都系转载.引用或抄袭.(原文:https://oj.leetcode.com/discuss/857/constant-space-solution) 大多数转载都写得语焉不详,有的甚至据为己有.本帖旨在全面解析该算法. 如下: int singleNumber(int A[], int n) { int ones = 0, twos = 0, threes = 0; for (int

位运算

位运算的实际应用场景 http://blog.csdn.net/zmazon/article/details/8262185

POJ 1781 In Danger Joseph环 位运算解法

Joseph环,这次模固定是2.假设不是固定模2,那么一般时间效率是O(n).可是这次由于固定模2,那么能够利用2的特殊性,把时间效率提高到O(1). 规律能够看下图: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQva2VuZGVuMjM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" > 具体具体解析请看大师Knuth的Concrete m

位运算总结&amp;拾遗

JavaScript 位运算总结&拾遗 最近补充了一些位运算的知识,深感位运算的博大精深,此文作为这个系列的总结篇,在此回顾下所学的位运算知识和应用,同时也补充下前文中没有提到的一些位运算知识. 把一个数变为大于等于该数的最小的2的幂 一个数为2的幂,那么该数的二进制码只有最高位是1. 根据这个性质,我们来举个栗子,比如有数字10,转为二进制码后为: 1 0 1 0 我们只需把 0 bit的位置全部用1填充,然后再把该二进制码加1就ok了.而x | (x + 1)正好可以把最右边的0置为1,可是