题目描述
给定一个范围[m, n] 0 <= m, n <= 2147483647,返回此闭区间内的所有数字进行与运算后的结果。
这是一道leetcode上的经典题目,下面我将介绍两种解法,当然有兴趣的可以去原网站看一下具体题目和官方解答。
常规解法
这种解法是最简单的,包括我一开始也是这样做的。
具体代码如下:
class Solution {
public:
int rangeBitwiseAnd(int m, int n) {
int ans = m;
for (int i = m; i <= n; ++i)
ans = ans & i;
return ans;
}
};
时间复杂度为O(N),leetcode的编译器是通不过这个复杂度的。何况题目难度为medium,题目要求肯定不会让我们这么。
另外,如果有不了解位运算的,可以看一下我的另一篇博客《位运算知识点总结》。
解法一
我们先观察9到12的二进制。
当我们进行与运算的时候,要想成立, 所有位必须为 1 ,反之,如果有一数上的对应位为 0 的话,计算的结果肯定为 0 。再观察上面每一个数的二进制,他们的二进制一定有一个公共前缀(黄色标注)。
这个公共前缀的与运算一定是等于这个公共前缀的。
而任意两个数的与运算,除了公共前缀之外,它的后缀进行与运算的话一定是等于0的,大家不妨拿上笔多试几个数可以发现这个规律。
所以!!这道题由计算它的与运算,转向找到它的公公前缀后就可以得到答案了。
如何计算,在看完代码后,大家一定能够知道。
class Solution {
public:
int rangeBitwiseAnd(int m, int n) {
int ans = 0; // 用来记录左移了几位
while (m != n) {
m >>= 1;
n >>= 1;
ans += 1;
}
// 还原
return m << ans;
}
};
时间复杂度为O(1),因为我们只用了常数级的数据来计算公共前缀
解法二
此解法,我就简单讲一下了,我们在进行计算的时候,实则其实就找到两个数的公共前缀就行了,然后向左移位,就把后缀全部变成0了。
其实还有一种解法,我们可以把二进制最右边的1变成0。
n = n & (n - 1)
,这个公式就可以把我们一个数的二进制最右边的1变成0,具体可以结合上面的图片思考。
我们参考上面的数据,要计算 9 到 12 的所有数的与运算,其实就是把 12 的 二进制右边的 1 不断的变成 0 ,直到其二进制成为0000 1000
,此时肯定比 9 小,那么我们就可以退出循环了,然后将 9 与 0000 1000
进行与运算就得到答案了,其实我们不断的把 12 右边第一个 1 变成 0 的过程,就是在求公共前缀。
class Solution {
public:
int rangeBitwiseAnd(int m, int n) {
while (m < n) { // 当 m = n 或者 大于 n的时候,就说明找到了公共前缀了
n = n & (n - 1); // 让n最右边的1变成0
}
return n & m;
}
};
时间复杂度O(1)