在LeetCode的位运算题目中,191. 位1的个数(也称为汉明重量)是一道基础且经典的题目。它不仅考察对二进制的理解,更能帮助我们掌握位运算的核心技巧——从直观的暴力遍历到高效的位运算优化,两种解法的差距也能让我们深刻体会到“优化”在编程中的意义。
先明确题目要求:给定一个正整数 n,编写一个函数,获取其二进制形式中「设置位」(即值为1的位)的个数。比如,n=3(二进制 11),返回2;n=4(二进制 100),返回1。
下面我们逐一拆解两种解法,从思路、代码到优缺点,帮你彻底搞懂这道题。
解法一:暴力遍历法(右移 + 取余判断)
思路解析
最直观的思路的是:既然要统计二进制中1的个数,我们可以逐位判断每一位是否为1。具体步骤如下:
-
初始化一个计数器 res,用于记录1的个数,初始值为0;
-
用一个临时变量 cur 保存当前的n(也可以直接操作n,这里用cur是为了更清晰地展示过程);
-
循环判断 cur 是否不为0:每次用 cur % 2 取余,若余数为1,说明当前最低位是1,计数器res加1;
-
将 cur 右移1位(cur = cur >> 1),相当于把二进制的每一位依次“移到”最低位,方便下一次判断;
-
当 cur 变为0时,说明所有位都判断完毕,返回res即可。
代码实现
function hammingWeight_1(n: number): number {
let res = 0;
let cur = n;
while (cur !== 0) {
const one = cur % 2;
if (one === 1) res++;
cur = cur >> 1;
}
return res;
};
优缺点分析
优点:思路简单、容易理解,适合刚接触位运算的新手,代码可读性强,没有复杂的位运算技巧。
缺点:效率一般。假设n的二进制有k位,那么循环就要执行k次。比如n是2^31-1(二进制31个1),就需要循环31次,虽然实际运行速度也不慢,但存在优化空间。
解法二:高效位运算法(n & (n-1) 技巧)
这是本题的最优解,核心是利用位运算的特性,减少循环次数——循环次数等于二进制中1的个数,比解法一更高效。
核心技巧:n & (n - 1) 的作用
先记住一个关键结论:n & (n - 1) 会将n的二进制中「最右边的一个1」变成0。
举个例子:
-
n = 6(二进制 110),n-1 = 5(二进制 101),n & (n-1) = 100(二进制),最右边的1(第2位)被变成0;
-
n = 4(二进制 100),n-1 = 3(二进制 011),n & (n-1) = 000,最右边的1(第3位)被变成0;
-
n = 7(二进制 111),n-1 = 6(二进制 110),n & (n-1) = 110,最右边的1(第1位)被变成0。
也就是说,每执行一次 n & (n-1),就会消除一个1。那么,我们只需要统计执行多少次这个操作,直到n变成0,次数就是1的个数。
思路解析
-
初始化计数器 res 为0;
-
循环判断n是否不为0:每次执行n = n & (n - 1),消除一个1,同时res加1;
-
当n变为0时,说明所有1都被消除,返回res。
代码实现
function hammingWeight_2(n: number): number {
let res = 0;
while (n) {
n &= n - 1;
res++;
}
return res;
};
优缺点分析
优点:效率极高。循环次数等于二进制中1的个数,比如n=2^31-1(31个1),只需要循环31次;如果n=0,循环0次,是最优解。代码简洁,体现了位运算的魅力。
缺点:思路相对抽象,需要理解n & (n-1) 的特性,新手可能需要多举例验证才能掌握。
两种解法对比
| 解法 | 核心思路 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|---|
| 暴力遍历法 | 右移取余,逐位判断1 | O(k),k为二进制位数 | O(1) | 新手入门,理解二进制基础 |
| 高效位运算法 | n & (n-1) 消除最右1,统计次数 | O(m),m为二进制中1的个数 | O(1) | 面试最优解,追求高效 |
注意事项(避坑点)
-
本题中n是正整数,若n可能为负数(比如JavaScript中数字是有符号的),右移操作可能会出现符号位补1的情况,导致死循环。但题目明确n是正整数,因此无需处理这种情况;
-
解法一中,cur可以直接用n代替,简化代码(即去掉cur变量,直接操作n),效果一致;
-
解法二中,循环条件是while(n),等价于while(n !== 0),因为当n为0时,布尔值为false,循环终止。
总结
LeetCode 191题虽然简单,但两种解法的差异体现了“思路优化”的重要性。暴力解法胜在直观,适合入门;而n & (n-1) 的技巧则是位运算中的经典用法,不仅能解决这道题,还能应用在其他位运算题目中(比如判断一个数是否是2的幂、统计两个数的二进制差异等)。