思路1:
首先根据题目确定数字的位数最多31位。其实就是依次判断对应位是否相同。可以通过对数字进行右移然后判断最低位是否相同。最低位我们知道是决定奇偶的。所以直接通过对x y奇偶是否相同进行判断即可~
代码:
class Solution {
public:
int hammingDistance(int x, int y) {
int count=0;
for(int i=1; i<=31;i++){
if(x%2!=y%2)
count+=1;
x=x>>1; y=y>>1;
}
return count;
}
};
思路2:
思路2和思路1差不多,但是能减少一定的时间复杂度。主要利用了异或的性质。不同位输出1,相同位输出0,而我们正是要不同位。问题就变成了检测异或结果中1位的个数:
class Solution {
public:
int hammingDistance(int x, int y) {
int s = x ^ y, ret = 0;
while (s) {
ret += s & 1; // 注意到出了%2 也可以用 s与1来&位与实现最后1位是否1的判断。因为1前面都是0,输出与肯定都是0,所以输出是否1看s的最后1位是不是1.所以也是判断奇偶的
s >>= 1;
}
return ret;
}
};
思路3:
思路三很巧妙,主要用到了Brian Kernighan 算法,这个算法的性质可以拿来活用。
在方法二中,对于 s=(10001100)2 的情况,我们需要循环右移 8次才能得到答案。而实际上如果我们可以跳过两个 11 之间的 00,直接对 11 进行计数,那么就只需要循环 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 {
public:
int hammingDistance(int x, int y) {
int s = x ^ y, ret = 0;
while (s) {
s &= s - 1;
ret++;
}
return ret;
}
};
总结:
1. x%2 或者x&1==1?帮助我们判断数字的奇偶或者说他们最后一位是1还是0.并通过左移实现多位判断。
2. 异或性质帮助我们找出两个二进制中不同位并且标号1
3. 说白了 Brian Kernighan算法的性质能帮助我们快速统计二进制中等于1的位数!!