201. 数字范围按位与
难度 中等
给你两个整数 left
和 right
,表示区间 [left, right]
,返回此区间内所有数字 按位与 的结果(包含 left
、right
端点)。
示例 1:
输入:left = 5, right = 7
输出:4
示例 2:
输入:left = 0, right = 0
输出:0
示例 3:
输入:left = 1, right = 2147483647
输出:0
提示:
0 <= left <= right <= 231 - 1
题解
刚开始看到的时候,就觉得是数学题目,就去看看有什么规律,然后果然找到一个规律,就是如果left右移一位还小于right的话,一定返回0。(简称跨位与)
2^3 | 2^2 | 2^1 | 2^0 | |
---|---|---|---|---|
5 | 0 | 1 | 0 | 1 |
6 | 0 | 1 | 1 | 0 |
7 | 0 | 1 | 1 | 1 |
8 | 1 | 0 | 0 | 0 |
9 | 1 | 0 | 0 | 1 |
当5 & 6 & 7 = 4时,4的二进制位100
2^3 | 2^2 | 2^1 | 2^0 | |
---|---|---|---|---|
4 | 0 | 1 | 0 | 0 |
8 | 1 | 0 | 0 | 0 |
9 | 1 | 0 | 0 | 1 |
4 & 8 = 0,很容易就发现,如果right(9)大于left(5)左移一位(right > left << 1)时,无论后面是什么,返回的肯定是0。
另外可能要注意的就是left左移的时候会溢出(变为负数),导致right一定大于left << 1,所以我们还需要判断left是否大于2^30。
如果大于,那就会溢出,但是right小于2^31-1,两个条件限制了left和right最高位一样,不会出现跨位与的情况;
如果小于,不会溢出,再判断是否出现跨位与
class Solution {
public int rangeBitwiseAnd(int left, int right) {
if(left < (1 << 30) && right > (left << 1)){//left是否小于1 << 30,且判断是否出现跨位与
return 0;
}else{//没有出现跨位与,直接返回按位与的情况
int ans = left;
while(left < right){
ans &= ++left;
}
return ans;
}
}
}
移位
2^3 | 2^2 | 2^1 | 2^0 | |
---|---|---|---|---|
5 | 0 | 1 | 0 | 1 |
6 | 0 | 1 | 1 | 0 |
7 | 0 | 1 | 1 | 1 |
8 | 1 | 0 | 0 | 0 |
9 | 1 | 0 | 0 | 1 |
还是这个表格,我们可以发现,当5 & 6 & 7 的时候,返回的是4,是他们的公共前缀,他们高位都是一样的,然后1 * 2^2 = 4。当我们5 & 6 & 7 & 8 & 9的时候,返回的是0,因为公共前缀在5,6,7出现了0。
所有我们要求他们的公共前缀,那名字高效的求解公共前缀呢,那就是移位了,通过左移,当左移到同一个数值时,就停止,比如5跟7左移两位变成1,8跟9左移两位变成1。然后我们统计左移了多少位,再右移回去,就得到了想要的答案,比如5跟7左移两位变为1之后,1右移两位变成4,。
class Solution {
public int rangeBitwiseAnd(int left, int right) {
int shift = 0;//统计移动了多少位
while(left < right){//会一直移动到right == left或者right和left都为0
left >>= 1;
right >>= 1;
++shift;
}
return left << shift;//右移复原
}
}