Bootstrap

汉明距离问题详解

https://leetcode.cn/problems/hamming-distance/solution/yi-ming-ju-chi-by-leetcode-solution-u1w7/

前言
汉明距离广泛应用于多个领域。在编码理论中用于错误检测,在信息论中量化字符串之间的差异。

两个整数之间的汉明距离是对应位置上数字不同的位数。

根据以上定义,我们使用异或运算,记为 \oplus⊕,当且仅当输入位不同时输出为 11。

计算 xx 和 yy 之间的汉明距离,可以先计算 x \oplus yx⊕y,然后统计结果中等于 11 的位数。

现在,原始问题转换为位计数问题。位计数有多种思路,将在下面的方法中介绍。

方法一:内置位计数功能
思路及算法

大多数编程语言都内置了计算二进制表达中 11 的数量的函数。在工程中,我们应该直接使用内置函数。

代码

C++JavaGolangC

class Solution {
public:
    int hammingDistance(int x, int y) {
        return __builtin_popcount(x ^ y);
    }
};
复杂度分析

时间复杂度:O(1)O(1)。不同语言的实现方法不一,我们可以近似认为其时间复杂度为 O(1)O(1)。

空间复杂度:O(1)O(1)。

方法二:移位实现位计数
思路及算法

在锻炼算法能力时,重复造轮子是不可避免的,也是应当的。因此读者们也需要尝试使用各种方法自己实现几个具有位计数功能的函数。本方法将使用位运算中移位的操作实现位计数功能。

具体地,记 s = x \oplus ys=x⊕y,我们可以不断地检查 ss 的最低位,如果最低位为 11,那么令计数器加一,然后我们令 ss 整体右移一位,这样 ss 的最低位将被舍去,原本的次低位就变成了新的最低位。我们重复这个过程直到 s=0s=0 为止。这样计数器中就累计了 ss 的二进制表示中 11 的数量。

代码

C++JavaC#JavaScriptGolangC

class Solution {
    public int hammingDistance(int x, int y) {
        int s = x ^ y, ret = 0;
        while (s != 0) {
            ret += s & 1;
            s >>= 1;
        }
        return ret;
    }
}
复杂度分析

时间复杂度:O(\log C)O(logC),其中 CC 是元素的数据范围,在本题中 \log C=\log 2^{31} = 31logC=log2 
31
 =31。

空间复杂度:O(1)O(1)。

方法三:\text{Brian Kernighan}Brian Kernighan 算法
思路及算法

在方法二中,对于 s=(10001100)_2s=(10001100) 
2

  的情况,我们需要循环右移 88 次才能得到答案。而实际上如果我们可以跳过两个 11 之间的 00,直接对 11 进行计数,那么就只需要循环 33 次即可。

我们可以使用 \text{Brian Kernighan}Brian Kernighan 算法进行优化,具体地,该算法可以被描述为这样一个结论:记 f(x)f(x) 表示 xx 和 x-1x−1 进行与运算所得的结果(即 f(x)=x~\&~(x-1)f(x)=x & (x−1)),那么 f(x)f(x) 恰为 xx 删去其二进制表示中最右侧的 11 的结果。

基于该算法,当我们计算出 s = x \oplus ys=x⊕y,只需要不断让 s = f(s)s=f(s),直到 s=0s=0 即可。这样每循环一次,ss 都会删去其二进制表示中最右侧的 11,最终循环的次数即为 ss 的二进制表示中 11 的数量。

注意

\text{Brian Kernighan}Brian Kernighan 算法发布在 19881988 年出版的 \text{The C Programming Language (Second Edition)}The C Programming Language (Second Edition) 的练习中(由 \text{Brian W. Kernighan}Brian W. Kernighan 和 \text{Dennis M. Ritchie}Dennis M. Ritchie 编写),但是 \text{Donald Knuth}Donald Knuth 在 20062006 年 44 月 1919 日指出,该方法第一次是由 \text{Peter Wegner}Peter Wegner 在 19601960 年的 \text{CACM3}CACM3 上出版。可以在上述书籍中找到更多位操作的技巧。

代码

C++JavaC#JavaScriptGolangC

class Solution {
    public int hammingDistance(int x, int y) {
        int s = x ^ y, ret = 0;
        while (s != 0) {
            s &= s - 1;
            ret++;
        }
        return ret;
    }
}
复杂度分析

时间复杂度:O(\log C)O(logC),其中 CC 是元素的数据范围,在本题中 \log C=\log 2^{31} = 31logC=log2 
31
 =31。

空间复杂度:O(1)O(1)。

;