数字范围按位与

84 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 11 天,点击查看活动详情

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

示例 1:

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

示例 2:

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

示例 3:

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

提示:

0 <= left <= right <= 231 - 1

解法一

思路和算法

最直观的做法是遍历区间 [left,right][left,right] 中的所有整数计算按位与运算的结果。由于 rightright 的最大值是 231−1231−1,因此遍历区间中的所有整数的时间复杂度过高,需要考虑时间复杂度更低的做法。

由于 leftleft 和 rightright 都是非负整数,因此二进制表示的最高位即符号位是 00,只需要考虑除了最高位以外的 3131 位。对于 0≤i<310≤i<31,从低到高的第 ii 位表示 2i2i。

如果 leftleft 和 rightright 的二进制表示的从低到高第 ii 位的值不同,则区间 [left,right][left,right] 中的所有整数按位与运算的结果的第 00 位到第 ii 位都是 00,理由如下。

考虑两个数的二进制表示的第 00 位到第 ii 位,区间 [left,right][left,right] 中一定包含一个整数 xx,其二进制表示的第 ii 位的值是 11,第 00 位到第 i−1i−1 位的值都是 00。

由于 leftleft 和 rightright 的第 ii 位的值不同,因此按位与运算的结果的第 ii 位的值是 00。

由于 xx 参与按位与运算,xx 的第 00 位到第 i1i−1 位的值都是 00,因此按位与运算的结果的第 00 位到第 i1i−1 位的值都是 00

考虑 leftleft 和 rightright 的二进制表示的连续最高位的相同部分,称为公共前缀。假设两个数的最长公共前缀为第 jj 位到最高位,则两个数的按位与运算的结果的第 jj 位到最高位的前缀等于两个数的最长公共前缀。

根据上述分析,计算区间 [left,right][left,right] 中的所有整数的按位与运算的结果等价于计算 leftleft 和 rightright 的二进制表示的最长公共前缀,该最长公共前缀后面的位都用 00 填充,即可得到按位与运算的结果。

具体做法是,从高到低依次遍历 leftleft 和 rightright 的二进制表示中除了最高位以外的每一位,将相等的位加到公共前缀中,遍历结束或者遇到不相等的位时结束遍历,遍历结束之后即可得到按位与运算的结果。

    public int rangeBitwiseAnd(int left, int right) {
        final int BITS = 31;
        int bitwiseAnd = 0;
        for (int i = BITS - 1; i >= 0; i--) {
            int mask = 1 << i;
            int bit1 = left & mask, bit2 = right & mask;
            if (bit1 == bit2) {
                bitwiseAnd += bit1;
            } else {
                break;
            }
        }
        return bitwiseAnd;
    }
}