本文示例代码均使用JavaScript,阅读本文需要位运算的基本知识。
一、题干
请实现一个函数,输入一个整数,输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。
二、解法
最容易理解的方法(可能引起死循环):
思路:先判断整数的二进制表示中最右边一位是不是1。然后把整数右移一位,这时原来处于最右边第二位的被移到最右边第一位了,再判断是不是1。这样每次移动一位,直到这个数变为0为止。比如9,即1001,设计数变量x=0,1001最后一位为1,x=x+1,所以此时x=1,右移之后变成0100,最后一位不为1,x不变,继续右移变成0010,x不变,再右移变成0001,x=x+1,此时x=2,继续右移0000,此时整数为0,所以x最终的值为2。
那么如何判断一个整数最右边是不是1呢?很简单,只需要把这个整数和1按位与就可以了。因为整数1的二进制表示除了最后一位是1其余位都是0,所以如果整数最右边一位是1,则按位与之后值为1,否则为0。
于是我们可以写出如下代码:
function hanmingWeight(n) {
let x = 0
while(n) {
if(1 & n) {
x++
}
n = n >> 1
}
return x
}
这个解法对于负数来说会陷入死循环,原因是当输入是一个负数的时候,右移之后最高位补位的是1,所以n就永远都不会变成0了。
常规解法
为了避免陷入死循环,我们可以换个思路,既然负数右移会有问题,那我们可以把1左移,这样也可以把n的每一位比对到。
function hanmingWeight(n) {
let x = 0
let flag = 1
while(flag) {
if(flag & n) {
x++
}
flag = flag << 1
}
return x
}
这个代码终止条件是flag的溢出,也就是flag左移之后,溢出了无符号整型的范围,被截断之后就变成了0,while循环就会终止。
这个可以在控制台自己试一下:
由于第32位是符号位,所以当第32位为1的时候,即a << 2之后产生的值就会变成一个很大的负数,a << 3就溢出了,输出0。
位运算符要求它的操作数是整数,这些整数表示为32位整型而不是64位浮点型。 《JavaScript权威指南》(原书第六版)4.8.3
对于有符合的整数,32位中的前31位用于表示整数的值。第32位用于表示数值的符号:0表示正数,1表示负数。这个表示符合的位叫做符号位。《JavaScript高级程序设计》(第三版) 3.5.2
奇思妙想
第三种方法需要知道一个类似定理的东西,那就是:把一个整数减去1再和这个整数做按位与运算,就会让这个整数的二进制表示的最右边的1变成0。比如12,即1100,1100减1,就变成了1011,再和1100按位与,就变成了1000,1000再减1,得到0111,和1000按位与,得到0000。这个数有多少1就可以这样操作几次。
按照这个思路我们可以写出如下代码:
function hanmingWeight(n) {
let x = 0
while(n) {
n = n & (n -1)
x++
}
return x
}
三、参考资料
1.题目来源:剑指offer面试题10
2.《JavaScript权威指南》(原书第六版)4.8.3
3.《JavaScript高级程序设计》(第三版) 3.5.2
力扣(LeetCode)链接:leetcode-cn.com/problems/er…
如有疑问欢迎留言 ღ( ´・ᴗ・` )比心