计算正整数二进制表示中汉明重量的两种实现方式对比
在编程的世界里,我们常常会遇到一些有趣又实用的小问题,今天就来和大家分享一下如何计算一个正整数二进制表示中设置位(也就是 1
的个数,专业术语叫汉明重量)的问题。这看似简单,实则里面也有不少门道呢,下面我就带大家一起来看看两种不同的实现方式以及它们各自的特点。
一、最初的实现方式及分析
先来看下面这段 Java 代码,它的目的就是计算给定正整数 n
的二进制表示中 1
的个数。
public class Solution {
public int hammingWeight(int n) {
int count = 0;
//先把正整数变成二进制的形式
while(n > 1){
if(n % 2 == 1){
count++;
n /= 2;
}
}
if(n == 1){
count++;
}
//获取1的个数
//输出总数
return count;
}
}
首先,定义了一个变量 count
,用来统计 1
的个数,初始值设为 0
。然后进入了一个 while
循环,只要 n
的值大于 1
,就会持续执行循环体里面的操作。在循环体中,通过 n % 2 == 1
这个条件判断来检查 n
的二进制表示的最低位是不是 1
。如果是 1
的话,就说明找到了一个设置位,这时候就把 count
的值加 1
,接着再通过 n /= 2
操作将 n
的值除以 2
,相当于去掉已经判断过的最低位,好继续去判断下一位。
当 while
循环结束后,有可能出现 n
刚好剩下 1
的情况呀,所以又单独用了一个 if
语句来判断,如果 n
等于 1
,那就意味着还有一个设置位没统计到,这时候再把 count
的值加 1
。最后,通过 return count
把统计好的设置位个数返回出去。
不过呢,虽然这段代码能够实现我们想要的功能,但是它存在一些不足之处哦。
从效率方面来讲,它采用的是常规的取余(%
)和除法(/
)运算来逐位判断整数 n
的二进制情况。要知道,在 Java 这种编程语言里,位运算的效率往往比算术运算要高很多呢,尤其是像咱们这种需要频繁去判断每一位情况的场景。每次都用取余和除法操作,计算次数多了的话,就会花费比较多的时间,要是处理的数据量很大或者整数本身比较大,那这个性能问题就会更明显啦。
从代码简洁性和可读性的角度来看,它把对 n
大于 1
时的循环处理和 n
等于 1
时的单独判断分开写了,虽然逻辑上不难理解,但是相对来说有点不够简洁,要是代码逻辑更复杂一点,这样分开处理可能就会让整体代码结构显得有点凌乱了呢。
二、改进后的实现方式
为了克服上面代码存在的一些小问题,我们可以采用位运算来对代码进行优化,下面就是改进后的代码:
public class Solution {
public int hammingWeight(int n) {
int count = 0;
while (n!= 0) {
count += n & 1;
n >>>= 1;
}
return count;
}
}
一开始也是定义了一个 count
变量,初始化为 0
,用来统计 1
的个数。然后进入了 while
循环,不过这里循环的条件变成了 n!= 0
,只要 n
的值不为 0
,就会一直循环下去。
在循环体里面,关键的操作就是 count += n & 1
这一句啦。这里利用了位运算中的按位与(&
)操作,n & 1
可以巧妙地获取 n
的二进制表示的最低位的值哦,如果最低位是 1
,那这个按位与的结果就是 1
,如果最低位是 0
,结果就是 0
,然后把这个结果累加到 count
上,就相当于统计到了这一位是不是设置位啦。接着,通过 n >>>= 1
这个无符号右移操作,把 n
的二进制表示向右移动一位,相当于去掉刚刚已经判断过的最低位,这样就能接着去判断下一位了。就这样,循环一直进行,直到 n
的值变为 0
,这时候 count
里面存储的就是 n
的二进制表示中 1
的个数啦,最后通过 return count
返回这个统计结果就行。
对比最初的代码,改进后的代码在效率上有了很大的提升哦,利用位运算快速地逐位判断,避免了频繁的算术运算带来的性能损耗。而且代码结构也更加简洁明了,把整个判断和统计的过程都统一在一个循环里面完成了,让人一眼就能看明白它的逻辑,阅读和维护起来也更加方便呢。
希望通过对这两种计算正整数二进制表示中汉明重量的代码实现方式的分析,大家能对这类问题的解决以及代码优化有更深入的理解呀。在实际编程中,我们要时刻留意这些小细节,选择更高效、更简洁的实现方式,这样才能编写出高质量的代码哦。