题目描述:
两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。
给出两个整数 x 和 y,计算它们之间的汉明距离。
解题思路1:最最最最简单的想法,一定是先将两个数转换为二进制数,然后将他们二进制值存入数组,再遍历比较两个数组中的每一个值,若相减的绝对值不为0,就存在差异,然后给总数加一就能得到最初的结果。
所遇问题1:解题思路相当清晰,代码完成度也很高,进行检验,虽然通过了测试,但是感觉效率太低,完成所用空间和时间都相当的低。
public int hammingDistance(int x, int y) {
//分别拿到x和y的对应二进制值
Object arr[] = change(x);
Object brr[] = change(y);
int add = 0;
int j = 0;
//获取其中较小的一个值得二进制长度
int min = arr.length < brr.length ? arr.length:brr.length;
//在他们相同长度部分的区域,相减绝对值不为0就不相同
for (j = 0; j < min; j++) {
add = add + Math.abs((int)arr[j] - (int)brr[j]);
}
//在长度不同区域,较长的那一个只要每一位值不为0则两个数的每位比较就不相同
if(min == arr.length){
for (; j < brr.length; j++) {
if((int)brr[j] == 1)
add++;
}
}
else {
for (; j < arr.length; j++) {
if((int)arr[j] == 1)
add++;
}
}
return add;
}
//用于二进制转换的change方法
public Object[] change(int x) {
//用列表存储二进制值,就可动态获取长度
ArrayList list = new ArrayList();
int i = 0;
while (x != 0) {
list.add(x % 2);
x = x / 2;
}
Object arr[] = list.toArray();
return arr;
}
解题思路2:虽然校验通过,但我还是想要改进一下自己的代码,以获得更高效率,所以我想会不会是自己在列表转化数组,在强制内型转换耗费较多时间,且长度不同,又有太多分支判断,所以我就想着能不能用二维数组来分别存储这两个值的二进制数,因为题中限定了x和y的大小,所以他们的二进制长度也不会超出相应范围,这样定长后相减也较为方便。
所遇问题2:在对提出的问题进行解决之后,我发现代码的效率反而更低了,我就很疑惑。
public static int hammingDistance(int x, int y) {
int arr[][] = change(x,y);
int add = 0;
int j = 0;
//对于这个二维数组进行每一位比较
for (j = 0; j < 35; j++) {
add = add + Math.abs(arr[0][j] - arr[1][j]);
}
return add;
}
public static int[][] change(int x,int y) {
int[][] arr = new int[35][35];
int i = 0;
//寻找出较大的一个值,用于循环出口设置
int temp = x > y?x :y;
//进行二进制转换并存储
while (temp != 0) {
arr[0][i] = x % 2;
arr[1][i++] = y % 2;
x = x / 2;
y = y / 2;
temp = temp/2;
}
return arr;
}
解题思路3:最后我在浏览解题评论区当中发现了超级优化的解题思路,原作者是这样解释他的思路的:代码详解
//题解一
class Solution {
public int hammingDistance(int x, int y) {
return Integer.bitCount(x ^ y);
}
}
//题解二
class Solution {
public int hammingDistance(int x, int y) {
int xor = x ^ y;
int distance = 0;
while (xor != 0) {
if (xor % 2 == 1)
distance += 1;
xor = xor >> 1;
}
return distance;
}
}
//题解三
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;
}
}
代码理解:这道题,官方给出了三道解题思路,显然官方题解认为这道题的难点应该是在于如何是求和加一,我忘记了可以直接利用 ^ 运算符直接求出两个数的异或值。
1、 首先关于题解一,使用到了java自身的内置函数,去对于二进制中为1个数的求和相加。代码整体简短精炼。
2、 题解二则是将求得的二进制值进行移位操作,每次比较临界位的值是否为一,然后进行计数操作。
3、 最后一种解题思路,作者使用到了布赖恩·克尼根算法,方法二是逐位移动,逐位比较边缘位置是否为 1。寻找一种更快的方法找出等于 1 的位数。是否可以像人类直观的计数比特为 1 的位数,跳过两个 1 之间的 0。例如:10001000。上面例子中,遇到最右边的 1 后,如果可以跳过中间的 0,直接跳到下一个 1,效率会高很多。这是布赖恩·克尼根位计数算法的基本思想。该算法使用特定比特位和算术运算移除等于 1 的最右比特位。当我们在 number 和 number-1 上做 AND 位运算时,原数字 number 的最右边等于 1 的比特会被移除。
学习收获:
- 学习到了一个Integer类当中的内置方法—>bitcount方法,可以更加快捷的计算二进制数中的一的个数。