劍指Offer面試題10(Java版):二進位制中的1的個數
題目:請實現一個函式,輸入一個整數,輸出該數二進位制表示中1的個數。例如把9表示成二進位制是1001,有2位是1,因此如果輸入9,該函式輸出2.
1、可能引起死迴圈的解法
這是一道很基本的考察二進位制和位運算的面試題。題目不是很難,面試官提出問題之後,我們很快形成一個基本的思路:先判斷證書二進位制表示中最右邊一位是不是1.接著把輸入的證書右移一位,此時原來處於從右邊樹起的第二位被移到最後一位,再判斷是不是1.這樣沒移動一位,知道整個整數變成0為止。現在的問題變成怎麼判斷一個整數的最右邊是不是1了。這很簡單,只要把整數和1做位與運算看結果是不是0就知道了。1除了最右邊的一位之外所有的位都是0.基於這個思路,我們很快寫出這樣的程式碼:
int numberOf1(int n)
{
int count = 0;
while(n!=0){
if(n & 1)
count++;
n = n>>1;
}
return count;
}
面試官看了 程式碼後可能會問:把證書右移一位和把整數除以2在數學上是等價的,那上面的程式碼中可以把右移換成除以2嗎?答案是否定的。因為除法的效率比移位運算要低很多,在實際程式設計中應儘可能地用移位運算代替乘除法。
面試官會問第二個問題就是:上面的函式如果輸入一個負數,比如0x80000000,執行的時候會發生什麼情況呢?把負數0x80000000右移一位的時候,並不是簡單地把最高位的1移到第二位變成0x40000000,而是0xC0000000.這是因為移位前是個負數,仍然保證移位是個負數,因此移位後的最高位會設為1.如果一直做右移位運算,最終這個數字會程式設計0xFFFFFFFF而陷入死迴圈。
2、常規解法:
為了避免死迴圈,我們可以不右移輸入的數字n.首先把n和1做與運算,判斷n的最低位是不是1.接著把1左移一位得到2,再和n做與運算,就能判斷n的次低位是不是1。。。。這樣反覆左移,每次都能判斷n的其中一位是不是1.基於這種思路,我們可以寫出這樣的程式碼:
int number1(int n){
int count = 0;
int flag= 1;
while(flag ! =0){
if(n& flag)
count++;
flag =flag <<1;
}
return count;
}
這個解法中迴圈的次數等於二進位制中的位數,32位的整數需要迴圈32次,下面我們再介紹一個演算法,整數中有幾個1就只迴圈幾次。
3、能給面試官帶來驚喜的演算法。
我們的分析就是:把一個整數減去1,再和原整數做與運算,會把該整數最右邊的一個1變成0.那麼一個整數的二進位制表示中有多少個1,就可以進行多少次運算。基於這種思路,我們可以寫出這樣的程式碼:
/**
*題目:實現一個函式,輸入一個整數,輸出該數二進位制表示中的1的個數。
*例如把9改成二進位制是1001,有2位是1.因此如果輸入9,該函式輸出2.
*/
package swordForOffer;
/**
* @author JInShuangQi
*
* 2015年7月30日
*/
public class E10NumberOf1InBinary {
public int numberOf1(int num) {
int count = 0;
while (num != 0) {
count++;
num = num & (num - 1);
}
return count;
}
public static void main(String[] args) {
E10NumberOf1InBinary test = new E10NumberOf1InBinary();
System.out.println(test.numberOf1(9));
}
}