【剑指offer】 15. 二进制中1的个数-逐位判断 / n&(n-1)

  今日份刷题遇到了一道题目,本来做题的感悟都是自己写备注放在脑图上的,但是感觉这道题挺有意义的,加上之前同学面试字节跳动的时候有被问到,所以就专门写一下博客。这道题是剑指offer第15题:二进制中1的个数

解法1 :从右往左一个一个和 1 相 & 

   很多人看到这题的第一反应是逐位判断,具体的思路是将 n 和数字 1 相 &,这么做了之后就能判断当前 n 的最后一位是否为1,举个例子,就比如数字:1010001,把它和数字 1 相与,就是 1010001 & 0000001 = 1 ,从而能得知这个数字的最后一位是1,那要怎么知道整个数字的 1 的个数呢?只需要套上一个 while 循环,循环里面要做的事情有两件:

1. res += n & 1; //res就是要返回的结果值, n & 1 的结果只有 0 或者 1,所以直接把这个结果加到res上面就可以

2.n >>>= 1; // n无符号右移,注意哦,这里必须必须是 3 个 > 符号,不能是两个,因为如果是数字:
11111111111111111111111111111101,右移之后就会在最左边补 1 ,整个数字会被看成负数,这会导致我们的 while 循环陷入死循环

  所以我最后提交的代码是:

var hammingWeight = function(n) {
    let res = 0;
    while(n > 0) {
        res += n & 1;
        n >>>= 1;
    }
    return res;
};

 

解法2:n & (n - 1)

  这个解法比较巧妙,但也是面试官希望你能答出来的答案。为什么它比较好呢,因为它需要判断循环的次数更少,这个数字有几个 1 ,就循环几次。

  这种写法能获得从右往左第一位为 1 (该位记为index)的数字,(n-1) 之后,index 后的数都为 1,index 前的数都不变(还跟 n 的 index 位前一致),index 这个位置的数字由 1 变为 0 。然后,n & (n-1) 就能得到一个数,这个数字把index后的数都置为 0,所以这道题我们就可以一直 while(n),循环里面 n &= n-1,这样子的话,经过了k次循环直到n变为0,那么这个k次,就是原数的1的个数。

  因为二进制减法借位后变成0,0与1相与后变成0,这样就可以把1消去并计数。

 

 

  举个例子更容易理解一些:假设数字 n 为:1100100

  那么根据思路第一个操作就是将 n & (n - 1),

    n: 1 1 0 0 1 0 0

    n - 1: 1 1 0 0 0 1 1

n&(n-1):1 1 0 0 0 0 0

次数:第 1 次进入while

  求出 n & (n-1)后,把这个值赋给 n ,进入下一轮的 while 循环:

   n: 1 1 0 0 0 0 0

  n-1: 1 0 1 1 1 1 1

n&(n-1): 1 0 0 0 0 0 0

次数:第 2 次进入while

 

  求出 n & (n-1)后,把这个值赋给 n ,进入下一轮的 while 循环:

   n: 1 0 0 0 0 0 0

  n-1: 0 1 1 1 1 1 1

n&(n-1): 0 0 0 0 0 0 0

次数:第 3 次进入while

  最后把 n & (n-1) = 0的结果赋给 n ,n = 0,触碰到了 while 循环的终止条件,就结束了,最后进入 while 循环的总次数是 3 ,所以 1 的个数就是 3。按照这种思路我提交的代码是:

var hammingWeight = function(n) {
    let res = 0;
    while(n != 0) {
        ++res;
        n &= n - 1;
    }
    return res;
}

 

  注意两种写法,while的判断条件略微不同,第一种写法,因为是无符号右移 <<<,所以while的判断条件是 n>0;第二种写法,由于没作无符号的处理,所以如果从左开始看起的第一位数字是1,是会被解释成一个负数的,这时候的while判断条件不能单纯地写成n > 0,而应该是n != 0,因为这种写法的停止条件就是n的每位都变成0

posted on 2020-08-08 14:32  heySarah  阅读(286)  评论(0编辑  收藏  举报

导航