最近刷题时又碰到 LeetCode 190“颠倒二进制位”这道经典位运算题,第一眼觉得不就是把二进制串反转一下嘛,但真正动手写的时候,才发现里面藏着不少细节。题目要求把一个 32 位无符号整数的二进制位完全倒过来,比如最低位变成最高位、次低位变成次高位,如此类推。这道题非常适合用来检验位运算的基本功,也是面试中频率不低的题。不管你是刚开始刷题的新手,还是想巩固基础准备面试,这篇文章我都建议你认真看完,我会把两种主流解法逐位拆开讲:一种是直观易懂的逐位颠倒法,另一种是效率更高的分治法,每一步都讲清楚“为什么这么做”。
整个过程里你会发现,位运算题目其实套路很固定,核心就是几个运算符的排列组合。只要理解了位级的操作逻辑,以后遇到类似的题基本都能迎刃而解。
1. 题目拆解与整体思路
1.1 题目到底在问什么
题面并不复杂:给定一个 32 位无符号整数 n,返回它的二进制位颠倒后的结果。这里的“颠倒”不是二进制字符串反转那么简单,它在位级上等价于:原数的第 0 位放到结果的第 31 位,原数的第 1 位放到结果的第 30 位,以此类推。
举个例子:输入 43261596,它的二进制表示是 00000010100101000001111010011100,颠倒后变成 00111001011110000010100101000000,这个二进制对应的十进制数就是 964176192。这两个数转来转去,看着挺迷惑,但一旦你把二进制列出来,逻辑就很清楚了:第一个数的最低八位是 10011100,颠倒后变成了结果的最高八位 00111001,注意这里的细节,是原低八位的顺序整个反过来,而不是简单平移。
这里有个容易出现认知偏差的点:LeetCode 题目明确说是“无符号 32 位整数”,但 Java 里的 int 是有符号的,C++ 里的 int 通常也是 32 位有符号。也就是说,你的代码接收到的 n 可能是一个负数,但你依然要把它当成 32 位无符号数来处理。这是后续很多 bug 的根源,也是我重点想让你留意的地方。如果面试官追问“为什么用无符号右移”,你答得上来说明你是真懂而不是背答案。
1.2 位运算基础不太牢固的看这里
既然这道题考查位运算,那基础运算符得先过一遍。按位与 &,同时为 1 才为 1;按位或 |,有一个为 1 就是 1;异或 ^,相同为 0、不同为 1;取反 ~,0 变 1、1 变 0;左移 <<,低位补 0;右移分两种,算术右移 >> 和逻辑右移 >>>,这是 Java 的写法,算术右移用符号位补高位,逻辑右移用 0 补高位。
对于无符号数的位颠倒场景,我们必须使用逻辑右移 >>>,因为如果 n 的最高位是 1,用 >> 会不断在左边补 1,最终导致你取出的位永远是 1,循环就失控了。这一点在解法一里会直接体现,也是新手最容易踩的坑。C++ 里没有 >>> 这种三箭头运算符,如果你用 uint32_t 类型,>> 天然就是逻辑右移,所以 C++ 党反而不太容易踩这个坑,但 Java 党要格外警惕。
1.3 两条路线怎么选
这道题的主流解法大体分两派:逐位颠倒法和分治法。逐位法直观、代码可读性强,思想上就是一个 for 循环把 32 个位全部处理完,适合作为第一时间的解题思路,也方便在面试时边写边讲;分治法则是位运算里的高级玩法,通过掩码和移位一次处理多个位,代码精简、指令数少,但理解门槛更高。
我的建议是:先保证自己能写出逐位法,然后再去啃分治法。面试的时候如果能在两种解法之间切换,甚至主动讲出分治法的掩码推导过程,会非常加分。毕竟面试官见过太多背答案的人了,能现场推演掩码的候选人确实少。接下来我先讲逐位法,因为它最容易迅速 AC,后面再讲分治。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 解法一:逐位颠倒,最稳妥的暴力美学
2.1 核心思路:一位一位搬运
逐位颠倒的思路其实就一句话:把原数的每一位从低位往高位读,同时将结果从高位往低位写。具体操作可以分解成三步:先让结果变量向左移动一位,腾出最低位的位置;再取出当前原数的最低位,也就是 n & 1;把取出的这一位或进结果的最低位,同时原数向右移动一位,处理下一位。
这个流程重复 32 次,就完成了整个 32 位整数的颠倒。为什么是 32 次而不是 while 循环直到 n 变成 0?这个问题我后面专门展开讲,这里先记住,因为题目给的是固定 32 位的数据,前导零也必须参与颠倒。
你想象一下搬砖的场景:有一排 32 块砖,编号从 0 到 31,你要把它们按相反顺序搬到另一排。逐位法就是每次从原砖堆的
