二進制中1的個數

題目描述:

輸入一個整數,輸出該數二進制表示中1的個數。其中負數用補碼表示。

分析:

先復習幾個知識點:

  • 補碼: 在計算機中所有的整數都是以二進制形式存儲的,其中正整數用原碼表示,負數用其正值的補碼表示。
    補碼的計算機方式:對該負數的正值的原碼,進行取反后加一,得到補碼。
    例如-6在計算機中的補碼表示如下:(以32位計算機為例)

-6 的正值 6 的二進制:
00000000 00000000 00000000 00000110
取反,得反碼:
11111111 11111111 11111111 11111001
+1 ,得補碼,即 -6 在計算機中的二進制表示:
11111111 11111111 11111111 11111010

  • 補碼的性質:在計算機中,加法器實現最為簡單,很多運算最終都要轉化為加法運算,而,減去一個數相當于加上這個數的補碼。(非常巧妙的設計,讓人佩服馮諾依曼的天才創意!關于補碼的詳細介紹
  • 負數的右移:因為負數在內存中是以補碼形式存在的,所有首先根據負數的原碼求出負數的補碼(符號位不變,其余位按照原碼取反加1),然后保證符號位不變,其余位向右移動到X位,在移動的過程中,高位補1.等移位完成以后,然后保持符號位不變,其余按位取反加1,得到移位后所對應數的原碼。即為所求。詳見

  • 正數的左移與右移、負數的無符號右移,就是相應的補碼移位所得,在高位補0即可。
    負數的右移,就是補碼高位補1,然后按位取反加1。

  • 左移、右移:
    在不溢出的情況下
    左移n位后的值 等于原值乘以2的n次方
    例如 4 <<2 就是16,二進制就是 00000100 <<00010000
    -4<<2 就是-16 二進制就是 11111100 <<11110000
    右移n位后的值 等于原值除以2的n次方的商
    例如 4 >>2 就是1,二進制就是 00000100 >>00000001
    -4>>2 就是-1 二進制就是 11111100 <<11111111

代碼:

  1. 將二進制轉化為數組,然后一位一位進行判斷(重點:熟悉java中相應的API)
public class Solution {
    public int NumberOf1(int n) {
        int result =0;
        char[] array = Integer.toBinaryString(n).toCharArray();
        for(char c:array){
            if(c=='1'){
                result ++;
            }
        }
        return result;
    }
}
  1. 改進,既然熟悉java的API,就會發現JAVA 的 JDK 庫里 Integer 有個 bitCount 方法,直接調用即可:
public class Solution {
    public int NumberOf1(int n) {
       return Integer.bitCount(n);
    }
}

查看Integer.bitCount()的源碼如下:

/** 
     * Returns the number of one-bits in the two's complement binary 
     * representation of the specified {@code int} value.  This function is 
     * sometimes referred to as the <i>population count</i>. 
     * 
     * @return the number of one-bits in the two's complement binary 
     *     representation of the specified {@code int} value. 
     * @since 1.5 
     */  
    public static int bitCount(int i) {  
        // HD, Figure 5-2  
        i = i - ((i >>> 1) & 0x55555555);  
        i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);  
        i = (i + (i >>> 4)) & 0x0f0f0f0f;  
        i = i + (i >>> 8);  
        i = i + (i >>> 16);  
        return i & 0x3f;  
    }  

二分法,兩兩一組相加,之后四個四個一組相加,接著八個八個,最后就得到各位之和了。
第一行是計算每兩位中的 1 的個數 , 并且用該對應的兩位來存儲這個個數 , 如 : 01101100 -> 01011000 , 即先把前者每兩位分段 01 10 11 00 , 分別有 1 1 2 0 個 1, 用兩位二進制數表示為 01 01 10 00, 合起來為 01011000.
第二行是計算每四位中的 1 的個數 , 并且用該對應的四位來存儲這個個數 . 如 : 01101100 經過第一行計算后得 01011000 , 然后把 01011000 每四位分段成 0101 1000 , 段內移位相加 : 前段 01+01 =10 , 后段 10+00=10, 分別用四位二進制數表示為 0010 0010, 合起來為 00100010 .
下面的各行以此類推 , 分別計算每 8 位 ,16 位 ,32 位中的 1 的個數 .
將 0x55555555, 0x33333333, 0x0f0f0f0f 寫成二進制數的形式就容易明白了 .

3.根據上面的源碼,可以類似的使用二分法寫出 HAKMEM 算法的解法

public class Solution {
    public int NumberOf1(int n) {
       int num;     
       num = (n >> 1) & 033333333333;     
       n = n - num;    
       num = (num >> 1) & 033333333333;    
       n = n - num;     
       n = (n + (n >> 3)) & 030707070707;    
       n = n%63;   
       return n ;   
    }
}

這個通過率只有40%,應該是哪里有問題。

  1. 通過位移判斷奇偶數并計數,標志位初始為1,將其和輸入值相與,n & 1 的結果為 1 或 0 ,為 1 的時候 count+=1 ,為 0 的時候 count+=0
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;
    }
}
  1. 正整數的二進制數最高位為 0 ,負整數和二進制數最高們為 1 ,則可利用左移、判斷正負來實現 1 的個數的計算。
public class Solution {
    public int NumberOf1(int n) {
      int count = 0;
       while(n!=0){
           if(n<0){
               count++;}
           n=n<<1;//左移一位,左邊的最高位為符號位,根據正負數來判斷符號位的0,1,從而得到1的個數
       }
        return count;
    }
}

這種方法最容易理解.

  1. 思想: x & (x-1) 可以消去 x 二進制數的最后一位 1

n : 10110100
n-1 : 10110011
n&(n-1) : 10110000

public class Solution {
    public int NumberOf1(int n) {
      int cnt = 0;
        while (n != 0) {
            cnt++;
            n &= (n - 1);
        }
    return cnt;
    }
}

把一個整數減去1,再和原整數做運算,會把該整數最右邊一個1變成0,那么一個整數的二進制表示有多少個1,就可以進行多少次這樣的操作.
O(logM) 時間復雜度解法,其中 M 表示 1 的個數。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容