颠倒二进制位

📌 定义

颠倒给定的 32 位无符号整数的二进制位。

示例:
输入: 00000010100101000001111010011100
输出: 00111001011110000010100101000000
解释: 将二进制位完全颠倒

输入: 11111111111111111111111111111101
输出: 10111111111111111111111111111111

核心思路

三种主要方法:

  1. 逐位颠倒:从右往左逐位取出,从左往右放入
  2. 分治法:递归地交换相邻的位、字节等
  3. 查表法:预计算所有可能的字节反转结果
逐位颠倒示例:
原数字: 1011 (11)

步骤1: result = 0000, 取出最右位1
       result = 0001, n右移 → 101

步骤2: result = 0010, 取出最右位1
       result = 0011, n右移 → 10

步骤3: result = 0110, 取出最右位0
       result = 0110, n右移 → 1

步骤4: result = 1100, 取出最右位1
       result = 1101, n右移 → 0

结果: 1101 (13)

复杂度分析

方法时间复杂度空间复杂度说明
逐位颠倒O(log n)O(1)32次循环
分治法O(1)O(1)固定次数操作
查表法O(1)O(256)空间换时间

Go 代码

Go 实现

package main
 
import "fmt"
 
// 方法1: 逐位颠倒
func reverseBits(n uint32) uint32 {
    var result uint32 = 0
 
    for i := 0; i < 32; i++ {
        result <<= 1
        result |= n & 1
        n >>= 1
    }
 
    return result
}
 
// 方法2: 分治法
func reverseBitsDivideConquer(n uint32) uint32 {
    // 交换相邻的1位
    n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1)
 
    // 交换相邻的2位
    n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2)
 
    // 交换相邻的4位
    n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4)
 
    // 交换相邻的8位
    n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8)
 
    // 交换相邻的16位
    n = (n >> 16) | (n << 16)
 
    return n
}
 
// 方法3: 查表法
var reverseByteTable [256]uint8
 
func init() {
    for i := 0; i < 256; i++ {
        reverseByteTable[i] = reverseByte(uint8(i))
    }
}
 
func reverseBitsLookup(n uint32) uint32 {
    return (uint32(reverseByteTable[n&0xFF]) << 24) |
        (uint32(reverseByteTable[(n>>8)&0xFF]) << 16) |
        (uint32(reverseByteTable[(n>>16)&0xFF]) << 8) |
        (uint32(reverseByteTable[(n>>24)&0xFF]))
}
 
func reverseByte(b uint8) uint8 {
    var result uint8 = 0
    for i := 0; i < 8; i++ {
        result = (result << 1) | (b & 1)
        b >>= 1
    }
    return result
}
 
func main() {
    n := uint32(0b00000010100101000001111010011100)
    result := reverseBits(n)
 
    fmt.Printf("输入: %032b\n", n)
    fmt.Printf("输出: %032b\n", result)
}

思路展开

方法1详解:逐位颠倒

示例: n = 1011 (4位演示)

初始: result = 0000, n = 1011

循环1:
  result <<= 1  → 0000
  result |= 1   → 0001  (取n的最右位)
  n >>= 1       → 0101

循环2:
  result <<= 1  → 0010
  result |= 1   → 0011
  n >>= 1       → 0010

循环3:
  result <<= 1  → 0110
  result |= 0   → 0110
  n >>= 1       → 0001

循环4:
  result <<= 1  → 1100
  result |= 1   → 1101
  n >>= 1       → 0000

最终: result = 1101

方法2详解:分治法

示例: n = 10110011 (8位演示)

步骤1: 交换相邻1位
原数: 10 11 00 11
掩码: 10101010 (0xAA) 和 01010101 (0x55)
操作: ((10110011 & 10101010) >> 1) | ((10110011 & 01010101) << 1)
     = (10100010 >> 1) | (00010001 << 1)
     = 01010001 | 00100010
     = 01110011

步骤2: 交换相邻2位
原数: 0111 0011
掩码: 11001100 (0xCC) 和 00110011 (0x33)
操作: ((01110011 & 11001100) >> 2) | ((01110011 & 00110011) << 2)
     = (01000000 >> 2) | (00110011 << 2)
     = 00010000 | 11001100
     = 11011100

步骤3: 交换相邻4位
原数: 1101 1100
掩码: 11110000 (0xF0) 和 00001111 (0x0F)
操作: ((11011100 & 11110000) >> 4) | ((11011100 & 00001111) << 4)
     = (11010000 >> 4) | (00001100 << 4)
     = 00001101 | 11000000
     = 11001101

结果: 11001101 (原数10110011的反转)

查表法详解

预计算0-255的反转:
0   (00000000) → 0   (00000000)
1   (00000001) → 128 (10000000)
2   (00000010) → 64  (01000000)
3   (00000011) → 192 (11000000)
...
255 (11111111) → 255 (11111111)

32位拆分成4个字节:
n = ABCD (每个字母代表一个字节)

反转:
reverse(n) = reverse(D) << 24 |
             reverse(C) << 16 |
             reverse(B) << 8  |
             reverse(A)

示例:
n = 0x12345678
  = 0001 0010 | 0011 0100 | 0101 0110 | 0111 1000

reverse(0x12) << 24 = 0x48000000
reverse(0x34) << 16 = 0x002C0000
reverse(0x56) << 8  = 0x00006A00
reverse(0x78)       = 0x0000001E

result = 0x482C6A1E

经典题目

LeetCode 问题

  • 颠倒二进制位 - LeetCode 190
  • 颠倒整数 - LeetCode 7(十进制反转)
  • 回文数 - LeetCode 9(判断回文)

扩展问题

  • 反转字符串
  • 反转链表

⚖️ 优缺点

优点

  • ✅ 逐位法简单直观:易于理解和实现
  • ✅ 分治法效率高:固定次数操作
  • ✅ 查表法最快:适合大量调用
  • ✅ 无需额外空间:原地操作

缺点

  • ❌ 分治法不直观:需要理解掩码
  • ❌ 查表法占空间:需要256字节表

🎨 应用场景

💡 优化技巧

相关主题


返回:位运算 | 算法学习导航