leetcode-201. 数字范围按位与

题目

给定范围 [m, n],其中 0 <= m <= n <= 2147483647,返回此范围内所有数字的按位与(包含 m, n 两端点)。

示例 1:

输入: [5,7]
输出: 4

示例 2:

输入: [0,1]
输出: 0

解题思路

暴力版,从m到n一路按位与下去,会超时。

Period Rule

Note the 1s and 0s are changing following a rule: the number of 1s and 0s in digit i are 2^i, so for digit i, there will only be 2^i / 2 1s and 2^i / 2 0s. If the number between left and right is larger than 2^i / 2, then there must be 0s, so after AND the digit must be 0. If the number is smaller than 2^i / 2, then the result depends on left and right. If left and right at the current digit are both 0, then it must be 0. If one of them is 0, then the result is 0. Only when left and right both have 1 on the digit, will the result be 1.

Time complexity: o ( log ⁡ n ) o(\log n) o(logn)
Space complexity: o ( 1 ) o(1) o(1)

官方题解版

以计算[9, 12]为例,先写出来每个数字对应的二进制形式:

9: 	0 0 0 0 1 0 0 1
10: 0 0 0 0 1 0 1 0
11: 0 0 0 0 1 0 1 1
12: 0 0 0 0 1 1 0 0

因为是从mn的连续区间,所以必然有某个位x满足:该数字的x位为0,x后面的位都是1,下一个数字的x位是1,x后面的数字都是0
这样按位与之后,会导致x及x之后所有位都是0
因此最终的结果实际上是求区间开头和区间结尾的二进制表示中的共同前缀。

求二进制共同前缀的方法:
位移法
将两个数字同时向右移位,并记录移位的次数。当两个数字相等时,就得到了共同的前缀,再将这个共同前缀向左移位移回原先大小即可。

时间复杂度 o ( log ⁡ 2 n ) o(\log_2n) o(log2n),其中n是区间大小

Brian Kernighan 算法
背景知识:将一个数nn - 1按位与之后,可以去除n最右边的1

考虑对于区间右边界n,不断去除最右边的1,当n <= m时,非公共前缀的1就全部去掉了,此时的n就是答案

时间复杂度仍然是 o ( log ⁡ 2 n ) o(\log_2n) o(log2n)

代码

Period Rule

class Solution:
    def rangeBitwiseAnd(self, left: int, right: int) -> int:
        number_between = right - left + 1
        res = 0
        period_len = 1
        while period_len <= right:
            if number_between <= period_len:
                res += left & right & period_len
            period_len *= 2
        return res

位移版

class Solution:
    def rangeBitwiseAnd(self, m: int, n: int) -> int:
        shift = 0
        while m < n:
            m >>= 1
            n >>= 1
            shift += 1
        return m << shift

Brian Kernighan 算法

class Solution:
    def rangeBitwiseAnd(self, m: int, n: int) -> int:
        while n > m:
            n &= (n - 1)
        return n
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值