LeetCode 190.颠倒二进制位
方法一:逐位颠倒
思路
将 n 视作一个长为 32 的二进制串,从低位往高位枚举 n 的每一位,将其倒序添加到翻转结果 rev 中。
代码实现中,每枚举一位就将 n 右移一位,这样当前 n 的最低位就是我们要枚举的比特位。当 n 为 0 时即可结束循环。
需要注意的是,在某些语言(如 Java)中,没有无符号整数类型,因此对 n 的右移操作应使用逻辑右移。
代码
cpp
class Solution {
public:
uint32_t reverseBits(uint32_t n) {
uint32_t rev = 0; // 初始化一个变量 rev,用于存储反转后的结果,初始值为0
for (int i = 0; i < 32 && n > 0; ++i) { // 循环32次,但若 n 变为0则可提前退出(优化)
rev |= (n & 1) << (31 - i); // 取出 n 的最低位,将它左移到对应的反转位置,然后通过按位或合并到 rev 中
n >>= 1; // 将 n 右移一位,丢弃已处理的最低位,准备处理下一位
}
return rev; // 返回反转后的结果
}
};
代码2
cpp
class Solution {
public:
uint32_t reverseBits(uint32_t n) {
uint32_t ans = 0; // 初始化结果变量 ans 为 0,用于逐步存储反转后的值
int i = 32; // 设置循环计数器 i 为 32,表示要处理 32 位
while(i--) // 循环 32 次,每次 i 减 1,直到 i 变为 0(后置递减,先判断 i 是否为非零,再减)
{
ans <<= 1; // 将 ans 左移 1 位,为即将加入的新位腾出最低位的位置
ans += n & 1; // 取出 n 的最低位(n & 1),加到 ans 的最低位(此时 ans 最低位为 0,等效于设置该位)
n >>= 1; // 将 n 右移 1 位,丢弃已处理的最低位,使下一低位成为新的最低位
}
return ans; // 返回反转后的结果
}
};
方法二:位运算分治
位运算分治(也称为并行位反转或分治位交换)是一种利用位运算掩码和移位操作,通过将数据划分为更小的块并并行交换相邻块的位置,从而高效地完成位序反转、位计数等任务的技术。其核心思想是分而治之:先将问题分解为若干独立的小规模子问题(如交换相邻的1位),然后逐层扩大交换的规模(2位、4位、8位等),最终得到整体结果。整个过程不需要循环,时间复杂度为 O(log n) 次操作(对于32位整数只需5步),且所有操作都是按位并行执行,效率极高。
以下代码运用了这种分治原理来实现32位无符号整数的二进制位反转:
cpp
class Solution {
private:
const uint32_t M1 = 0x55555555; // 0101... 用于交换相邻1位
const uint32_t M2 = 0x33333333; // 0011... 用于交换相邻2位
const uint32_t M4 = 0x0f0f0f0f; // 00001111... 用于交换相邻4位
const uint32_t M8 = 0x00ff00ff; // 0000000011111111... 用于交换相邻8位
public:
uint32_t reverseBits(uint32_t n) {
// 第1步:交换相邻的1位(即每2位为一组,将组内高低位互换)
n = n >> 1 & M1 | (n & M1) << 1;
// 第2步:交换相邻的2位(即每4位为一组,将前后两个2位互换)
n = n >> 2 & M2 | (n & M2) << 2;
// 第3步:交换相邻的4位(即每8位为一组,将前后两个4位互换)
n = n >> 4 & M4 | (n & M4) << 4;
// 第4步:交换相邻的8位(即每16位为一组,将前后两个8位互换)
n = n >> 8 & M8 | (n & M8) << 8;
// 第5步:交换高16位和低16位
return n >> 16 | n << 16;
}
};
原理详解
掩码的作用:每个掩码的二进制模式是交替的"0"和"1"块,用于提取偶数位置或奇数位置的位组。例如 M1 = 0x55555555 的二进制为 0101...,它保留了所有偶数位(第0、2、4...位),而将奇数位清零。
每一步的操作:
(n & M1) << 1:提取偶数位并左移1位,使它们移到奇数位的位置。
n >> 1 & M1:将原数右移1位,提取原来的奇数位(现在位于偶数位),然后与M1相与得到这些位。
两者按位或,即完成相邻位的交换。
后续步骤类似,只是掩码和移动的位数逐步加倍。经过五次交换,最终实现了整个32位数的完全反转。
这种分治算法利用了位运算的并行性,无需循环,执行速度极快,是处理位反转问题的经典优化方法。