这是一个很有意思的问题,是在面试中特别容易被问到的问题之一,这个问题有一个正式的名字叫 Hamming weight。
解决这个问题第一想法肯定是一位一位的去判断,是1计数器+1,否则不操作,跳到下一位,十分容易,编程初学者就可以做得到!
于是很容易得到这样的程序:
int Sum1ByBin(int num) { int sum = 0; while (num) { if(sum %2 ==1) { sum ++; } sum/=2; } return sum; }
或者用牛逼的位运算:
int Sum1ByBin(int num) { int sum = 0; while (num) { sum += num&1; num>>1; } return sum; }
上面这两篇代码是用的同样的算法, 时间复杂度是二进制的位数,于是可以想一下,有没有只有与二进制中1的位数相关的算法呢?
可以考虑每次找到从最低位开始遇到的第一个1,计数器加1然后把它清零,然后继续找下一个1。方法就是n&(n-1),这个操作对比当前位高的位没有任何影响,
对低位完全清零。举个栗子吧!5。 5是101,第一次运算101&100 == 100,并且计数器加一,第二次运算100&011 == 0,计数器加一。循环结束啦!
所以5有2个1。牛逼吧! 看看代码是怎么实现的:
int Sum1byBin(int num) { int sum = 0; while(num) { num &= num-1; sum++; } return 0; }很是简单,这就是位运算的好处,据说这个算法没把位运算发挥到极致,也没有得到这个算法最优解。
请读者先look一段代码,看看能不能看懂是什么功能:
int Sum1byBin(int num) { num = (num&0x55555555) + ((num>>1)&0x55555555); num = (num&0x33333333) + ((num>>2)&0x33333333); num = (num&0x0f0f0f0f) + ((num>>4)&&0x0f0f0f0f); num = (num&0x00ff00ff) + ((num>>4)&&0x00ff00ff); num = (num&0x0000ffff) + ((num>>4)&&0x0000ffff); return num; }卧槽,这个是在玩什么中的制表符导致空格这么大的不怪我!
回想第一次遇到这个代码的时候我第一印象,这是什么**玩意,后来经过分析并且有高手帮助理解,这个功能正是利用了位运算,将一个数中二进制表中1的个数算了出来。利用的是分治思想,先计算每对相邻的2位中有几个1,再计算相邻的4位有一个1 ,再计算相邻的8位、16位、32位有几个1,到此结束,32位的机器int就是32位,所以算
到32位就够啦!!!
大家好好理解吧,这里的代码片是一键一键敲出并且经过验证的!祝回家的童鞋们一路顺风,祝像我一样单身汉早日找到女神!!!