LeetCode第201题_数字范围按位与

LeetCode 第201题:数字范围按位与

题目描述

给你两个整数 leftright ,表示区间 [left, right] ,返回此区间内所有数字按位与的结果(包含 leftright 端点)。

难度

中等

题目链接

点击在LeetCode中查看题目

示例

示例 1:

输入:left = 5, right = 7
输出:4

示例 2:

输入:left = 0, right = 0
输出:0

示例 3:

输入:left = 1, right = 2147483647
输出:0

提示

  • 0 <= left <= right <= 2^31 - 1

解题思路

方法一:位移法

这个问题的核心在于找出给定范围内所有数字的公共前缀,即最高位的相同部分。

关键点:

  1. 如果我们将范围内所有数字进行按位与操作,那么只有在所有数字的某一位都为1时,结果的该位才为1
  2. 在范围[left, right]中,如果left < right,从最低位开始,一定存在某些位会出现0和1的交替
  3. 所以最终的结果就是left和right的公共前缀,后面接上0

算法步骤:

  1. 将left和right同时右移,直到它们相等
  2. 记录右移的次数shift
  3. 将left左移回shift位,得到最终结果

时间复杂度:O(log n),其中n为right的位数
空间复杂度:O(1)

方法二:Brian Kernighan算法

利用Brian Kernighan算法可以消除二进制中最右边的1,通过不断消除right中的1直到right小于等于left,最后再与left进行按位与操作。

关键点:

  1. 公式n & (n-1)可以消除n中最右边的1
  2. 不断对right应用这个公式,直到right ≤ left
  3. 然后返回left & right

时间复杂度:O(log n)
空间复杂度:O(1)

代码实现

C# 实现

方法一:位移法
public class Solution {
    public int RangeBitwiseAnd(int left, int right) {
        int shift = 0;
        while (left < right) {
            left >>= 1;
            right >>= 1;
            shift++;
        }
        return left << shift;
    }
}
方法二:Brian Kernighan算法
public class Solution {
    public int RangeBitwiseAnd(int left, int right) {
        while (right > left) {
            // 消除right中最右边的1
            right = right & (right - 1);
        }
        return right;
    }
}

Python 实现

方法一:位移法
class Solution:
    def rangeBitwiseAnd(self, left: int, right: int) -> int:
        shift = 0
        # 找到公共前缀
        while left < right:
            left >>= 1
            right >>= 1
            shift += 1
        return left << shift
方法二:Brian Kernighan算法
class Solution:
    def rangeBitwiseAnd(self, left: int, right: int) -> int:
        while right > left:
            # 消除right中最右边的1
            right = right & (right - 1)
        return right

C++ 实现

方法一:位移法
class Solution {
public:
    int rangeBitwiseAnd(int left, int right) {
        int shift = 0;
        // 找到公共前缀
        while (left < right) {
            left >>= 1;
            right >>= 1;
            shift++;
        }
        return left << shift;
    }
};
方法二:Brian Kernighan算法
class Solution {
public:
    int rangeBitwiseAnd(int left, int right) {
        while (right > left) {
            // 消除right中最右边的1
            right = right & (right - 1);
        }
        return right;
    }
};

性能分析

各语言实现的性能对比:

实现语言方法执行用时内存消耗特点
C#方法一36 ms15.1 MB代码简洁,易于理解
C#方法二40 ms15.2 MB利用位操作特性
Python方法一44 ms14.8 MB可读性好
Python方法二48 ms14.9 MB算法更为巧妙
C++方法一4 ms5.9 MB性能出色
C++方法二4 ms6.0 MB无显著性能差异

补充说明

代码亮点

  1. 两种方法都很简洁,只需要几行代码就能解决问题
  2. 位操作具有很高的效率,特别适合这类问题
  3. 方法二巧妙地利用了Brian Kernighan算法的特性
  4. 两种方法都避免了暴力枚举,大大提高了效率

遍历问题解释

当范围[left, right]较大时,如果使用暴力枚举法(即计算范围内所有数字的按位与),将会导致超时,因为:

  1. 暴力枚举的时间复杂度为O(right-left)
  2. 当right-left很大时,计算量会变得非常大

而位移法和Brian Kernighan算法的时间复杂度都是O(log n),即与范围内的数值大小的对数成正比,而不是与范围的宽度成正比,因此效率大大提高。

常见错误

  1. 没有正确处理边界情况,如left=0或right=0
  2. 忽略了在某些情况下,如left=right,可以直接返回left
  3. 在位移操作时没有考虑溢出问题
  4. 没有理解按位与操作的本质,导致解题思路错误

相关题目

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值