二进制整数中的“1”
这里主要通过一些位运算的技巧来找出一个整数的二进制表示中1的个数或其中某些特殊的1的位置。
1 的个数
求二进制表示中1的个数存在时间复杂度为O(n)(n为1的个数)的算法。 其要点是 若整数 x 不为0, 表达式(指C语言表达式) x&(x-1) 的运算结果是将 x 的二进制表示中最右侧的1消除后的值, 若x为0, 表达式的值为0。 这样, 若 x 不为0, 我们就可以通过表达式 x = x&(x-1) 反复消除 x 最右侧的1直到x为0, 消除的次数即为所求1的个数。
C语言代码(输入为32位无符号整型)
1 int number_of_ones(uint32_t x) { 2 int result = 0; 3 while (x) { 4 x = x & (x - 1); 5 result++; 6 } 7 return result; 8 }
最右侧1的位置
最右侧1的位置通过表达式 x ^ (x & (x-1))计算, 如, x: 0x12345678, x ^ (x & (x-1)) : 0x00000008
1 uint32_t right_most_one(uint32_t x) { 2 return x ^ (x & (x - 1)); 3 }
最左侧1的位置
求最左侧1的位置相对比较复杂, 下面是一种比较直接的算法。
1 uint32_t left_most_one(uint32_t x) { 2 uint32_t test = x & (x - 1); 3 while (test) { 4 x = test; 5 test = x & (x - 1); 6 } 7 return x; 8 }
在Java ArrayDeque 的 allocateElements 源码中看到一种比较有意思的算法, 它用来计算大于一个数的2的整数次幂。 我们可以对所求2的次幂按位右移一位来求最左侧1的位置。
下面给出源代码 ps: 自行思考一下算法的原理 :)
1 uint32_t left_most_one2(uint32_t x) { 2 uint32_t tmp = x; 3 tmp |= (tmp >> 1); 4 tmp |= (tmp >> 2); 5 tmp |= (tmp >> 4); 6 tmp |= (tmp >> 8); 7 tmp |= (tmp >> 16); 8 tmp++; 9 return tmp ? (tmp >> 1) : (0x80000000); 10 }
参考
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/ArrayDeque.java