劍指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

方法總結:

&:位的與運算

<<:左移一位

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章