题目描述
思路解析动画文字版
记住这句「n & (n-1) 消掉最低位的 1」,下面每一帧都在套它——而且循环次数 = 1 的个数,不是 32 位逐位扫。
间隔分布的 6 个 1,最能看清「每次只灭最右边那个 1、范围逐步左收」。
先把 n = 2730 摊成 12 位。亮着的格子是 1、暗的是 0。接下来每按一次 n&(n-1),就灭掉最右边那个 1。
当前 n = 2730。n−1 会把「最低位这个 1」借成 0、它右边的 0 全变 1;再和 n 按位与,正好只把这一位清零,其余不动。
那一位已变 0(绿色记一笔),计数 +1 = 1。n 还剩 2728,继续消下一个最低位的 1。
当前 n = 2728。n−1 会把「最低位这个 1」借成 0、它右边的 0 全变 1;再和 n 按位与,正好只把这一位清零,其余不动。
那一位已变 0(绿色记一笔),计数 +1 = 2。n 还剩 2720,继续消下一个最低位的 1。
当前 n = 2720。n−1 会把「最低位这个 1」借成 0、它右边的 0 全变 1;再和 n 按位与,正好只把这一位清零,其余不动。
那一位已变 0(绿色记一笔),计数 +1 = 3。n 还剩 2688,继续消下一个最低位的 1。
当前 n = 2688。n−1 会把「最低位这个 1」借成 0、它右边的 0 全变 1;再和 n 按位与,正好只把这一位清零,其余不动。
那一位已变 0(绿色记一笔),计数 +1 = 4。n 还剩 2560,继续消下一个最低位的 1。
当前 n = 2560。n−1 会把「最低位这个 1」借成 0、它右边的 0 全变 1;再和 n 按位与,正好只把这一位清零,其余不动。
那一位已变 0(绿色记一笔),计数 +1 = 5。n 还剩 2048,继续消下一个最低位的 1。
当前 n = 2048。n−1 会把「最低位这个 1」借成 0、它右边的 0 全变 1;再和 n 按位与,正好只把这一位清零,其余不动。
这一位灭掉后 n 变成 0,循环结束。一共消了 6 次,就是 6 个 1。
所有 1 都被消光、n 归 0。绿格子就是当初的每个 1。注意循环只跑了 6 次(= 1 的个数),不是把 12 位逐个看一遍。
相邻的 1 也不例外:n−1 只动「最低那一个 1」及其右边,左边的 1 纹丝不动。
先把 n = 7 摊成 12 位。亮着的格子是 1、暗的是 0。接下来每按一次 n&(n-1),就灭掉最右边那个 1。
当前 n = 7。n−1 会把「最低位这个 1」借成 0、它右边的 0 全变 1;再和 n 按位与,正好只把这一位清零,其余不动。
那一位已变 0(绿色记一笔),计数 +1 = 1。n 还剩 6,继续消下一个最低位的 1。
当前 n = 6。n−1 会把「最低位这个 1」借成 0、它右边的 0 全变 1;再和 n 按位与,正好只把这一位清零,其余不动。
那一位已变 0(绿色记一笔),计数 +1 = 2。n 还剩 4,继续消下一个最低位的 1。
当前 n = 4。n−1 会把「最低位这个 1」借成 0、它右边的 0 全变 1;再和 n 按位与,正好只把这一位清零,其余不动。
这一位灭掉后 n 变成 0,循环结束。一共消了 3 次,就是 3 个 1。
所有 1 都被消光、n 归 0。绿格子就是当初的每个 1。注意循环只跑了 3 次(= 1 的个数),不是把 12 位逐个看一遍。
边界看两端:全 0 一次不进、全 1 跑满位宽、2 的幂只消一次——循环次数永远等于 1 的个数。
两个高频追问:借位原理 + 相比逐位扫的优势(循环次数 = 1 的个数)。
参考代码
def hammingWeight(n: int) -> int: count = 0 while n != 0: n &= n - 1 # 抹掉最低位的 1 count += 1 # 抹一次就数一个 return count复杂度
- 时间:O(k),k = 1 的个数;循环只跑「1 的个数」次,而非位宽次
- 空间:O(1),只用 n 和 count 两个变量
易错点
面试追问把动画讲成自己的话
追问为什么 n & (n - 1) 能恰好消掉最低位的 1?
追问这个方法比「逐位 &1 右移」好在哪?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
比特位计数
LeetCode 338 · 简单 · 沿着 位运算 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题