ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

LeetCode 0190 Reverse Bits 题解:Go 逐位反转 32 位无符号整数

LeetCode 0190 Reverse Bits 题解:Go 逐位反转 32 位无符号整数 LeetCode 0190 Reverse Bits 题解Go 逐位反转 32 位无符号整数【免费下载链接】LeetCode-Go✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go本篇文章基于 LeetCode-Go 仓库中 0190.Reverse-Bits 题解文档完整解析 LeetCode 第 190 题「颠倒二进制位Reverse Bits」的题意、位运算原理与 Go 实现。文章会逐行拆解仓库内的位运算解法源码并结合测试用例与调试输出帮助你彻底掌握「逐位提取 左移拼接」这一反转二进制位的经典套路。题目回顾颠倒 32 位无符号整数的二进制位题目要求非常简单给定一个 32 位无符号整数32 bits unsigned integer将其二进制表示完全倒序后返回。也就是说原数的最低位第 0 位成为结果数的最高位第 31 位原数的最高位成为结果数的最低位其余位以此类推。示例一低位与高位整体互换输入00000010100101000001111010011100输出00111001011110000010100101000000其中输入二进制串对应的无符号整数为43261596反转后得到964176192其二进制表示正是输出的那串00111001011110000010100101000000。可以直观地看到两串二进制位完全互为镜像。示例二全 1 前缀被反转后移到低位输入11111111111111111111111111111101输出10111111111111111111111111111111输入二进制串代表无符号整数4294967293反转后返回3221225471。注意输出串最右侧是01这正是输入串最左侧的11反转后拼接的结果。关于有符号/无符号的说明原文档特别提醒在 Java 等语言中不存在无符号整数类型输入与输出都会被声明为有符号整数类型但这不影响实现正确性——因为整数在内存中的底层二进制表示与它有符号还是无符号无关Java 编译器采用[二进制补码]表示有符号整数因此在示例二中输入二进制串11111111111111111111111111111101若以有符号视角解读为-3则输出串10111111111111111111111111111111对应有符号整数-1073741825。而在 Go 中原生提供了uint32无符号 32 位整数类型可以直接用其精确承载 32 位二进制位无需担心符号扩展问题这让 Go 成为实现本题最自然的语言之一。解题思路右移提取最低位左移拼接结果反转二进制位的核心思路可以概括为一句话每次取原数最低位的比特把它拼到结果的最低位然后原数右移、结果左移。重复 32 次后原数的每一位都恰好移动到了镜像对称的位置上。原文档给出的思路如下把num往右移动不断地消除右边最低位的 1将这个最低位取出并交给resres不断左移即可实现反转二进制位的目的。用更精确的语言描述单次迭代包含三步取位num 1提取num当前最低位0 或 1拼接res res1 | (num 1)将res左移一位腾出最低位再用按位或把刚取出的比特拼接上去收缩num 1丢弃已经处理过的最低位让下一位变成新的最低位。循环 32 次后res中保存的二进制位正好是num的镜像反转。为什么只循环 32 次因为题目明确规定输入是 32 位无符号整数。固定循环 32 次可以保证高位原本为 0 的位也会被完整搬运到低位不会因为前导零而丢失无论num的实际有效位数是多少结果始终是完整的 32 位反转结果。如果循环次数不足 32反转结果会缺少高位的 0直接导致答案错误这是本题最容易踩的坑之一。仓库源码逐行解析LeetCode-Go 仓库在 leetcode/0190.Reverse-Bits/190. Reverse Bits.go 中给出了完整实现代码与题解文档完全一致package leetcode func reverseBits(num uint32) uint32 { var res uint32 for i : 0; i 32; i { res res1 | num1 num 1 } return res }逐行拆解行代码作用var res uint32声明结果变量初始值为 0作为拼接反转结果的累加器for i : 0; i 32; i固定循环 32 次逐位处理 32 位二进制res res1 \| num1结果左移一位后拼入num最低位单步核心取位 拼接num 1num右移一位消除已处理的最低位return res返回 32 位反转结果结果类型仍为uint32算例推演以 43261596 为例假设num 43261596二进制为00000010100101000001111010011100第 1 次迭代num 1取出最低位0res从0左移一位仍为0拼入0后res 0然后num右移一位第 2 次迭代num 1取出新的最低位0res左移一位后拼入0res仍为0第 3 次迭代num 1取出1res左移一位得0按位或1后res 1二进制...01依此类推每轮res向左让出一个空位把num从低位到高位吐出的比特依次填进去。经过 32 轮后num的全部比特被倒序写入res得到00111001011110000010100101000000即964176192与题目示例一完全吻合。边界情况全 1 输入的符号视角当输入为4294967293二进制11111111111111111111111111111101时该数值大于2^31-1若按有符号int32解读即为-3但作为uint32参与位运算时底层比特串不变逐位反转后得到10111111111111111111111111111111即3221225471若按有符号视角解读输出为-1073741825。这正对应原文档关于二进制补码记法的说明比特串相同解读视角不同而已位运算过程本身不受影响。测试验证仓库自带测试用例与运行方式LeetCode-Go 仓库为每题都配套了测试文件第 190 题的测试位于 leetcode/0190.Reverse-Bits/190. Reverse Bits_test.go覆盖了题目给出的两组官方示例qs : []question190{ { para190{43261596}, ans190{964176192}, }, { para190{4294967293}, ans190{3221225471}, }, }测试代码中值得注意的细节是它先用strconv.FormatUint(uint64(p.one), 2)把输入整数转成二进制字符串再用fmt.Sprintf(%0*v, 32, input)补足 32 位、保留前导 0最后调用reverseBits(p.one)并打印输入与输出的二进制对照方便人工核对反转结果的每一位input : strconv.FormatUint(uint64(p.one), 2) // 32位无符号整数转换为二进制字符串 input fmt.Sprintf(%0*v, 32, input) // 格式化输出32位,保留前置0 output : reverseBits(p.one) outputBin : strconv.FormatUint(uint64(output), 2) outputBin fmt.Sprintf(%0*v, 32, outputBin) fmt.Printf(【input】:%v 【output】:%v (%v)\n, input, output, outputBin)这种「把整数格式化为定宽二进制串再打印」的调试手法同样值得借鉴在位运算类题目中肉眼比对二进制串往往比比对十进制数值更容易定位错误。如何在本地运行测试仓库根目录的 gotest.sh 定义了全量测试命令单个题目也可以按包粒度运行。针对本题可以进入对应目录执行go test -v ./leetcode/0190.Reverse-Bits/也可以借助仓库的-cover机制单独查看本题的覆盖率go test -covermodeatomic -coverprofilecoverage.txt ./leetcode/0190.Reverse-Bits/本题实现没有分支语句循环体内每行都被测试用例覆盖从实现角度看天然满足仓库「100% test coverage」的工程约束。延伸思考其他反转方案与对比仓库选用的是最直观、最易读的逐位迭代方案时间复杂度O(1)固定 32 次循环空间复杂度O(1)。在此基础上还可以延伸出两种思路方案一掩码分治Divide and Conquer先交换相邻位、再交换相邻两位组、四位组……逐级扩大交换粒度func reverseBits(num uint32) uint32 { num num16 | num16 num (num0xff00ff00)8 | (num0x00ff00ff)8 num (num0xf0f0f0f0)4 | (num0x0f0f0f0f)4 num (num0xcccccccc)2 | (num0x33333333)2 num (num0xaaaaaaaa)1 | (num0x55555555)1 return num }该方案通过常数级掩码运算一次完成反转同样只有O(1)的时间复杂度但常数更小适合对性能有极致要求的场景。方案二内置函数Go 标准库math/bits提供了bits.Reverse32可一行完成相同功能import math/bits func reverseBits(num uint32) uint32 { return bits.Reverse32(num) }从仓库实现角度看手写循环版的优势在于不依赖标准库、逻辑透明、可读性强这也是 LeetCode-Go 仓库选择手写实现作为标准答案的原因面试时手写逐位循环版也是最稳妥、最不容易出错的呈现方式。小结本题表面上是简单题实则考察了三个基础但重要的点位运算基本功取位、左移腾位、|拼位、消除已处理位的组合运用固定长度循环32 位整数必须完整循环 32 次否则前导零会丢失有符号/无符号视角同一比特串在不同类型解读下数值不同但位运算结果一致理解了这一点就能从容应对 Java 等语言的有符号陷阱。仓库提供的实现与测试用例可以在 leetcode/0190.Reverse-Bits/190. Reverse Bits.go 和 leetcode/0190.Reverse-Bits/190. Reverse Bits_test.go 中直接查看配合题解文档 website/content.en/ChapterFour/0100~0199/0190.Reverse-Bits.md 一起阅读可以形成「题意 → 思路 → 代码 → 测试」的完整学习闭环。【免费下载链接】LeetCode-Go✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表