1. 消除最后的一个“1”bit
x & ( x - 1 )
看上去很难理解,但是有很容易理解。要不先用几分钟思考一下?
比如:6 & ( 6 - 1 ) 就从 110 变为 100,消除了最后面的一个"1"bit,为什么呢?
其实只需要考虑110 后面 的“10”就可以了,因为任何一个数后面都是10的结构(0可能有0~n),x > 0,为什么?
因为整数只有奇数和偶数。首先偶数最后一位必然为0,奇数则为1,这是也是我们判断奇偶数的原理。
所以了可以把任何一个正整数最后面的位看成10结构(0为0~n个)。
因此分两种情况,后面有没有0与n个"0"的情况。
第一种(没有0,也就是奇数情况):
x & (x-1),x为奇数,这个很容易知道啊,x - 1 把二进制最后的1个去掉了,只是改动了一位,前面的没有动,因此消除了一个1bit
5 & (5-1) => 101 & (100) = 100 (黄色的地方没变化,变化的是第一位)
第二种情况(偶数,10结构):
这次使用一个大点的会容易理解,二进制数 n = xxxx100000,前面的xxxx我们先忽略,因为我们不需要知道具体的值,只要符合10结构既可以,
因此 n - 1 = xxxx011111,这样看来 n & (n-1),就会把n中黄色的地方变为0,也就达到了消除最后一个1bit的效果
嗯,至少我先这么理解吧
2. 获取最后一个"1"bit
这次跟上面的其实有点相似的,只要思考思考就会想明白了。
同样我们也只要考虑最后面的几个bit就好了
思路:有没有可以使用位运算来消除多余的位,只剩下最后那个1bit呢? & 可以试一试
比如一个正整数 n & ~n =0;这个会把所有的到消除了,不合适。
不妨假设 x 是n前面的bit,并且进行~运算会得到y
因为每个大于0的整数都是10结构,比如:n & (~n) = x10000 & y01111 = 0,但是 n & (~n+1) = x10000 & ( y01111 + 1 ) = x10000 & ( y10000 ) = 10000
思想就是把最后的 10结构保留原样,前面的就取反,这样进行&运算就可以得到最后的1bit了。
我们来优化一下,~n + 1 可以变成什么?我们知道n是大于0的整数啊,~n + 1 不就是 -n的补码(使用补码存储与运算)?所以可以优化为: n & -n就可以了
~n + 1 = -n
==> n & (~n+1) = n & -n
==> ~n = -(n+1)
3. 按区域取反
如果我想把某一个数n的某一个区域的bit取反会怎么样?
提示:使用异或运算,可以知道1遇到0会变1;如果1遇到1就会变0;0遇到啥都不会变;难懂这不就是取反吗
例子:A ^ B = 1010 ^ 1100 = 0100 。这个例子黄色部分bit就把A中某一部分的bit取反了。
接下来:就是让读者写出具有通用的代码了(让某一个数的n位到m位取反)
4. 位运算需要32位
需要注意的是,位运算在一些语言里面是基于32为整型。 比如PHP,Javascript等。
问:有人会问为什么JS也是要求32位呢,不是不区分浮点数,整型,更没有位数吗?
答:JS的确没有这些区分,但是位运算只能用于32位整型的。另外JS对于整型是有安全范围的,如果你使用过大的数(超过整型 Number.MAX_SAFE_INTEGER)就必须转为字符串,否则失真
类似的问题:
* 某个区域设定固定的bit,比如 n 的4-7为设置为固定的bit不如全是1或者全是0,其他保持不变?
还有一些“著名”的题目:
single number I
single number II
single number III
3部曲,可以了解一下
以下是一个面试题的网站:
http://www.lintcode.com/zh-cn/problem/