1. 程式人生 > 實用技巧 >【劍指offer】 15. 二進位制中1的個數-逐位判斷 / n&(n-1)

【劍指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的個數。

  舉個例子更容易理解一些:假設數字 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