面試題10:請實現一個函數,輸入一個整數,輸出該數二進制表示中1的個數。例如把9表示成二進制是1001,有2位是1。所以若是輸入9,該函數輸出2。面試
牛客網OJ:二進制中1的個數bash
位運算無外乎 與、或、異或、左移和右移 5 種類型的運算。函數
使用位運算符進行運算時,整數會自動轉爲二進制形式,再進行位運算。全部位運算的題型基本都是各類類型位運算的組合。ui
注意點:整數包含正、負數。spa
負數右移須要在真空位補上1,如-1,二進制原碼爲 1000 0001,第一個位爲符號位,負數爲1,非負數爲0開頭。在計算機中,負數採用補碼來表示,-1的最終二進制表現形式爲: 絕對值取反加1,附帶上符號位。即 111 1111 加上符號位爲 1111 1111。3d
-1 >> 1 的結果是 1111 1111。code
判斷二進制數中1的位數,能夠經過和整數 1 進行 與 運算,1&1 = 1,1&0 = 0。
1 的二進制形式爲:0000 0001
9 的二進制形式爲:0000 1001
9&1 = 0000 0001 != 0,能夠肯定最後一位是 1。
那麼此時要如何繼續往前判斷另外一個1呢?
解決方案:
將 1 左移一位,即 0000 0001 變爲 0000 0010,再判斷 9(0000 1001)的第 2 位
0000 0010 & 0000 1001 = 0000 0000 == 0,能夠肯定第 2 位爲 0。
這個過程當中,使用一箇中間計數器變量 count 每次肯定與運算結果爲非 0, 則加 1 便可統計 1 的個數。
再將 1 左移一位,即 0000 0010 變爲 0000 0100,再判斷 9(0000 1001)的第 3 位
0000 0100 & 0000 1001 = 0000 0100 != 0,能夠肯定第 3 位爲 1。
後面同上,1 挨個左移,直到移動到最左邊,變成 0000 0000,宣告位遍歷結束:
0000 0001
0000 0010
0000 0100
0000 1000
0001 0000
0010 0000
0100 0000
1000 0000
0000 0000 結束
複製代碼
public class Solution {
public int NumberOf1(int n) {
int count = 0;
int flag = 1;
while(flag != 0){
if((n&flag) != 0){
count++;
}
flag = flag << 1;
}
return count;
}
}
複製代碼
位運算的題首先要想到位移運算和 與或 運算的結合。cdn
一個整數若是是2的整數次方,那麼它的二進制表示中有且只有一位是1,而其餘全部位都是0。blog
方案:把這個整數減去1以後再和它本身作與運算,這個整數中惟一的1就會變成0。
好比 8 的二進制位 0000 1000,0000 1000 - 1 = 0000 1000 - 0000 0001 = 0000 0111
而後再將計算結果和 8 自身進行與運算:0000 1000 & 0000 0111 = 0000 0000 結果恰好是 0 。
複製代碼
if((n - 1) & n == 0){
//是2的整數次方
}
複製代碼
好比 10 的二進制表示爲 1010,13 的二進制表示爲 1101,須要改變 1010 中的 3 位才能獲得 1101。get
咱們能夠分爲兩步解決這個問題:第一步求這兩個數的異或,第二步統計異或結果中1的位數。
1010 ^ 1101 = 0111
再運用移位運算 + 與運算判斷 1 的個數。
複製代碼
public int numOfBitToChange(int m, int n) {
int result = m ^ n;
int count = 0;
int flag = 1;
while(flag != 0) {
if((result & flag) != 0) {
count ++;
}
flag << 1;
}
return count;
}
複製代碼
把一個整數減去1以後再和原來的整數作位與運算,獲得的結果至關因而把整數的二進制表示中的最右邊一個1變成0。
5 - 1 = 0101 - 0001 = 0100
0101 & 0100 = 0100 (最右邊的 1 變成0:0101 --> 0100)
4 - 1 = 0100 - 0001 = 0011
0100 & 0011 = 0000 (最右邊的 1 變成0:0100 --> 0000)
10 - 1 = 1010 - 0001 = 1001
1010 & 1001 = 1000 (最右邊的 1 變成0:1010 --> 1000)
複製代碼
不少二進制的問題均可以用這個思路解決。