2020-08-23——位运算相关题目

总结

Brian Kernighan 算法 ——n&(n-1)

Brian Kernighan 算法的关键在于我们每次对 number \textit{number} number
number − 1 \textit{number}-1 number1 之间进行按位与运算后, number \textit{number} number 中最右边的 11
会被抹去变成 00。
在这里插入图片描述

对应题目

  • 201
  • 461

题目

201. 数字范围按位与——Medium

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

示例 1:

输入: [5,7] 输出: 4 示例 2:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/bitwise-and-of-numbers-range
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解法1——第一思路

public int rangeBitwiseAnd(int m, int n) {
        int res=0;
        int bit = 0;
        while(m!=0||n!=0){
            if((m&0x1)==1 && (n&0x1)==1){
                // System.out.println((1<<bit)+"+"+res);
                res=(1<<bit)+res;
            }else if((m&0x1)==0 && (n&0x1)==0){

            }else{
                res=0;
            }
            bit++;
            m>>=1;
            n>>=1;
            // System.out.println(m+"-"+n+":"+res);
        }
        return res;
    }

从右开始,对位分情况运算

  • 都为1的时候,在结果左边添1
  • 都为0的时候,在结果左边添0(相当于什么都不做)
  • 一个为1,一个为0的时候,从0到1,0位于1=0;前面累计的结果置为0。
  • 例如[5,7]
    5 :101
    7 :111
    (1)1 (2)0 (3)1>>2=4
  • 例如[5,5]
    5 :101
    5 : 101
    (1) 1 (2) 01 (3)101

解法2——官方题解思路

最直观的解决方案就是迭代范围内的每个数字,依次执行按位与运算,得到最终的结果,但此方法在 [m,n][m,n] 范围较大的测试用例中会因超出时间限制而无法通过,因此我们需要另寻他路。

我们观察按位与运算的性质。对于一系列的位,例如 [1, 1, 0, 1,
1][1,1,0,1,1],只要有一个零值的位,那么这一系列位的按位与运算结果都将为零。

回到本题,首先我们可以对范围内的每个数字用二进制的字符串表示,例如 9=00001001_{(2)}9=00001001 (2) ​
,然后我们将每个二进制字符串的位置对齐。
在这里插入图片描述

在上图的例子中,我们可以发现,对所有数字执行按位与运算的结果是所有对应二进制字符串的公共前缀再用零补上后面的剩余位。

那么这个规律是否正确呢?我们可以进行简单的证明。假设对于所有这些二进制串,前 ii 位均相同,第 i+1i+1 位开始不同,由于
[m,n][m,n] 连续,所以第 i+1i+1 位在 [m,n][m,n] 的数字范围从小到大列举出来一定是前面全部是 00,后面全部是
11,在上图中对应 [9,11][9,11] 均为 00,[12,12][12,12] 均为 11。并且一定存在连续的两个数 xx 和
x+1x+1,满足 xx 的第 i+1i+1 位为 00,后面全为 11,x+1x+1 的第 i+1i+1 位为 11,后面全为
00,对应上图中的例子即为 1111 和 1212。这种形如 0111 … 0111 \ldots 0111 0000 … 0000 \ldots 0000
的二进制串的按位与的结果一定为 0000 … 0000 \ldots 0000,因此第 i+1 位开始的剩余位均为 00,前 ii
位由于均相同,因此按位与结果不变。最后的答案即为二进制字符串的公共前缀再用零补上后面的剩余位。

进一步来说,所有这些二进制字符串的公共前缀也即指定范围的起始和结束数字 mm 和 nn 的公共前缀(即在上面的示例中分别为 99 和
1212)。

因此,最终我们可以将问题重新表述为:给定两个整数,我们要找到它们对应的二进制字符串的公共前缀。

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/bitwise-and-of-numbers-range/solution/shu-zi-fan-wei-an-wei-yu-by-leetcode-solution/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

用位移法找到最长公共前缀
public int rangeBitwiseAnd(int m, int n) {
        int shift = 0;
        // 找到公共前缀
        while (m < n) {
            m >>= 1;
            n >>= 1;
            ++shift;
        }
        return m << shift;
    }

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/bitwise-and-of-numbers-range/solution/shu-zi-fan-wei-an-wei-yu-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
*Brian Kernighan 算法

Brian Kernighan 算法的关键在于我们每次对 number \textit{number} number
number − 1 \textit{number}-1 number1 之间进行按位与运算后, number \textit{number} number 中最右边的 11
会被抹去变成 00。
在这里插入图片描述
基于上述技巧,我们可以用它来计算两个二进制字符串的公共前缀。

其思想是,对于给定的范围 [m,n][m,n](m<nm<n),我们可以对数字 nn 迭代地应用上述技巧,清除最右边的
11,直到它小于或等于 mm,此时非公共前缀部分的 11 均被消去。因此最后我们返回 nn 即可。

在这里插入图片描述

在上图所示的示例(m=9, n=12m=9,n=12)中,公共前缀是 0000100001。在对数字 nn 应用 Brian
Kernighan 算法后,后面三位都将变为零,最后我们返回 nn 即可。

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/bitwise-and-of-numbers-range/solution/shu-zi-fan-wei-an-wei-yu-by-leetcode-solution/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

class Solution {
    public int rangeBitwiseAnd(int m, int n) {
        while (m < n) {
            // 抹去最右边的 1
            n = n & (n - 1);
        }
        return n;
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/bitwise-and-of-numbers-range/solution/shu-zi-fan-wei-an-wei-yu-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

461. 汉明距离

两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。

给出两个整数 x 和 y,计算它们之间的汉明距离。

注意: 0 ≤ x, y < 231.

示例:

输入: x = 1, y = 4

输出: 2

解释: 1 (0 0 0 1)
4 (0 1 0 0)
↑ ↑

上面的箭头指出了对应二进制位不同的位置。

x^y是异或,相同位为0,不同位为1 。那么只需要计算x^y为1 的位数。

public int hammingDistance(int x, int y) {
        int n=x^y;
        int count=0;
        while(n!=0){
            n=n&(n-1);
            count++;
        }
        return count;
    }

class Solution {
    public int hammingDistance(int x, int y) {
        return Integer.bitCount(x ^ y); 
        /**
        内置的,实现原理与上面一致
        **/
    }
}

class Solution {
  public int hammingDistance(int x, int y) {
    int xor = x ^ y;
    int distance = 0;
    while (xor != 0) {
      if (xor % 2 == 1)
        distance += 1;
      xor = xor >> 1;
    }
    return distance;
  }
  /**
     对每一位进行判断。
  **/
}

作者:LeetCode
链接:https://leetcode-cn.com/problems/hamming-distance/solution/yi-ming-ju-chi-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值