1. 题目描述
2. 解题思路
题目明确这是二进制位的运算,那么我们就得从常数转换为二进制入手。最常规的方法,把常数除二取余然后直接乘对应的位权,完事了。对于C语言来说,确实是这样子的,因为在C语言当中有无符号常数uint32
,但在Java语言当中就没那么顺利了。C语言几行代码可以搞定的逻辑,在Java中则需要分情况讨论,因为32位的二进制数,若最高位为1时表示的是负数(这里涉及原码、反码和补码的知识),但在本题的含义中却是不能够看成负数的,因此需要对这个假负数进行转换,把它的最高位置0(颠倒前的最高位相当于颠倒后的最低位,因此颠倒后要加1)后才可以与正数一样进行相关运算。
上述的方法时间性能到了100%,我以为这已经是属于好的方案了,然而官解中全都是一些位运算,实属让人恶心。但也没办法,还是得啃一啃,其实就是把十进制转二进制过程中的除二取余法替换成了直接的位移运算,可以从前往后进行或从后往前进行。
官解中还有一种分治的写法,但个人认为可读性极差,这里不作相关解读了。
3. 代码实现
3.1 数值运算(C语言版)
uint32_t reverseBits(uint32_t n) {
uint32_t sum = 0;
for (int i = 0; i < 32; i++) {
sum *= 2;
sum += n % 2;
n /= 2;
}
return sum;
}
3.2 数值运算
public int reverseBits(int n) {
if (n < 0) {
n = n << 1;
n = n >>> 1;
return compute(n) + 1;
}
return compute(n);
}
public int compute(int n) {
int sum = 0;
for (int i = 0; i < 32; i++) {
sum *= 2;
sum += n % 2;
n /= 2;
}
return sum;
}
这个代码相比于C语言实在太臃肿了,没办法,谁叫咋们Java没有uint32
呢!
3.3 二进制运算逐位逆转
public int reverseBits(int n) {
int sum = 0;
for (int i = 0; i < 32; i++) {
// 从前往后
sum += (n << i >>> (31 - i)) & 1 << i;
// 从后往前
// sum += n & 1 << 31 - i;
// n = n >>> 1;
}
return sum;
}
从前往后:
从后往前:
3.3 对比
其中最终的结果对比也是让我大吃一惊的,用位运算的解法时间消耗居然比数值运算还要高,不过空间消耗倒是优化了挺多。两种算法都是常数级的时间复杂度O(1),而空间复杂度也同样是O(1)。