Design Bitset 的 O(1) 翻转实现:codeforces-go 中懒标记 flip 的完整解析

发布时间:2026/10/10 2:17:07
Design Bitset 的 O(1) 翻转实现:codeforces-go 中懒标记 flip 的完整解析 科学计算【免费下载链接】codeforces-go算法竞赛模板库 by 灵茶山艾府 项目地址https://gitcode.com/GitHub_Trending/co/codeforces-go点击查看免费下载导读本篇技术指南以 周赛第 279 场第三题的题解笔记 为核心深入剖析一道经典设计题Design Bitset如何在不真正遍历整个位集的情况下用 O(1) 时间完成整体翻转flip操作。文章会完整继承原笔记中的懒标记思想与七个操作的逻辑并结合仓库中 完整实现 和 测试用例 做源码级讲解。读完本文你将掌握用懒标记延迟整体翻转、以 cnt1 增量维护 1 的个数这一设计范式并能直接复现可提交的 Go 实现。一、问题背景为什么整体翻转不能真的去翻转题目要求设计一个Bitset类支持以下方法方法语义fix(idx)将下标idx处的值置为 1若已经为 1 则忽略unfix(idx)将下标idx处的值置为 0若已经为 0 则忽略flip()翻转整个位集所有 0 变 1、1 变 0all()判断所有位是否都是 1one()判断是否存在至少一个 1count()返回 1 的个数toString()返回当前位集的字符串表示在题目给定的规模约束size与调用次数均可达 10^5 级别下如果flip()真的去遍历每一位取反一次操作就是 O(size)多次调用必然超时。原笔记给出的破局思路非常关键偶数次翻转等于没有翻转奇数次翻转等于翻转一次。也就是说我们不需要记录翻转后的真实位只需要记录总共翻转了奇数次还是偶数次。用一个懒标记flip表示当前是否处于翻转状态把整个数组的翻转推迟到真正需要读数的那一刻再做这就是本题的核心优化。二、数据结构三个字段撑起整个类根据原笔记的设计类中只维护三个量s底层存储的位集字符串或字节数组存的是物理值即未应用懒标记前的原始状态flip懒标记布尔值表示当前是否处于整体翻转状态cnt1逻辑上 1 的个数注意它随懒标记同步维护永远表示对用户可见的 1 的数量。在仓库的 Go 实现 中三者以包级全局变量形式存在var ( s []byte flip bool cnt1 int )构造函数负责初始化s用bytes.Repeat铺满字符0懒标记置falsecnt1清零func Constructor(size int) (_ Bitset) { s bytes.Repeat([]byte{0}, size) flip, cnt1 false, 0 return }之所以把Constructor的返回值命名为_ Bitset空结构体类型是为了与 LeetCode 的类设计题模板保持一致所有状态都放在全局变量里方法接收者只是一个空壳。测试文件开头的t.Log(记得初始化所有全局变量)正是对这一点的重要提醒——因为状态是全局的每个测试用例前都必须通过构造函数重置。三、七个操作的逐一定义原笔记对这七个操作做了精炼定义下面逐一展开并结合源码说明为什么这样写。3.1 fix(idx)把某位置为逻辑 1原笔记条件如果没有发生翻转并且s[idx]0或者发生翻转并且s[idx]1那么翻转s[idx]的值将cnt1加一。对应源码func (Bitset) Fix(i int) { if s[i] 1 flip { s[i] ^ 1 cnt1 } }这里s[i] 1 flip是一个连续比较等价于(s[i] 1) flipGo 中的比较结果为布尔值可与布尔量再比较。把它翻译成逻辑式用户看到的逻辑值 s[i] XOR flip物理值异或懒标记fix希望把逻辑值变为 1所以只有当当前逻辑值已经是 1时才无事可做否则就要把物理位翻转、cnt1加一条件(s[i] 1) flip恰好在逻辑值为 0时成立flip false时要求s[i] 0flip true时要求s[i] 1。满足条件后执行s[i] ^ 1。由于 ASCII 码0 48、1 490^1恰好等于11^1恰好等于0——一行的异或即可完成单字节翻转无需判断分支。3.2 unfix(idx)把某位置为逻辑 0原笔记条件如果没有发生翻转并且s[idx]1或者发生翻转并且s[idx]0那么翻转s[idx]的值将cnt1减一。func (Bitset) Unfix(i int) { if s[i] 0 flip { s[i] ^ 1 cnt1-- } }逻辑完全对称(s[i] 0) flip恰好在逻辑值为 1时成立此时把物理位翻转为 0同时cnt1减一。由于只在逻辑值确实为 1时才减cnt1永远不会出现负数正确性有保证。3.3 flip()只翻转懒标记不动数组原笔记不去翻转整个s仅将懒标记flip取反同时cnt1置为size-cnt1。func (Bitset) Flip() { flip !flip cnt1 len(s) - cnt1 }这是全篇最精妙的一行级优化整体翻转后所有 0 变 1、1 变 0因此1 的个数直接由size - cnt1得到O(1) 完成。数组s一个字节都不用动翻转被挂账在懒标记上。3.4 all / one / count纯 O(1) 查询func (Bitset) All() bool { return cnt1 len(s) } func (Bitset) One() bool { return cnt1 0 } func (Bitset) Count() int { return cnt1 }三个查询全部直接读cnt1不触碰底层数组all()cnt1等于总位数即全为 1one()cnt1大于 0 即存在 1count()直接返回cnt1。3.5 toString()唯一需要兑现懒标记的操作原笔记如果没有翻转则直接返回s否则翻转s的每一位并返回。func (Bitset) ToString() string { if flip { t : make([]byte, len(s)) for i, ch : range s { t[i] ch ^ 1 } return string(t) } return string(s) }这是所有操作中唯一需要 O(size) 的操作当懒标记为真时才真正复制数组、逐位异或1生成逻辑视图并返回。其余任何时刻数组都保持脏状态不被触碰这正是懒的含义。3.6 操作复杂度汇总操作时间复杂度空间复杂度ConstructorO(size)O(size)fix/unfixO(1)O(1)flipO(1)O(1)all/one/countO(1)O(1)toStringO(size)O(size)临时数组四、完整实现与官方样例走查把上述片段拼起来就是 c.go 的完整可运行实现package main import bytes // 懒标记法 // github.com/EndlessCheng/codeforces-go type Bitset struct{} var ( s []byte flip bool cnt1 int ) func Constructor(size int) (_ Bitset) { s bytes.Repeat([]byte{0}, size) flip, cnt1 false, 0 return } func (Bitset) Fix(i int) { if s[i] 1 flip { s[i] ^ 1 cnt1 } } func (Bitset) Unfix(i int) { if s[i] 0 flip { s[i] ^ 1 cnt1-- } } func (Bitset) Flip() { flip !flip cnt1 len(s) - cnt1 } func (Bitset) All() bool { return cnt1 len(s) } func (Bitset) One() bool { return cnt1 0 } func (Bitset) Count() int { return cnt1 } func (Bitset) ToString() string { if flip { t : make([]byte, len(s)) for i, ch : range s { t[i] ch ^ 1 } return string(t) } return string(s) }下面用手工推演验证 测试用例 中给出的官方样例观察懒标记如何与cnt1协同工作操作序列: [Bitset,fix,fix,flip,all,unfix,flip,one,unfix,count,toString] 参数: [[5],[3],[1],[],[],[0],[],[],[0],[],[]]步骤操作s物理值flipcnt1逻辑视图返回值1Bitset(5)00000false000000null2fix(3)00010false100010null3fix(1)01010false201010null4flip()01010true5-2310101null5all()—true3—false6unfix(0)11010true200101null7flip()11010false5-2311010null8one()—false3—true9unfix(0)01010false201010null10count()—false2—211toString()—false20101001010完整输出为[null, null, null, null, false, null, null, true, null, 2, 01010]与测试文件中的期望值完全一致。注意第 6 步此时懒标记为真unfix(0)希望把逻辑位 0 置为 0逻辑值本就是 0但条件判断的是(s[0]0) flip即true true说明逻辑值此刻为 1因此要翻转物理位使其逻辑上归 0——推演结果也证实s[0]从0翻成了1。五、测试驱动反射执行的类设计题验证本仓库对 LeetCode 类设计题有一套通用的反射测试框架见 leetcode/testutil/leetcode.go 中的RunLeetCodeClassWithExamples。其工作流程是解析第一维字符串数组得到方法名如fix并调用strings.Title转为大写以匹配 Go 方法名Fix用反射调用构造函数拿到类实例逐个方法名通过pObj.MethodByName(name)取出方法解析参数后用method.Call(in)反射调用把实际输出拼接成[null, ...]数组与期望输出比对并支持targetCaseNum精确指定某个用例调试-1表示最后一个。因此 c_test.go 只需要声明样例数据即可运行func Test_c(t *testing.T) { t.Log(记得初始化所有全局变量) examples : [][3]string{ { [Bitset, fix, fix, flip, all, unfix, flip, one, unfix, count, toString], [[5], [3], [1], [], [], [0], [], [], [0], [], []], [null, null, null, null, false, null, null, true, null, 2, 01010], }, } targetCaseNum : 0 // -1 if err : testutil.RunLeetCodeClassWithExamples(t, Constructor, examples, targetCaseNum); err ! nil { t.Fatal(err) } }在仓库根目录执行以下命令即可验证实现go test ./leetcode/weekly/279/c/ -v该测试文件由 copypasta/template/leetcode/generator.go 自动生成文件头部的// Code generated by注释即标记了这一点。仓库内所有周赛/双周赛题目的测试文件都遵循同一套约定格式完全统一。六、从逐字节位集到懒标记位集与 copypasta 位集库的对照理解本题实现后不妨把它与仓库的通用位集模板 copypasta/bitset.go 对照能更清晰地认识两种位集的定位差异通用位集库底层是type Bitset []uint按机器字分块每块bits.UintSize位提供Has/Set/Reset/Flip(p)单点操作、SetAll1批量置 1、Foreach遍历所有 1、Index0找第一个 0 等能力服务于 Codeforces/AtCoder 场景的位运算加速如传递闭包、子集枚举本题实现底层是[]byte每字节存一个0/1字符核心诉求是支持整体翻转这一高频操作因此引入了懒标记flip与计数cnt1把除toString外的所有操作压到 O(1)。前者用^操作单个字来模拟任意位翻转后者用全局懒标记来摊销整体翻转——两者针对的题目形态不同但都体现了位集 按位异或这一底层思想字符0/1或比特位与1异或即可完成翻转无需条件分支。此外copypasta/bitset.go 的注释中还提示若题目要求fix/unfix大量作用于区间可考虑用 0-1 线段树 替代这也是翻转类区间操作问题的一个常见进阶方向。七、总结懒标记思想的通用性本题的flip懒标记本质上与线段树/树状数组中的区间翻转惰性标记同源先记账、后兑现。在本仓库中这种思想还被广泛应用于区间反转、矩形覆盖等题目如 copypasta/segment_tree.go 中各类带懒标记的线段树。对本实现而言记忆要点只有三条逻辑值 物理值 s[i] XOR 懒标记 flipflip()只翻转标记、用size - cnt1更新计数是 O(1) 的单点fix/unfix的判定条件本质上是当前逻辑值是否需要改变只需一次连续比较 一次异或。凭借这三点你可以在任何要求设计支持整体翻转的位集/布尔数组的题目中直接套用达到所有核心操作 O(1)、唯一 O(size) 的toString惰性兑现的最优复杂度。赞分享科学计算【免费下载链接】codeforces-go算法竞赛模板库 by 灵茶山艾府 项目地址https://gitcode.com/GitHub_Trending/co/codeforces-go点击查看免费下载相关推荐codeforces-go 算法笔记Design Task Manager 与懒删除堆Lazy Deletion Heap实战详解codeforces go 算法笔记Design Task Manager 与懒删除堆Lazy Deletion Heap实战详解 本文以本项目 leet科学计算Moonlight主题入门指南如何快速安装和配置这款月光VS Code主题Moonlight主题入门指南如何快速安装和配置这款月光VS Code主题 Moonlight主题是一款以月光为背景、带有泡泡糖色彩的VS Code主题能为LeetCode-Go 189. Rotate Array原地 O(1) 空间的数组旋转实现与三次翻转法详解LeetCode Go 189. Rotate Array原地 O 1 空间的数组旋转实现与三次翻转法详解 本文以 LeetCode Go 仓库中 189.示例工程上一篇MTKClient刷机工具完整指南从零开始掌握联发科设备刷机下一篇PaddleHub Module 快速体验用预训练模型一行代码完成图像分割、人脸检测与中文 NLP 任务创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询