leetcode1/leetcode 仓库实战解析:Counting Bits(LeetCode 338)从位掩码到 O(n) 动态规划的完整解法

发布时间:2026/11/9 8:55:45

leetcode1/leetcode 仓库实战解析:Counting Bits(LeetCode 338)从位掩码到 O(n) 动态规划的完整解法 leetcode1/leetcode 仓库实战解析Counting BitsLeetCode 338从位掩码到 O(n) 动态规划的完整解法【免费下载链接】leetcodeLeetcode solutions项目地址: https://gitcode.com/GitHub_Trending/leetcode1/leetcode本文以leetcode1/leetcode仓库中的 articles/counting-bits.md 为核心系统拆解「给定整数 n返回 0 到 n 每个整数的二进制中 1 的个数」这一经典问题的五种解法32 位掩码逐位检查、Kernighan 算法、语言内置 popcount、按 2 的幂 offset 的动态规划DP、右移 DP。并结合仓库中 11 种语言的 0338 题目实现 与 hints/counting-bits.md 官方提示逐条交叉印证读完你可以独立推导任意一种 bit counting 递推式并规避移位方向、运算符优先级、循环边界等常见错误。一、题目陈述与前置知识问题定义与 c/0338-counting-bits.c 头部注释一致给定整数n返回长度为n 1的数组ans其中ans[i]是i的二进制表示中 1 的个数。约束为0 n 10^5。例如n 2时返回[0, 1, 1]0有 0 个 11有 1 个 12 10有 1 个 1这一示例在 cpp/0338-counting-bits.cpp 的注释中也有记载。原始文档在动手解题前列出了三项前置能力要求位操作Bit Manipulation理解二进制表示与按位与、按位或、移位运算动态规划Dynamic Programming能够基于已计算结果增量地构建解二进制数系统Binary Number System理解整数如何以二进制存储、如何统计置位set bit。hints/counting-bits.md 给出的「目标复杂度」是O(n) 时间、O(n) 空间其中空间主要指输出数组本身提示还明确指出逐位遍历的朴素做法是 O(n log n)应通过观察连续数字的二进制模式寻找更优递推关系。二、解法一32 位掩码逐位检查Bit Manipulation - I核心思想对0到n中的每个数独立地检查它的每一位整数通常按32 位表示用位掩码(1 i)测试第i位是否置位。该解法不是最优的但最直观地展示了位运算在底层是如何工作的适合作为位操作的入门范本。算法步骤初始化空列表res对每个num ∈ [0, n]置计数器one 0对每一位i ∈ [0, 31]若(1 i) num非零one加一将one追加进res返回res。class Solution: def countBits(self, n: int) - List[int]: res [] for num in range(n 1): one 0 for i in range(32): if num (1 i): one 1 res.append # 占位示意完整写法见下 return res完整可运行版本Python 与 Javaclass Solution: def countBits(self, n: int) - List[int]: res [] for num in range(n 1): one 0 for i in range(32): if num (1 i): one 1 res.append(one) return respublic class Solution { public int[] countBits(int n) { int[] res new int[n 1]; for (int num 1; num n; num) { for (int i 0; i 32; i) { if ((num (1 i)) ! 0) { res[num]; } } } return res; } }原文档中此解法还附有 C、JavaScript、C#、Go、Kotlin、Swift、Rust 共 10 种语言的完整实现统一遵循「外层遍历数字、内层遍历 32 个位」的结构可查阅 articles/counting-bits.md 获取全部代码。复杂度时间复杂度O(n log n)按原文档口径内层循环固定 32 次实际执行量是 32n 次位与操作常数因子与机器字长相关空间复杂度O(1) 额外空间输出数组 O(n)。三、解法二Kernighan 算法Bit Manipulation - II核心思想高效统计单个整数的 1 位数可以使用Brian Kernighan 算法其关键观察是n (n - 1)会抹掉n最低位的 1重复该操作直到n变为 0操作次数就是 1 位的个数。这样就避免了「每个数字都检查全部 32 位」的浪费——循环次数只与该数实际含有的 1 位数成正比。算法步骤创建长度为n 1、初始全 0 的数组res对每个i ∈ [1, n]令num i当num ! 0时res[i]加一并执行num (num - 1)返回res。class Solution: def countBits(self, n: int) - List[int]: res [0] * (n 1) for i in range(1, n 1): num i while num ! 0: res[i] 1 num (num - 1) return respublic class Solution { public int[] countBits(int n) { int[] res new int[n 1]; for (int i 1; i n; i) { int num i; while (num ! 0) { res[i]; num (num - 1); } } return res; } }仓库实现印证仓库中的 Rust 与 Swift 实现正是这一思路的独立落证rust/0338-counting-bits.rs 将 Kernighan 循环抽成独立函数set_bits(n)主函数对0..n逐个调用swift/0338-counting-bits.swift 中的popCount(_ x: Int)同样是while x ! 0 { x x - 1; count 1 }。此外 java/0338-counting-bits.java 给出了一个值得注意的变体——把 Kernighan 思想直接融入 DP 的一行式class Solution { public int[] countBits(int n) { int res[] new int[n 1]; for (int i 1; i n; i) res[i] 1 res[i (i - 1)]; return res; } }因为i (i - 1)恰好是「抹掉i最低位 1 之后的数」所以i的 1 位数等于该数的 1 位数加 1一行同时完成了去位与查表把 O(n log n) 的逐数循环压缩成 O(n) 的单次查表。复杂度时间复杂度O(n log n)内层 while 执行次数等于该数的 1 位数空间复杂度O(1) 额外空间输出数组 O(n)。四、解法三语言内置函数In-Built Function适用场景很多语言提供了「转二进制字符串」或「直接统计置位」的内置手段写出来的解法非常简洁。原文明确其适用条件n为小到中等规模可读性优先于极限性能想要快速、可靠的实现。算法步骤初始化空结果列表对每个i ∈ [0, n]用内置工具把i转为二进制表示或调用 popcount 原语统计其中 1 的个数返回结果。原文档给出的 10 种语言实现可以归纳成一张内置函数速查表每种语言的具体代码均可在 articles/counting-bits.md 中对照语言核心调用示例写法Pythonbin(i).count(1)return [bin(i).count(1) for i in range(n 1)]JavaInteger.bitCount(i)res[i] Integer.bitCount(i);C__builtin_popcount(i)GCC/Clang 内建res[i] __builtin_popcount(i);JavaScripti.toString(2).split(1).length - 1转二进制字符串后按1分割计数C#Convert.ToString(i, 2)配合 LINQ...Count(c c 1)Gobits.OnesCount(uint(i))math/bits包res[i] bits.OnesCount(uint(i))Kotlin标准库扩展it.countOneBits()return IntArray(n 1) { it.countOneBits() }Swiftnum.nonzeroBitCountres[num] num.nonzeroBitCountRusti.count_ones()(0..n).map(\|i\| i.count_ones() as i32).collect()JavaScript 版本的具体实现展示了「无 popcount 原语时如何用字符串技巧数 1」class Solution { countBits(n) { let res []; for (let i 0; i n; i) { res.push(i.toString(2).split(1).length - 1); } return res; } }仓库中的 typescript/0338-counting-bits.ts 走的也是字符串路线只是把逐字符判断封装成了辅助函数hammingWeight(n)function hammingWeight(n: number): number { let base2 n.toString(2).split(); let count 0; base2.forEach((item) { if (item 1) count 1; }); return count; }从源码结构看TS 版与原文档 JS 版都属于「O(log n) 转字符串 O(log n) 计数」一类与上表的字符串路线一致。复杂度时间复杂度O(n log n)每个数的二进制转换/计数是 O(log n)空间复杂度O(1) 额外空间输出数组 O(n)。五、解法四按 2 的幂 offset 的动态规划Bit Manipulation - DP这是 hints/counting-bits.md 提示逐步引导出的官方 DP 关系。核心思想二进制表示有一个关键模式每到一个 2 的幂位图就整体重复一遍。2 的幂次本身就只含1 个 1 位任意数i都可以拆成i highestPowerOfTwo(≤ i) 余数因此i的 1 位数 1最高 2 的幂贡献 余数的 1 位数。这使我们可以增量地复用已算出的结果正是动态规划的标准形态。以 hints/counting-bits.md 中 Hint 2 的例子计算 7 的 1 位数时等于在 3 的 1 位数上加 1而 3 的 1 位数又是在 1 的 1 位数上加 1。观察可推广为小于 4 的数在「往前 2 个位置」的计数上加 1小于 8 的数在「往前 4 个位置」的计数上加 1。算法步骤创建 DP 数组dp长度n 1dp[i]存放i的 1 位数初始化dp[0] 0offset 1追踪最近的 2 的幂对每个i ∈ [1, n]若i 2 * offset到达下一个 2 的幂更新offset i递推dp[i] 1 dp[i - offset]返回dp。class Solution: def countBits(self, n: int) - List[int]: dp [0] * (n 1) offset 1 for i in range(1, n 1): if offset * 2 i: offset i dp[i] 1 dp[i - offset] return dppublic class Solution { public int[] countBits(int n) { int[] dp new int[n 1]; int offset 1; for (int i 1; i n; i) { if (offset * 2 i) { offset i; } dp[i] 1 dp[i - offset]; } return dp; } }func countBits(n int) []int { dp : make([]int, n1) offset : 1 for i : 1; i n; i { if offset*2 i { offset i } dp[i] 1 dp[i - offset] } return dp }仓库实现印证这一解法在仓库中是「官方主解」多语言实现几乎逐行对应文档python/0338-counting-bits.py 的主体实现与文档 Python 版完全一致offset * 2 i时更新 offsetdp[i] 1 dp[i - offset]go/0338-counting-bits.go 与 kotlin/0338-counting-bits.kt 同样是 offset 递推hints/counting-bits.md 的 Hint 3 直接点明递推式dp[i] 1 dp[i - offset]以及「offset * 2等于当前数时更新 offset」这一判定技巧与上述代码一一对应。另外 python/0338-counting-bits.py 还附带了一个注释为「Another dp solution」的奇偶拆分变体思路更直白奇数的 1 位数等于前一个偶数加 1偶数右移一位 1 位数不变class Solution2: def countBits(self, n: int) - List[int]: res [0] * (n 1) for i in range(1, n 1): if i % 2 1: res[i] res[i - 1] 1 else: res[i] res[i // 2] return resruby/0338-counting-bits.rb 则是 offset 递推的一个更复杂的 Ruby 风格写法用offset初值 2、每轮翻倍的方式处理边界从源码结构看属于同一递推关系的不同工程化实现。复杂度时间复杂度O(n)每个数只做一次查表空间复杂度O(1) 额外空间输出数组 O(n)。六、解法五右移 DPBit Manipulation - Optimal核心思想这是原文档标注的最优解递推式最短i 1相当于「去掉最低位」后的数即i / 2i 1告诉最低位是 1 还是 0即i mod 2因此setBits(i) setBits(i 1) (i 1)。每个结果只依赖一个已算出的更小值天然是 DP。cpp/0338-counting-bits.cpp 的注释里把这个等式写成了整数语言f(x) f(x / 2) (x mod 2)并注明 Time O(n)、Space O(1)输出数组不计入。算法步骤创建 DP 数组dp长度n 1dp[0] 0对每个i ∈ [1, n]dp[i] dp[i 1] (i 1)返回dp。class Solution: def countBits(self, n: int) - List[int]: dp [0] * (n 1) for i in range(n 1): dp[i] dp[i 1] (i 1) return dpint *countBits(int n, int *returnSize) { int *ret calloc(n 1, sizeof(int)); *returnSize n 1; for (int i 1; i n; i) ret[i] ret[i 1] (i 1); return ret; }C 语言版本c/0338-counting-bits.c值得逐行注意两个 C 特有的细节用calloc一次性清零分配n 1个int等价于dp [0] * (n 1)通过输出参数*returnSize n 1向调用者返回数组长度且文件头注释声明「返回数组由 malloc 分配由调用者负责 free」——这是 LeetCode C 题模板的内存约定移植到其他项目时必须保留否则会泄漏内存。JavaScript 版则展示了「以函数参数承载 DP 表」的等价写法见 javascript/0338-counting-bits.jsvar countBits function (n, dp [0]) { for (let i 1; i n 1; i) { const [mid, bit] [i 1, i 1]; dp.push(dp[mid] bit); } return dp; };原文档该解法同样覆盖 C、C#、Go、Kotlin、Swift、Rust 等语言结构均为「一行递推 单循环」可对照 articles/counting-bits.md。复杂度时间复杂度O(n)空间复杂度O(1) 额外空间输出数组 O(n)。七、五种解法横向对比结合原文档的复杂度结论与 hints/counting-bits.md 的目标复杂度要求解法核心操作时间复杂度额外空间特点32 位掩码(1 i) num逐位测试O(n log n)O(1)最直观展示位运算底层Kernighannum num - 1逐个去 1O(n log n)O(1)循环次数与 1 位数成正比内置函数bitCount/popcount等O(n log n)O(1)最简洁可读性优先offset DPdp[i] 1 dp[i - offset]O(n)O(1)利用 2 的幂周期官方提示路线右移 DPdp[i] dp[i 1] (i 1)O(n)O(1)递推最短仓库 C/C/JS 主选表中所有解法的输出数组均为 O(n)不计入额外空间与原文档口径一致。仓库各语言实现的实际选型也印证了这组权衡追求最优复杂度的 c/0338-counting-bits.c、cpp/0338-counting-bits.cpp、javascript/0338-counting-bits.js 选右移 DPpython/0338-counting-bits.py、go/0338-counting-bits.go、kotlin/0338-counting-bits.kt 选 offset DP而 rust/0338-counting-bits.rs、swift/0338-counting-bits.swift 选 Kernighan 循环java/0338-counting-bits.java 选i (i - 1)单行 DP——同一题目在不同实现里各取所长恰好可以作为多语言刷题时的对照样本。八、常见陷阱Common Pitfalls原文档专门列出两类高频错误结合仓库代码逐一展开陷阱 1移位方向与运算符优先级在递推dp[i] dp[i 1] (i 1)中两个经典失误# 错误一把右移写成左移 dp[i] dp[i 1] (i 1) # i 1 越界访问索引大于 i # 错误二加法先于移位执行运算符优先级陷阱 dp[i] dp[i 1 (i 1)] # 实际计算的是 i (1 (i 1)) # 正确写法 dp[i] dp[i 1] (i 1)对照 c/0338-counting-bits.c 中ret[i] ret[i 1] (i 1)的括号组织方式以及 javascript/0338-counting-bits.js 中先解构出mid i 1、bit i 1再组合的写法可以看出把移位与取位拆成两个中间变量本身就是对抗优先级错误的有效手段。陷阱 2循环边界 off-by-one题目要求统计0到n含 n结果数组必须是n 1个元素# 错误一数组开小了 dp [0] * n # 缺少 dp[n] # 错误二循环没有包含 n for i in range(n): # 应为 range(n 1) # 正确 dp [0] * (n 1) for i in range(n 1): # processC 语言里对应的正确姿势见 c/0338-counting-bits.ccalloc(n 1, sizeof(int))分配n 1个元素*returnSize n 1与循环for (int i 1; i n; i)三者保持一致任何一处写成n都会直接越界或漏算最后一个数。九、仓库文件索引围绕本题当前仓库中可直接查阅的材料如下路径内容articles/counting-bits.md本文核心来源五种解法 × 10 种语言完整实现hints/counting-bits.md官方提示目标复杂度、DP 递推推导python/0338-counting-bits.pyoffset DP 主解 奇偶拆分 DP 变体java/0338-counting-bits.java1 res[i (i - 1)]单行 DPc/0338-counting-bits.c右移 DP含returnSize与内存约定说明cpp/0338-counting-bits.cpp右移 DP附 f(x) f(x/2) x%2 推导注释rust/0338-counting-bits.rsKernighan 循环实现go/0338-counting-bits.gooffset DPkotlin/0338-counting-bits.ktoffset DPswift/0338-counting-bits.swiftKernighan popCount 实现javascript/0338-counting-bits.js右移 DP默认参数承载 DP 表typescript/0338-counting-bits.ts二进制字符串计数ruby/0338-counting-bits.rboffset 递推的 Ruby 风格实现以上实现均属于 README.md 所述的多语言题解集合该仓库收录 Python、Java、JavaScript、C、Go、Swift、C#、TypeScript、Rust、Kotlin、Ruby、C 等语言的 LeetCode 题解各文件保持独立、可直接复制到对应语言的 LeetCode 判题环境中运行验证。小结Counting Bits 是「位运算 动态规划」交叉点的教科书式问题——先学会 32 位掩码建立底层直觉再用 Kernighan 的n (n - 1)消除无谓的位检查最后用dp[i] dp[i 1] (i 1)或dp[i] 1 dp[i - offset]两条递推到 O(n) 收尾。仓库中 11 个语言的 0338 实现与官方提示文件提供了完整的交叉验证素材适合作为多语言位运算 DP 的对照学习样本。【免费下载链接】leetcodeLeetcode solutions项目地址: https://gitcode.com/GitHub_Trending/leetcode1/leetcode创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/11/9 8:55:45

万卡集群软硬协同:国产AI算力落地的系统工程

1. 万卡集群不是堆显卡,而是重构算力交付的底层逻辑“万卡集群”这四个字在最近两年的行业会议、技术白皮书和融资PPT里高频出现,但绝大多数人听到它,第一反应还是——“哇,好多GPU”。这种理解偏差,恰恰是国产大模型基…

2026/11/4 22:18:12

Rocky Linux中文乱码解决:字体安装与locale/编码排查指南

刚把一台 Rocky Linux 9 的测试服务器部署好,网页一打开,满屏的“□□□□□□”,日志文件里全是“????”。这种中文乱码,很多人第一反应是去改 locale、加export LANGzh_CN.UTF-8,结果折腾半天还是老样子。其实在…

2026/11/9 8:54:20

电工证初级本多久拿证?转岗人别再被中介忽悠了

电工证初级本多久拿证?转岗人别再被中介忽悠了 你是不是也遇到过这种情况:刚转行做电工,听说要考电工证初级本,但不知道去哪里报名,怕被中介坑?更别提多久拿证这个问题,一问三不知,最后花了冤枉钱还拿不到证?别急,今天就给你讲清楚电工证初级本怎么…

2026/11/9 8:54:20

淄矿技术学院电工证内部消息:零基础也能考下来,找个工作不难

淄矿技术学院电工证内部消息:零基础也能考下来,找个工作不难 你是不是正为找工作发愁?手里没个特种作业证,连面试资格都没有?别急, 淄矿技术学院电工证 就是你上岸的“跳板”,特别是对于想进工地、进工厂的劳务班组负责人来说, 内部消息…

2026/11/9 8:54:20

特种电工证昆明到期换证流程良心建议

特种电工证昆明到期换证流程良心建议 工地太忙根本没时间复习考试,这话说得真不假,我认识的一个工地电工老李,就是被这个绊住了。特种电工证昆明到期换证流程,对他来说不是难题,但复习和考试却成了大问题。其实很多人跟我一样,平时工作忙得脚不沾地,一…

2026/11/9 8:54:20

华亭考电工证真实经历:实操考试心里没底怕挂科怎么破?

华亭考电工证真实经历:实操考试心里没底怕挂科怎么破? 实操考试心里没底怕挂科,这是很多准备考电工证的朋友最真实的感受。尤其在华亭考电工证,很多人都担心自己动手能力不够,或者理论知识没掌握好,结果一上考场就慌了。其实,只要你方法对了,考试根本…

2026/11/9 8:54:20

淄矿技术学院电工证网上查询流程全解析,外省证怎么转过来

淄矿技术学院电工证网上查询流程全解析,外省证怎么转过来 之前考的证在外省能不能转过来?这几乎是所有准备考电工证或者已经持证的人最关心的问题。特别是在如今跨省务工、异地求职频繁的背景下,很多人都会面临类似困扰。而 淄矿技术学院电工证…

2026/11/9 8:49:20

通城县电工证考试地点内部消息:别再被中介坑,按这个流程走

通城县电工证考试地点内部消息:别再被中介坑,按这个流程走 你是不是也遇到过这种情况:听说要考电工证,但不知道通城县电工证考试地点在哪,也不清楚怎么报名,生怕被中介忽悠,花冤枉钱还拿不到证?这年头,证照成了吃饭的家伙,可要是走错了路,那就麻烦…

2026/11/9 0:37:55

电工证丢失还能补吗?千万别踩坑,丢了证等于失业

电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。…

2026/11/9 0:03:09

建设厅的特种工有哪些含金量高?别交完钱考不过

建设厅的特种工有哪些含金量高?别交完钱考不过 怕考不过白交培训费?别急,先搞清楚建设厅的特种工有哪些含金量高的证,再决定是不是值得投入时间和金钱。 建设厅的特种工证是建筑行业里最基础、也最实用的证书之一,涉及多个作业类别,每种证的 含金量…

2026/11/9 0:03:09

荆门市电工证报名怎么考才靠谱 全国通用证别交冤枉钱

荆门市电工证报名怎么考才靠谱 全国通用证别交冤枉钱 怕考不过白交培训费?荆门市电工证报名前必须知道这些避坑指南。别看网上一堆培训机构广告,真要报个电工证,很多人被坑得一塌糊涂。特别是那些打着“包过”旗号的,最后不是考试不过,就是证不是全国通…

2026/11/9 0:03:09

高空电工作业过程通过率揭秘:怎么选靠谱机构不被坑

高空电工作业过程通过率揭秘:怎么选靠谱机构不被坑 不知道去哪报名怕被中介坑?你不是一个人。现在特种作业证报名渠道五花八门,从线下培训机构到线上代理,真假难辨。尤其是像高空电工作业这类高风险、高门槛的作业类型,证书含金量和考试通过率直接影响你…

2026/11/9 6:30:27

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/7 14:50:12

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑