今天是 Kevin 的算法之路的第6天。为大家讲解 LeetCode 热门题中的第 461 题,是一道经典的位运算的题,值得了解一波。
每日一笑
小学有一次写作文,要求展现出真实情感来。我后来就编了个妈妈冒雨去给菜地浇水的故事,将自己都感动得快哭了。结果,作文却不及格。老师给的评语是:“不尊重事实。”后来才发现,下大雨哪里还需要浇水啊。
题目描述
解题思路
两个数字对应二进制位不同的位置的数目。我们应该想到异或(XOR)位运算,当且仅当输入位不同时输出为1。
xyx XOR y
现在问题就转化成了位计数问题。我们想想如何统计二进制数中1的位数呢?
方法一:内置函数
大多数编程语言中,都存在各种内置计算等于 1 的位数函数。比如 Java 中的 Integer.bitCount() ,Golang 中的 bits.OnesCount()
// java
class Solution {
public int hammingDistance(int x, int y) {
return Integer.bitCount(x ^ y);
}
}
//go
func hammingDistance(x int, y int) int {
xor := x ^ y
return bits.OnesCount(uint(xor))
}
方法二:移位
第一种方法虽然简单易写,在项目中写没有问题。但是如果在面试中,面试官可能就是要考查我们位计数,所以我们可以用移位操作来实现。
11
更准确的说,应该进行逻辑移位,移入零替换丢弃的位。
i % 2i & 1
// java
class Solution {
public int hammingDistance(int x, int y) {
int xor = x ^ y;
int distance = 0;
while (xor != 0) {
if (xor & 1 == 1)
distance += 1;
xor = xor >> 1;
}
return distance;
}
}
//go
func hammingDistance(x int, y int) int {
xor := x ^ y
dis := 0
for xor != 0 {
if xor & 1 == 1 {
dis += 1
}
xor = xor >> 1
}
return dis
}
方法三:布赖恩·克尼根算法
这个方法来自官方题解,感觉有点牛逼,分享一下。
思路
方法二是逐位移动,逐位比较边缘位置是否为 1。寻找一种更快的方法找出等于 1 的位数。
是否可以像人类直观的计数比特为 1 的位数,跳过两个 1 之间的 0。例如:10001000。
上面例子中,遇到最右边的 1 后,如果可以跳过中间的 0,直接跳到下一个 1,效率会高很多。
这是布赖恩·克尼根位计数算法的基本思想。该算法使用特定比特位和算术运算移除等于 1 的最右比特位。
当我们在 number 和 number-1 上做 AND 位运算时,原数字 number 的最右边等于 1 的比特会被移除。
10001000
//java
class Solution {
public int hammingDistance(int x, int y) {
int xor = x ^ y;
int distance = 0;
while (xor != 0) {
distance += 1;
// remove the rightmost bit of '1'
xor = xor & (xor - 1);
}
return distance;
}
}
//go
func hammingDistance(x int, y int) int {
xor := x ^ y
dis := 0
for xor != 0 {
dis += 1
xor = xor & (xor - 1)
}
return dis
}
郑重声明:
所展示代码已通过 LeetCode 运行通过,请放心食用~
在唠唠嗑
216.65216.610066.6