1. 程式人生 > >劍指offer刷題記錄11——二進位制中1的個數

劍指offer刷題記錄11——二進位制中1的個數

題目描述

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

將輸入的整數分成0,正,負三種情況分別討論。正數和0不必多說,當負數時,要將反碼的32位用0補全,補碼是取反加一,再求1的個數,所以可以不必求補碼,直接轉化成反碼加0,1和0的代數關係進行調換,即1 + 0 = 0, 0 + 0進一位0原位寫1,最終求0的個數,程式碼如下:

解法一:

public class Solution {
    public int NumberOf1(int n) {
        if(n == 0) {
            return 0;
        }
        if(n > 0) {
            return NumberOf1(n / 2) + n % 2;
        }
        int num = 0;
        if(n < 0) {
            if(n == Integer.MIN_VALUE) {
                 return 1;
            }
            n = -n;
            StringBuffer sb = new StringBuffer();
            int flag = 1;
            while(n > 0) {
                sb.append(n % 2);
                n /= 2;
            }
            while(sb.length() < 32) {
                sb.append("0");
            }
            for(char c : sb.toString().toCharArray()) {
                if(c == '0') {
                    if(flag == 0) {
                        num++;
                    }
                    else {
                        flag = 1;
                    }
                }
                else {
                    if(flag == 1) {
                        num++;
                        flag = 0;
                    }
                }
            }
        }
        return num;
    }
}

執行時間:16ms

佔用記憶體:9404k

要記得考慮到當整數位Integer.MIN_VALUE時,其補碼為1加上31個0,直接返回1即可。

不過想來這種解法實在繁瑣,進行了整數轉化為二進位制,再進行二進位制的計算兩步,但是否可以直接用整數的二進位制進行位運算呢,於是有了如下的解法。

解法二:遞迴

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

在二進位制的位面看待這些數,n - 1即是找到最後面的一個1,並將其置0,這個1後面的0全部置1,結果與n相與後剛剛置為1的那些位又重新變成0,即n & (n - 1)是將n的最後一個1找到並置0,其他位置不變,那麼這個操作可以遞迴呼叫多少次,n的二進位制表示就有多少個1。

執行時間:14ms

佔用記憶體:9288k

然而遞迴的實現是通過呼叫函式本身,函式呼叫的時候,每次呼叫時要做地址儲存,引數傳遞等,這是通過一個遞迴工作棧實現的。具體是每次呼叫函式本身要儲存的內容包括:區域性變數、形參、呼叫函式地址、返回值。那麼,如果遞迴呼叫N次,就要分配N*區域性變數、N*形參、N*呼叫函式地址、N*返回值。這勢必是影響效率的。

以上文字引用自其他人的部落格 。

因為遞迴使得效率低於普通的迴圈呼叫,簡單問題能不用遞迴就不用遞迴,將解法二進行改進↓

解法二改進:迴圈

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

執行時間:12ms

佔用記憶體:9272k

繼續利用位運算,題目要求找到有多少個1,那麼想到設定一個mask,這個mask的二進位制表示只有一個1,且這個1從最低位一直移動到最高位,每次移動之後與n進行與運算,結果不為0的次數就是所求1的個數。

解法三:

public class Solution {
    public int NumberOf1(int n) {
        int res = 0;
        int mask = 1;
        for(int i = 0; i < 32; i++) {
            if((mask & n) != 0) {
                res++;
            }
            mask <<= 1;
        }
        return res;
    }
}

執行時間:12ms

佔用記憶體:9280k

方法總結:

&:位的與運算

<<:左移一位