算法通关村——位运算的高频考题

一、位1的个数

        我们要判断一个数的二进制中有多少个1,最简单的方式就是在循环中与1进行与运算之后判断是否为1,然后1<<1,这样子就可以完成目标了。但是不够不够简练,首先我们要知道一个规律。

n:0001 0000 1001 0010 0100                                                                                                            n-1:0001 0000 1001 0010 0011

n&(n-1):0001 0000 1001 0010 0000

所以每一次n和(n-1)进行与运算都可以将一个1消掉。所以我们可以这么做

public int hammingweight(int n){
    int ones = 0;
    while(n!=0){
        n &= (n-1);
        ones++;
    }
    return ones;
}

二、颠倒无符号整数

首先这里说是无符号位,那不必考虑正负的问题,最高位的1也不表示符号位,这就省掉很多麻烦。我们注意到对于 n 的二进制表示的从低到高第i位,在颠倒之后变成第 31-i 位 0i32),所以可以从低到高遍历 n 的二进制表示的每一位,将其放到其在颠倒之后的位置,最后相加即可。看个例子,为了方便我们使用比较短的16位演示:

原始数据: 0000 1001 0010 0100

reversed:0*** **** **** ****

原始数据右移一位:0000 0100 1001 0010

所以我们的代码可以是

public int reverseBits(int n){
    int reversed = 0, power = 31;
    while(n!=0){
        reversed += (n&1)<<power;
        n >>>= 1;
        power--; 
    }
    return reversed;
}

三、位运算实现加法

输入: a= 1(01),b= 2(10)

输出:3(11)

二进制运算有四种情况

0+0=0

0+1=1

1+0=1

1+1=0(进位1)

两个位加的时候,我们无非就考虑两个问题: 进位部分是什么,不进位部分是什么。从上面的结果可以看到,对于a和b两个数不进位部分的情况是: 相同为0,不同为1,这不就是a⊕b吗?而对于进位,我们发现只有a和b都是1的时候才会进位,而且进位只能是1,这不就是a&b=1吗? 然后位数由1位变成了两位,也就是上面的[4]的样子,那怎么将1向前挪一下呢? 手动移位一下就好了,也就是(a &b) << 1。所以我们得到两条结论:
不进位部分: 用a⊕b计算就可以了。
是否进位,以及进位值使用(a & b) << 1计算就可以了

public int getSum(int a,int b){
    while(b!=0){
        //进位的结果
        int sign = (a&b)<<1;
        a = a ^ b;
        b = sign;
    }
    return a;
}

四、递归乘法

LeetCode里面试08.05,递归乘法。写一个递归函数,不使用* 运算符,实现两个正整数的相乘。可以使用加号、减号、位移,但要一些。

如果不让用*来计算,一种是将一个作为循环的参数,对另一个进行累加,但是这样效率太低,所以我们还是要考虑位运算。
首先,求得A和B的最小值和最大值,对其中的最小值当做乘数(为什么选最小值,因为选最小值当乘数,可以算的少),将其拆分成2的幂的和,即min = a 0* 20 + a 1* 21 + ... + a * 2i +.其中a i取或者1。其实就是用二进制的视角去看待min,比如12用二进制表示就是1100,即1000+0100。例如:
13 *12 = 13 *(8 + 4) = 13* 8 + 13* 4 =(13 << 3) + (13 << 2);上面仍然需要左移5次,存在重复计算,可以进一步简化:假设我们需要的结果是ans,
定义临时变量: tmp=13<<2 =52计算之后,可以先让ans=52然后tmp继续左移一次tmp=52<<1=104,此时再让ans=ans+tmp这样只要执行三次移位和一次加法,实现代码:

public static int multiply(int A, int B) {
        int max = A > B ? A : B;
        int min = A < B ? A : B;
        int ans = 0;
        for (int i = 0; min != 0; i++) {
            if ((min & 1) == 1) {
                ans += max;
            }
            min >>= 1;
            max <<= 1;
        }
        return ans;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值