劍指Offer(Java版):二進制中的1的個數

題目:請實現一個函數,輸入一個整數,輸出該數二進制表示中1的個數。例如把9表示成二進制是1001,有2位是1,所以若是輸入9,該函數輸出2.面試

一、可能引發死循環的解法算法

這是一道很基本的考察二進制和位運算的面試題。題目不是很難,面試官 提出問題以後,咱們很快造成一個基本的思路:先判斷證書二進制表示中最右邊一位是否是1.接着把輸入的證書右移一位,此時原來處於從右邊樹起的第二位被移 到最後一位,再判斷是否是1.這樣沒移動一位,知道整個整數變成0爲止。如今的問題變成怎麼判斷一個整數的最右邊是否是1了。這很簡單,只要把整數和1作 位與運算看結果是否是0就知道了。1除了最右邊的一位以外全部的位都是0.基於這個思路,咱們很快寫出這樣的代碼:編程

package cglib;函數

public class List1
{  
    public static int numberOf1(int num) {  
        int count = 0;spa

        while(num!=0){code

        if((num&1)!=0)數學

        count++;class

        num = num>>1;效率

        }擴展

        return count;
    }  
 
    public static void main(String[] args) {  
        
        System.out.println(numberOf1(9));  
        System.out.println(numberOf1(-9));
}
}

只輸出了2

負數死循環

面試官看了 代碼後可能會問:把證書右移一位和把整數除以2在數學上是等價的,那上面的代碼中能夠把右移換成除以2嗎?答案是否認的。由於除法的效率比移位運算要低不少,在實際編程中應儘量地用移位運算代替乘除法。

面試官會問第二個問題就是:上面的函數若是輸入一個負數,好比 0x80000000,運行的時候會發生什麼狀況呢?把負數0x80000000右移一位的時候,並非簡單地把最高位的1移到第二位變成 0x40000000,而是0xC0000000.這是由於移位前是個負數,仍然保證移位是個負數,所以移位後的最高位會設爲1.若是一直作右移位運算, 最終這個數字會編程0xFFFFFFFF而陷入死循環。

二、常規解法:

爲了不死循環,咱們能夠不右移輸入的數字n.首先把n和1作與運算,判斷n的最低位是否是1.接着把1左移一位獲得2,再和n作與運算,就能判斷n的次低位是否是1。。。。這樣反覆左移,每次都能判斷n的其中一位是否是1.基於這種思路,咱們能夠寫出這樣的代碼:

package cglib;

public class List1
{  
    public static int numberOf1(int num) {  
        int count = 0;

        int flag= 1;

        while(flag!=0){

        if((num&flag)!=0)

        count++;

        flag =flag <<1;

        }

        return count;
    }  
 
    public static void main(String[] args) {  
        
        System.out.println(numberOf1(9));  
        System.out.println(numberOf1(-9));
}
}

輸出:

2
31

 

這個解法中循環的次數等於二進制中的位數,32位的整數須要循環32次,下面咱們再介紹一個算法,整數中有幾個1就只循環幾回。

 

三、能給面試官帶來驚喜的算法。

咱們的分析就是:把一個整數減去1,再和原整數作與運算,會把該整數最右邊的一個1變成0.那麼一個整數的二進制表示中有多少個1,就能夠進行多少次運算。基於這種思路,咱們能夠寫出這樣的代碼:

package cglib;

public class List1
{  
    public static int numberOf1(int num) {  
        int count = 0;  
        while (num != 0) {  
            count++;
            System.out.println("與前:num="+num);//1001
            num = num & (num - 1);//00001001&00001000=00001000,00001000&0000111=00000000
            System.out.println("與後num="+num);
            System.out.println("count="+count);
 
        }  
        return count;  
    }  
 
    public static void main(String[] args) {  
        
        System.out.println(numberOf1(9));  
        System.out.println(numberOf1(-9));
}
}

輸出:

與前:num=9
與後num=8
count=1
與前:num=8
與後num=0
count=2
2
與前:num=-9
與後num=-10
count=1
與前:num=-10
與後num=-12
count=2
與前:num=-12
與後num=-16
count=3
與前:num=-16
與後num=-32
count=4
與前:num=-32
與後num=-64
count=5
與前:num=-64
與後num=-128
count=6
與前:num=-128
與後num=-256
count=7
與前:num=-256
與後num=-512
count=8
與前:num=-512
與後num=-1024
count=9
與前:num=-1024
與後num=-2048
count=10
與前:num=-2048
與後num=-4096
count=11
與前:num=-4096
與後num=-8192
count=12
與前:num=-8192
與後num=-16384
count=13
與前:num=-16384
與後num=-32768
count=14
與前:num=-32768
與後num=-65536
count=15
與前:num=-65536
與後num=-131072
count=16
與前:num=-131072
與後num=-262144
count=17
與前:num=-262144
與後num=-524288
count=18
與前:num=-524288
與後num=-1048576
count=19
與前:num=-1048576
與後num=-2097152
count=20
與前:num=-2097152
與後num=-4194304
count=21
與前:num=-4194304
與後num=-8388608
count=22
與前:num=-8388608
與後num=-16777216
count=23
與前:num=-16777216
與後num=-33554432
count=24
與前:num=-33554432
與後num=-67108864
count=25
與前:num=-67108864
與後num=-134217728
count=26
與前:num=-134217728
與後num=-268435456
count=27
與前:num=-268435456
與後num=-536870912
count=28
與前:num=-536870912
與後num=-1073741824
count=29
與前:num=-1073741824
與後num=-2147483648
count=30
與前:num=-2147483648
與後num=0
count=31
31

擴展1:用一條語句判斷一個整數是否是2的整數次方

將2的冪次方寫成二進制形式後,很容易就會發現有一個特色:二進制中只有一個1,而且1後面跟了n個0; 所以問題能夠轉化爲判斷1後面是否跟了n個0就能夠了。

若是將這個數減去1後會發現,僅有的那個1會變爲0,而原來的那n個0會變爲1;所以將原來的數與去減去1後的數字進行與運算後會發現爲零。

最快速的方法:

 

(number & number - 1) == 0

擴展2:

輸 入兩個整數m和n,計算須要改變m的二進制表示中的多少位才能獲得n。好比10的二進制表示爲1010,13的二進制表示爲1101,須要改變1010中 的3位才能獲得1101 。 咱們能夠分爲兩步解決這個問題:第一步求這兩個數的異或,第二步統計異或結果中1的位數。

  1. int GetCount(int N,int M)  
  2. {  
  3.     int value=N^M;//先將兩個數異或  //0111=7,0110=6
  4.     int count=0;  
  5.     while(value)  
  6.     {  
  7.         count++;  
  8.         value=(value&(value-1));//求異或後1的個數  ,0111& 0110 =0110=6;//0101=5,0100=4,
  9.     }  
  10.     return count;  
相關文章
相關標籤/搜索