全部博文(185)
分类:
2010-07-20 09:25:08
《编程之美》中也存在这道题的描述,不过input类型变为BYTE即unsigned char型(扩展中为DWORD)。需要注意的是,input的数据类型对此题影响很大。这里,我们以32位无符号整数即DWORD(unsigned int)为主,同时会简单介绍一下BYTE的O(1)算法。
算法一:
判断input的最低位是否为1(与1做’&’运算),若是则计数器++;否则,input<<1即右移一位,循环继续。程序写起来也比较简单:
算法的时间复杂度为O(n),n为二进制的位数。
算法二:
循环时只与位为1的进行判断,利用判断一个数是否为2的n次幂的算法来做。
为了简化描述,我们以8位2进制数为例来解析,32位类似。如:01000000,如何去判断这个二进制数里面有且仅有一个1呢?很自然的想到可以通过判断这个数是否为2的n次幂来实现。假设我们希望这个数和某个数的运算结果为0,则代表该数是2的n次幂,那么00111111便符合要求。即01000000 & 00111111 = 0,而00111111 = 01000000 – 1;连起来即01000000 & 00111111 = 01000000 & (01000000 – 1) = 0;
那么我们就有了数学上的描述,设数为x,那么其是否为2的n次幂就可以通过一个x & ( x – 1 ) == 0来判断(是则返回TRUE,否则FALSE)。
很快写出程序:
注意,while循环内第一句为’&=’运算。
算法的时间复杂度为O(M),M为二进制数中为1的位数
算法三:
对于input为BYTE类型的数,可以采用查表法,空间换时间,达到O(1)的时间复杂度,对于input为DWORD来说,这种方法没有意义。
算法四:
通过位运算技巧可得到一个效率极高的算法,同时可通过简单修改适用于各种数位,先给出完整代码:
下面对代码做下解释,解释大部分引用自http://blog.csdn.net/jiyucn/archive/2006/06/19/812845.aspx 感谢原作者:)
对于二进制数的理解,二进制数每一位只能为0和1,因此在这个地方,我们可以理解为每一位都可以表示该位中包含1的数目。
看bitcount第一行代码:x = (x & 0x55555555UL) + ((x >> 1) & 0x55555555UL);
5的二进制是 0101,当x & 0x55555555UL得出的结果是保留了0, 2, 4, 6 ... , 30 位的值,即 0, 2, 4, 6 ,... , 30 位中包含1的数目;那么自然可以理解(x >> 1) & 0x55555555UL得出的结果是保留了1, 3, 5, 7, ... , 31 位的值,即 1, 3, 5, 7, ..., 31 位中包含1的数目。把两者相加起来:
xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
+0101 0101 0101 0101 0101 0101 0101 0101
------------------------------------------------------------------------
0x0x 0x0x 0x0x 0x0x 0x0x 0x0x 0x0x 0x0x (这里的x即为x中 0, 2, 4, ... , 30 位的值)
0xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
+0101 0101 0101 0101 0101 0101 0101 0101
------------------------------------------------------------------------
0y0y 0y0y 0y0y 0y0y 0y0y 0y0y 0y0y 0y0y (这里的y即为x中 1, 3, 5 , ... , 31 位的值)
0x0x 0x0x 0x0x 0x0x 0x0x 0x0x 0x0x 0x0x
+0y0y 0y0y 0y0y 0y0y 0y0y 0y0y 0y0y 0y0y
------------------------------------------------------------------------
zz zz zz zz zz zz zz zz
可以把32位的X数每两位看成一个最小单元,x+y的值z即为这个两位最小单元中,1的个数。可以看出,巧妙地把两个加数种0的空间用来保存进位。那么到现在为止,我们就得到了数X中,每两位为最小单位,每两位中的数值表示着这两位中1的个数。
依此类推,接下来我们用同样的方法计算以4位为最小单位,4位中的数值表示该4位中1的个数,即:x = (x & 0x33333333UL) + ((x >> 2) & 0x33333333UL);
然后是 8位, 16位,当计算到以32位为最小单位,32位中的数值表示该32位中1的个数的时候,答案就揭晓了。
算法五:
HAKMEM算法,来自http://www.blogjava.net/zellux/archive/2008/04/15/192955.html,这里就不贴代码了,zellux解释的很清楚:)