题目来源
题目描述
class Solution {
public:
int rangeBitwiseAnd(int left, int right) {
}
};
题目解析
分析数据
- 区间范围很大,区间一定是正数
思路
算法
问题的本质是求解m和n的最长公共前缀,如果m==n直接返回。如果m<n,那一定是以下的形式。
m: S S S 0 X X X X
n: S S S 1 X X X X
上面的S代表相等,X代表未知(不一定相等)。那数字范围按位与的结果,应该为:
S S S 0 0 0 0 0
S S S 部分
算法
- 两数都向右移,直到移到他俩相等,这样就找到了他俩的公共前缀
- 刚刚向右移了几位,现在再向左移几位,这样的话就相当于把非公共前缀后面的非零位都变成0
class Solution {
public:
int rangeBitwiseAnd(int left, int right) {
int shift = 0;
// 为什么是 left != right 因为保证的是不能全移没了,
// 向右位移 最后的结果是 left 和 right 找到二进制位的公共前缀 这时候left 和right 已经相等了
while (left != right) {
// 都同步向右移
left >>= 1;
right >>= 1;
// 记录移动的次数
++shift;
}
// 再向左移动刚刚向右移动的次数
return left << shift;
}
};
清除二进制串中最右边的 1
还有一个位移相关的算法叫做「Brian Kernighan 算法」,它用于清除二进制串中最右边的 11。
Brian Kernighan 算法的关键在于我们每次对number 和 number−1 之间进行按位与运算后,number 中最右边的 1 会被抹去变成 0。
基于上述技巧,我们可以用它来计算两个二进制字符串的公共前缀。
其思想是,对于给定的范围 [m,n](m<n),我们可以对数字 n迭代地应用上述技巧,清除最右边的 1,直到它小于或等于 m,此时非公共前缀部分的 1 均被消去。因此最后我们返回 n 即可。
class Solution {
public:
int rangeBitwiseAnd(int left, int right) {
while (left < right){
right = right & (right - 1);
}
return right;
}
};