LeetCode 190.颠倒二进制位

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位数的完全反转。

这种分治算法利用了位运算的并行性,无需循环,执行速度极快,是处理位反转问题的经典优化方法。

相关推荐
骇城迷影2 小时前
代码随想录:链表篇
数据结构·算法·链表
闻哥2 小时前
Redis事务详解
java·数据库·spring boot·redis·缓存·面试
hrhcode2 小时前
【Netty】五.ByteBuf内存管理深度剖析
java·后端·spring·springboot·netty
道亦无名2 小时前
aiPbMgrSendAck
java·网络·数据库
专注前端30年3 小时前
智能物流路径规划系统:核心算法实战详解
算法
发现你走远了3 小时前
Windows 下手动安装java JDK 21 并配置环境变量(详细记录)
java·开发语言·windows
心 -3 小时前
java八股文DI
java
json{shen:"jing"}3 小时前
字符串中的第一个唯一字符
算法·leetcode·职场和发展
黎雁·泠崖3 小时前
Java常用类核心详解(一):Math 类超细讲解
java·开发语言