给定范围 [m, n],其中 0 <= m <= n <= 2147483647,返回此范围内所有数字的按位与(包含 m, n 两端点)。
示例 1:
输入: [5,7]
输出: 4
朴素
从 m 与 到 n,超出时间限制(m=0,n=21474…)
规律
可以将问题重新表述为:给定两个整数,我们要找到它们对应的二进制字符串的公共前缀
简单的证明:假设对于所有这些二进制串,前 i 位均相同,第 i+1 位开始不同,由于 [m,n] 连续,所以第 i+1 位在 [m,n] 的数字范围从小到大列举出来一定是前面全部是 0,后面全部是 1,在上图中对应 [9,11] 均为 0,[12,12] 均为 1。并且一定存在连续的两个数 x 和 x+1,满足 x 的第 i+1 位为 0,后面全为 1,x+1 的第 i+1 位为 1,后面全为 0,对应上图中的例子即为 11 和 12。这种形如 0111 和 1000 的二进制串的按位与的结果一定为 0000 。
方法一:位移
public int rangeBitwiseAnd(int m, int n) {
int shift = 0;
// 找到公共前缀
while (m < n) {
m >>= 1;
n >>= 1;
++shift;
}
return m << shift;
}
方法二:布赖恩·克尼根算法
public int rangeBitwiseAnd(int m, int n) {
while (m < n) {
// 抹去最右边的 1
n = n & (n - 1);
}
return n;
}