1. 程式人生 > 其它 >【位運算】力扣461:漢明距離

【位運算】力扣461:漢明距離

兩個整數之間的 漢明距離 指的是這兩個數字對應二進位制位不同的位置的數目。
給你兩個整數 x 和 y,計算並返回它們之間的漢明距離。
示例:

輸入:x = 1, y = 4
輸出:2
解釋:
1 (0 0 0 1)
4 (0 1 0 0)
↑ ↑
上面的箭頭指出了對應二進位制位不同的位置。

注意: \(0 ≤ x, y < 2 ^ {31}\)
漢明距離廣泛應用於多個領域。在編碼理論中用於錯誤檢測,在資訊理論中量化字串之間的差異。
兩個整數之間的漢明距離是對應位置上數字不同的位數。
根據以上定義,使用異或運算,記為 ⊕,當且僅當輸入位不同時輸出為 1。

計算 x 和 y 之間的漢明距離,可以先計算 x ⊕ y,兩個數字的異或結果,就是轉化為二進位制後按位比較的;然後統計結果中等於 1 的位數,那麼原始問題轉換為位計數問題


當然,如果不知道這樣的結果,也可以通過分別轉化兩個數成二進位制數,然後前補零,按照兩個字串的方式按位比較,效率也很高。這裡有個細節,具體前補多少零?因為 \(x, y < 2 ^ {31}\),所以設定zfill(32)即可。
方法一:暴力字串

class Solution:
    def hammingDistance(self, x: int, y: int) -> int:
        sx, sy = bin(x)[2:].zfill(32), bin(y)[2:].zfill(32) # 將整數x和y轉換為二進位制數,注意加上[2:]消除bin函式結果的字首影響
        res = 0
        for i in range(32):
            if sx[i] != sy[i]:
                res += 1
        return res

方法二:內建位計數功能

class Solution:
    def hammingDistance(self, x: int, y: int) -> int:
        return bin(x ^ y).count('1') # 注意count的是字串1,因為bin函式返回值是字串

時間複雜度:O(1)。
空間複雜度:O(1)。

方法二:移位實現位計數
記 s=x⊕y,可以不斷地檢查 s 的最低位,如果最低位為 1,那麼令計數器加一,然後我們令 s 整體右移一位,這樣 s 的最低位將被捨去,原本的次低位就變成了新的最低位。重複這個過程直到 s=0 為止。這樣計數器中就累計了 s 的二進位制表示中 1 的數量。

class Solution:
    def hammingDistance(self, x: int, y: int) -> int:
        s = x ^ y # 計算異或
        res = 0
        while(s):
            res += s & 1 # ???
            s >>= 1 # 右移一位
        return res

時間複雜度:O(logC),其中 C 是元素的資料範圍,在本題中 logC=log2^31=31。
空間複雜度:O(1)。

方法四:Brian Kernighan 演算法
在方法三中,對於 \(s=(10001100)_{2}\)的情況,需要迴圈右移 8 次才能得到答案。而實際上,如果可以跳過兩個 1 之間的 0,直接對 1 進行計數,那麼就只需要迴圈 3 次即可。
可以使用 Brian Kernighan 演算法進行優化,具體地,該演算法可以被描述為這樣一個結論:記 f(x) 表示 x 和 x−1 進行與運算所得的結果(即 f(x)=x & (x−1)),那麼 f(x) 恰為 x 刪去其二進位制表示中最右側的 1 的結果。

基於該演算法,當計算出 s=x⊕y,只需要不斷讓 s=f(s),直到 s=0 即可。這樣每迴圈一次,s 都會刪去其二進位制表示中最右側的 1,最終迴圈的次數即為 s 的二進位制表示中 1 的數量。

class Solution:
    def hammingDistance(self, x: int, y: int) -> int:
        s = x ^ y
        res = 0
        while(s):
            s &= s - 1
            res += 1
        return res

時間複雜度:O(logC),其中 C 是元素的資料範圍,在本題中logC=log2^31=31。
空間複雜度:O(1)。