题目描述

✅ 191. 位1的个数

image-20260928203903818

image-20260928203903819

题意分析

统计给定整数的二进制表示中有多少个位为 1,返回这个数量,也称为汉明重量。不是统计十进制数位,也不需要保留这些 1 原来的位置。

下面按照 32 位整数处理。Java 使用 int 保存位模式,Go 使用 uint32;要数的是全部 32 位中的 1,不应让 Java 的正负号改变计数方式。

解法:不断消去最低位 1

核心思路

[!blue]

对非零整数,定位它最右侧的 1。这一位右侧全为零,减去一时必须向这个 1 借位:它由一变成零,右侧的零全部变成一,更高的位不变。

再计算 n & (n - 1):更高位保持原样;这个最低位 1 与零相与后被清除;更低位原本就是零,与任何位相与仍为零。因此这次运算恰好消去一个 1,其他 1 不受影响。

每清除一次就将计数加一。处理若干轮后,“已经清除的数量 + 当前剩余的 1 的数量”始终等于原答案;当 n 变成零时没有剩余,计数器就是结果。这样循环次数只取决于 1 的数量,无需逐个检查为零的位。

条件使用 n != 0,而不是 n > 0。Java 的最高位为一时会把位模式解释成负数,但这些位仍然需要计数;补码减法和按位与同样能逐次清除它们。

解题步骤

  1. 初始化 count = 0。
  2. 当 n != 0 时,用 n &= n - 1 清除最低位的一个 1。
  3. 每清除一次,将 count 加一,继续处理剩余位。
  4. n 变为零后返回 count;输入本身为零时自然返回零。

代码实现

class Solution {
    public int hammingWeight(int n) {
        int count = 0;

        while (n != 0) {
            // 每次只清掉最低的一位一,循环次数就是置位数量。
            n &= n - 1;
            count++;
        }

        return count;
    }
}
func hammingWeight(n uint32) int {
    count := 0
    for n != 0 {
        // 每次只清掉最低的一位一,循环次数就是置位数量。
        n &= n - 1
        count++
    }
    return count
}

复杂度分析

  • 时间复杂度:$O(k)$,其中 $k$ 是二进制中 1 的数量,每轮恰好清除一个。对于固定 32 位整数,最多循环 32 次,也可以记为 $O(1)$。
  • 空间复杂度:$O(1)$,只保存计数器并更新整数本身。

关键点总结

[!green]

  • 减一改变最低位 1 及其右侧位,按位与则只消去这个 1。
  • 每轮恰好删除一个待计数对象,所以循环次数直接等于答案。
  • 按位计数处理的是位模式,有符号类型的最高位也必须统计。

易错点总结

[!yellow]

  • 使用 n > 0 会直接跳过最高位为一的 Java 位模式,应判断是否为零。
  • 只计算 n - 1 而没有按位与,并不保证只减少一个 1,甚至可能增加低位的一。
  • n & -n 会提取最低位的一,不能替代本题所需的“清除”操作。
  • 如果选择逐位检查的写法,必须覆盖全部 32 位;本方法无需手动维护位下标。

相似题目

题目 难度 关联与区别
338. 比特位计数 简单 对0到n批量求位数时,可利用更小数的计数结果复用状态,而不是逐个重新统计。
461. 汉明距离 简单 先异或得到不同位,再调用位计数即得到汉明距离。
137. 只出现一次的数字 II 中等 按位统计重复模式并重建答案;本题统计一个整数的置位数,该题各位计数对三取模。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/15862521
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!