颠倒给定的 32 位无符号整数的二进制位。
提示:
-3
,输出表示有符号整数 -1073741825
。简单
点击在LeetCode中查看题目
输入: n = 00000010100101000001111010011100
输出: 00111001011110000010100101000000
解释: 输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596,
因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000。
输入:n = 11111111111111111111111111111101
输出:10111111111111111111111111111111
解释:输入的二进制串 11111111111111111111111111111101 表示无符号整数 4294967293,
因此返回 3221225471,其二进制表示为 10111111111111111111111111111111。
如果多次调用这个函数,你将如何优化你的算法?
最直接的方法是逐位处理,从右到左依次取出原始数字的每一位,然后从左到右构建结果数字。
关键点:
时间复杂度:O(1),因为我们处理的是32位整数,循环次数是固定的
空间复杂度:O(1),只需要常数额外空间
我们可以使用分治法和位运算来优化算法。通过交换不同位置的位,逐步将整个二进制串颠倒。
关键点:
时间复杂度:O(1),因为操作次数是固定的
空间复杂度:O(1),只需要常数额外空间
如果需要多次调用这个函数,可以使用查表法优化。我们可以预先计算所有8位二进制数字的颠倒结果,然后在运行时直接查表。
关键点:
时间复杂度:O(1),只需要常数次操作
空间复杂度:O(1),预计算表的大小是固定的(256个条目)
public class Solution {
public uint reverseBits(uint n) {
uint result = 0;
// 遍历32位
for (int i = 0; i < 32; i++) {
// 将结果左移一位,为新的位腾出空间
result <<= 1;
// 如果n的最低位是1,则将结果的最低位设置为1
if ((n & 1) == 1) {
result |= 1;
}
// 将n右移一位,处理下一位
n >>= 1;
}
return result;
}
}
public class Solution {
public uint reverseBits(uint n) {
n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1);
n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2);
n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4);
n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8);
n = (n >> 16) | (n << 16);
return n;
}
}
public class Solution {
// 预计算表
private readonly uint[] reversedByte = new uint[256];
public Solution() {
// 初始化表
for (int i = 0; i < 256; i++) {
uint reversed = 0;
for (int j = 0; j < 8; j++) {
reversed <<= 1;
reversed |= (uint)(i >> j) & 1;
}
reversedByte[i] = reversed;
}
}
public uint reverseBits(uint n) {
uint result = 0;
// 处理四个字节
result |= reversedByte[n & 0xFF] << 24;
result |= reversedByte[(n >> 8) & 0xFF] << 16;
result |= reversedByte[(n >> 16) & 0xFF] << 8;
result |= reversedByte[(n >> 24) & 0xFF];
return result;
}
}
class Solution:
def reverseBits(self, n: int) -> int:
result = 0
# 遍历32位
for i in range(32):
# 将结果左移一位,为新的位腾出空间
result <<= 1
# 如果n的最低位是1,则将结果的最低位设置为1
if n & 1:
result |= 1
# 将n右移一位,处理下一位
n >>= 1
return result
class Solution:
def reverseBits(self, n: int) -> int:
n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1)
n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2)
n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4)
n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8)
n = (n >> 16) | (n << 16)
# 确保结果是32位无符号整数
return n & 0xFFFFFFFF
class Solution:
def __init__(self):
# 初始化表
self.reversed_byte = [0] * 256
for i in range(256):
self.reversed_byte[i] = self._reverse_byte(i)
def _reverse_byte(self, byte):
reversed_byte = 0
for i in range(8):
reversed_byte <<= 1
reversed_byte |= (byte >> i) & 1
return reversed_byte
def reverseBits(self, n: int) -> int:
result = 0
# 处理四个字节
result |= self.reversed_byte[n & 0xFF] << 24
result |= self.reversed_byte[(n >> 8) & 0xFF] << 16
result |= self.reversed_byte[(n >> 16) & 0xFF] << 8
result |= self.reversed_byte[(n >> 24) & 0xFF]
return result
class Solution {
public:
uint32_t reverseBits(uint32_t n) {
uint32_t result = 0;
// 遍历32位
for (int i = 0; i < 32; i++) {
// 将结果左移一位,为新的位腾出空间
result <<= 1;
// 如果n的最低位是1,则将结果的最低位设置为1
if (n & 1) {
result |= 1;
}
// 将n右移一位,处理下一位
n >>= 1;
}
return result;
}
};
class Solution {
public:
uint32_t reverseBits(uint32_t n) {
n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1);
n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2);
n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4);
n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8);
n = (n >> 16) | (n << 16);
return n;
}
};
class Solution {
private:
// 预计算表
uint32_t reversedByte[256];
// 初始化表
void initTable() {
for (int i = 0; i < 256; i++) {
uint32_t reversed = 0;
for (int j = 0; j < 8; j++) {
reversed <<= 1;
reversed |= (i >> j) & 1;
}
reversedByte[i] = reversed;
}
}
public:
Solution() {
initTable();
}
uint32_t reverseBits(uint32_t n) {
uint32_t result = 0;
// 处理四个字节
result |= reversedByte[n & 0xFF] << 24;
result |= reversedByte[(n >> 8) & 0xFF] << 16;
result |= reversedByte[(n >> 16) & 0xFF] << 8;
result |= reversedByte[(n >> 24) & 0xFF];
return result;
}
};
各语言实现的性能对比:
实现语言 | 方法 | 执行用时 | 内存消耗 | 特点 |
---|---|---|---|---|
C# | 方法一 | 28 ms | 24.3 MB | 简单直观,无需预处理 |
C# | 方法二 | 24 ms | 24.5 MB | 位运算优化,性能优秀 |
C# | 方法三 | 24 ms | 24.7 MB | 适合多次调用的场景 |
Python | 方法一 | 36 ms | 14.9 MB | 直观易懂 |
Python | 方法二 | 32 ms | 14.8 MB | 性能较好 |
Python | 方法三 | 28 ms | 15.1 MB | 查表优化,额外内存消耗 |
C++ | 方法一 | 4 ms | 5.9 MB | 基础实现 |
C++ | 方法二 | 0 ms | 5.8 MB | 性能最优 |
C++ | 方法三 | 0 ms | 6.2 MB | 预计算表占用额外空间 |
分治法中使用的掩码和移位操作可能看起来有些复杂,这里详细解释一下:
0xAAAAAAAA
表示二进制中所有偶数位为1(从0开始计数),0x55555555
表示所有奇数位为10xCCCCCCCC
表示每4位中高2位为1,0x33333333
表示每4位中低2位为10xF0F0F0F0
表示每8位中高4位为1,0x0F0F0F0F
表示每8位中低4位为10xFF00FF00
表示每16位中高8位为1,0x00FF00FF
表示每16位中低8位为1通过这种方式,我们可以在不使用循环的情况下,利用分治思想高效地完成位颠倒操作。