【leetcode.191】位1的個數

SunAlwaysOnline發表於2020-10-02

本文轉載自https://leetcode-cn.com/problems/number-of-1-bits/solution/wei-1de-ge-shu-by-leetcode/


一、題目描述

編寫一個函式,輸入是一個無符號整數,返回其二進位制表示式中數字位數為 ‘1’ 的個數(也被稱為漢明重量)。

示例 1:

輸入:00000000000000000000000000001011
輸出:3
解釋:輸入的二進位制串 00000000000000000000000000001011 中,共有三位為 '1'。
示例 2:

輸入:00000000000000000000000010000000
輸出:1
解釋:輸入的二進位制串 00000000000000000000000010000000 中,共有一位為 '1'。
示例 3:

輸入:11111111111111111111111111111101
輸出:31
解釋:輸入的二進位制串 11111111111111111111111111111101 中,共有 31 位為 '1'。
 

提示:

請注意,在某些語言(如 Java)中,沒有無符號整數型別。在這種情況下,輸入和輸出都將被指定為有符號整數型別,並且不應影響您的實現,因為無論整數是有符號的還是無符號的,其內部的二進位制表示形式都是相同的。
在 Java 中,編譯器使用二進位制補碼記法來表示有符號整數。因此,在上面的 示例 3 中,輸入表示有符號整數 -3。


二、思路

方法 1:迴圈和位移動
演算法

這個方法比較直接。我們遍歷數字的 32 位。如果某一位是1,則將計數器加一。

我們使用位掩碼來檢查數字的每一位。一開始,掩碼 m=1,因為1的二進位制表示是

0000 0000 0000 0000 0000 0000 0000 0001

顯然,任何數字跟掩碼1進行邏輯與運算,都可以讓我們獲得這個數字的最低位。檢查下一位時,我們將掩碼左移一位。

0000 0000 0000 0000 0000 0000 0000 0010

並重復此過程。

程式碼:

public int hammingWeight(int n) {
    int bits = 0;
    int mask = 1;
    for (int i = 0; i < 32; i++) {
        if ((n & mask) != 0) {
            bits++;
        }
        mask <<= 1;
    }
    return bits;
}

複雜度分析

時間複雜度:O(1)。執行時間依賴於數字n的位數。由於這題中n 是一個 32 位數,所以執行時間是 O(1) 的。

空間複雜度:O(1)。沒有使用額外空間。


方法 2:位操作的小技巧

我們可以把前面的演算法進行優化。我們不再檢查數字的每一個位,而是不斷把數字最後一個1反轉,並把答案加一。當數字變成0的時候偶,我們就知道它沒有1的位了,此時返回答案。

這裡關鍵的想法是對於任意數字n,將n和n-1做與運算,會把最後一個1的位變成0。為什麼?考慮n和n-1的二進位制表示。

在二進位制表示中,數字n中最低位的1總是對應n-1中的0。因此,將n和n-1與運算總是能把n中最低位的1變成0,並保持其他位不變。

使用這個小技巧,程式碼變得非常簡單。

程式碼:

public int hammingWeight(int n) {
    int sum = 0;
    while (n != 0) {
        sum++;
        n &= (n - 1);
    }
    return sum;
}

複雜度分析

時間複雜度:O(1)。執行時間與n中位為1的有關。在最壞情況下,n中所有位都是1。對於 32 位整數,執行時間是 O(1)的。

空間複雜度:O(1)。沒有使用額外空間。


三、後語

位運算確實有意思,對位運算有興趣的同學可以參考我的這篇文章拾遺位運算

相關文章