无符号整数翻转函数实现reverse_bits(unsigned int value)
题目:
Reverse Bits
Reverse bits of a given 32 bits unsigned integer.
For example, given input 43261596 (represented in binary as 00000010100101000001111010011100), return 964176192 (represented in binary as 00111001011110000010100101000000).
Follow up:
If this function is called many times, how would you optimize it?
PS:leetcode
一般的解决方案就是从左至右判断每一位,把获得的结果加在结果上,编码如下,
class Solution { public: uint32_t reverseBits(uint32_t n) { uint32_t bit = 0; uint32_t result = 0; uint32_t tmp = n; while(bit<32) { if((tmp>>bit) & 1 == 1) result = result + (1<<(31-bit)); bit ++; } return result; } };
这种方式简单易懂,但是效率不高,时间复杂度为O(n),n为数据类型的位数。
咱们换个角度想想,既然我们给定的数可以通过移动获得每一位的数字,那么我们也可以移动结果来填充每一步的值。
class Solution { public: uint32_t reverseBits(uint32_t n) { uint32_t answer; uint32_t int i; answer = 0; /*把一个unsigned int 数字1一直左移,直到它变成全0的时候,也就得到了该机器内unsigned int的长度*/ for (i = 1; i != 0; i <<= 1) { answer <<= 1; if (value & 1) { answer |= 1; } value >>= 1; } return answer; } };
这种方案将结果按照数据类型的位数进行移动,每次移动时判断value的最后一位是否为1,如果为1,那么在结果的最后一位添加1,如果不是,继续移位。
这种方案效率会高很多,因为我们知道对于C++编译器来讲,位操作肯定会比加法操作效率高得多。
试想一下,我们还有没有更好的方案呢,答案当时是肯定的。
http://graphics.stanford.edu/~seander/bithacks.html
在斯坦福的这篇文章里面介绍了众多有关位操作的奇思妙想,有些方法的确让人称赞,其中对于位翻转有这样的一种方法,将数字的位按照整块整块的翻转,例如32位分成两块16位的数字,16位分成两个8位进行翻转,这样以此类推知道只有一位。
对于一个8位数字abcdefgh来讲,处理的过程如下
abcdefgh -> efghabcd -> ghefcdab -> hgfedcba
class Solution { public: uint32_t reverseBits(uint32_t n) { n = (n >> 16) | (n << 16); n = ((n & 0xff00ff00) >> 8) | ((n & 0x00ff00ff) << 8); n = ((n & 0xf0f0f0f0) >> 4) | ((n & 0x0f0f0f0f) << 4); n = ((n & 0xcccccccc) >> 2) | ((n & 0x33333333) << 2); n = ((n & 0xaaaaaaaa) >> 1) | ((n & 0x55555555) << 1); return n; } };
这种方法的效率为O(log sizeof(int))。