近日在leetcode上做位运算的习题,发现很多人都用到n &= (n - 1)或者n = n & (n - 1)这种写法。一开始看觉得很奇怪,细细分析一下才发现它的奇妙。
从具体的数字开始分析。比如当n = 0b1000时,那么这句话做完,n = 0;这个过程是怎么样的呢?
我们知道n = 0b1000,那么n - 1 = 0b0111对吧。因此,n & (n - 1)就是结果0000了。
再如:n = 0b1010,那么n - 1 = 0b1001,那么n & (n - 1) = 0b1000。
到这里,我们发现了规律,原来n &= (n - 1)这句话可以把位数最低的那个1消除掉。
比如,0b1010做完之后变成0b1000,位数最低的那个1,不见了。
其实原理也很简单,因为位数最低的那个1,形式肯定是这样的n = …1000…
那么n - 1 = …0111…
…1000…
…0111…
两者相与,原本的那个1肯定就没了,并且后面也都变成了0。
那说了这么多,这句话在编程中到底有什么卵用呢?
【例题1,Leetcode 231. 2的幂】
给定一个整数,编写一个函数来判断它是否是 2 的幂次方。
示例 1:
输入: 1
输出: true
解释: 20 = 1
--------------------
示例 2:
输入: 16
输出: true
解释: 24 = 16
--------------------
示例 3:
输入: 218
输出: false
这个题用传统做法,一位一位地右移对比,也可以做出来:
class Solution {
public boolean isPowerOfTwo(int n) {
if(n == 0) return false;
int cnt = 0;
while(n != 0){
if((n & 1) == 1){
cnt++;
}
if(cnt > 1) return false;
n >>= 1;
}
return true;
}
}
执行完所有测试用例需要6ms,已经是很不错的做法了。
分析一下这个题,我们发现,只要这个数的二进制形式中有且仅有一个1,那么它就一定是2的幂。基于这样一种思路,我们可以利用n &= (n - 1)来加速。
class Solution {
public boolean isPowerOfTwo(int n) {
//如果只有1个1,那么这句话做完,temp一定为0
int temp = n & (n - 1);
//注意如果n == 0的特殊情况
if(n > 0 && temp == 0){
return true;
}else{
return false;
}
}
}
耗时3ms,成功击败了97%的用户。
因此我们发现,用n &= (n - 1)这种写法,可以快速统计二进制数中的1的个数。如果用移位操作,我们必须一位一位地比较,就慢多了。
【例题2.Leetcode 461. 汉明距离】
两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。
给出两个整数 x 和 y,计算它们之间的汉明距离。
注意:
0 ≤ x, y < 231.
示例:
输入: x = 1, y = 4
输出: 2
解释:
1 (0 0 0 1)
4 (0 1 0 0)
↑ ↑
上面的箭头指出了对应二进制位不同的位置。
题意比较好理解,用传统做法,对两个数进行移位,移的同时对比x和y的每一位是否相等,如果不相等,cnt++,最后返回cnt即可。
代码就不上了,这种做法效率很低,只能击败20%左右的用户。
分析一下这个题,无非就是看x、y每一位是否一样。
我们可以将z = x ^ y 。那么z中有多少个1,即我们最后的结果。因为异或的性质是不同的为1,相同的为0嘛,所以我们把问题转换成统计z中1的个数了。因此就回到上面说的n &= (n - 1)这种写法了
1ms代码,击败99.62%:
class Solution {
public int hammingDistance(int x, int y) {
int z = x ^ y;
int cnt = 0;
//看有多少个1,每次循环都可以消掉一个1,只要循环能继续,就说明还有1
while(z != 0){
z &= (z - 1);
cnt++;
}
return cnt;
}
}

本文深入解析位运算技巧n&=(n-1),揭示其工作原理及在编程中的应用,如快速判断2的幂次方和计算汉明距离,提升算法效率。
1254

被折叠的 条评论
为什么被折叠?



