算法通关村-----位运算高频算法题

1 位移的妙用

1.1 位1的个数

问题描述

编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。详见leetcode191

问题分析

可以将输入的无符号整数各个bit位依次与1进行与运算,通过设置计数器,与运算的结果为1,计数器加1,上面提到的依次可以通过移位运算来实现,具体是指无符号整数右移或者1左移

代码实现

1左移方式

public int hammingWeight(int n) {
    int count = 0;
    for(int i = 0;i<32;i++){
        // sum += ((n>>i)&1);
        if((n&(1<<i))!=0){
            count++;
        }
    }
    return count;
}

2 无符号整数右移方式

public int hammingWeight(int n) {
    int count = 0;
    for(int i = 0;i<32;i++){
        // sum += ((n>>i)&1);
        if(((n>>i)&1)!=0){
            count++;
        }
    }
    return count;
}

优化思路

任意无符号整数n与n-1相差1,表现在bit位上相差最低位1,即n&n-1的结果与n相差1,可以通过不断的执行n&n-1,直至n=0 来统计n中1的个数

代码实现

ublic int hammingWeight(int n) {
    int count = 0;
    while(n!=0){
        n = n&(n-1);
        count++;
    }
    return count;
}

1.2 比特位计数

问题描述

给你一个整数 n ,对于 0 <= i <= n 中的每个 i ,计算其二进制表示中 1 的个数 ,返回一个长度为 n + 1 的数组 ans 作为答案。

问题分析

可以看到,这仍然是统计二进制中1的个数问题,相比于上一题,只是增加了需要统计的元素数量,所以只需要加一层循环即可,无论是使用基本方法还是优化方法,都可以实现,这里提供优化方法的代码实现,大家可以自主实现基本方式

代码实现

public int[] countBits(int n) {
    int[] ans = new int[n+1];
    for(int i = 0; i<=n; i++){
        int count = 0;
        int x = i;
        while(x!=0){
            x = x & (x-1);
            count++;
        }
        ans[i] = count;
    }
    return ans;
}

1.3 颠倒无符号整数

问题描述

颠倒给定的 32 位无符号整数的二进制位。详见leetcode190

问题分析

对于32位无符号整数,调到之后变成31-i位,可以将无符号整数通过不断移位与1进行与运算,获取当前比特位的值,再通过移位操作放到指定的位置。

代码实现

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

2 通过位运算实现四则运算

2.1 通过位运算实现加法

问题描述

给你两个整数 a 和 b ,不使用 运算符 + 和 - ​​​​​​​,计算并返回两整数之和。详见leetcode371

问题分析

不使用运算符 + 和 - ​​​​​​​,可以考虑通过位运算来实现加法。两个比特位相加时,相同为0,不同为1,进位可以通过两个比特位进行与运算判断,因为只有两个比特位都为1时才会产生进位,此时啊a&b = 1,进位的值是(a&b)<<1;

代码实现

public int getSum(int a, int b) {
	 while(b!=0){
	     int flag = (a&b) << 1;
	     a = a^b;
	     b = flag;
	 }
	 return a;
}

2.2 通过位运算实现乘法

问题描述

递归乘法。 写一个递归函数,不使用 * 运算符, 实现两个正整数的相乘。可以使用加号、减号、位移,但要吝啬一些。详见leetcode08.05

问题分析

不使用*运算符,可以考虑累加,但题目中说要吝啬使用,可以考虑移位运算。可以以两个加数中较大的作为基数,通过判断较小值对应的比特位是否为1,来判断基数是否需要累加,这样可以通过较少的加法和移位运算来实现乘法。

代码实现

public int multiply(int A, int B) {
    int max = Math.max(A,B);
    int min = Math.min(A,B);
    int result = 0;
    while(min!=0){
        if((min&1)!=0){
            result += max;
        }
         min = min >> 1;
         max += max;
    }
    return result;
}

总结

位运算的题目往往比较简单,可以通过移位来实现我们之前所谓的循环遍历,可以通过与、或、非、异或等基本运算实现对指定比特位的获取和设置,获取和设置技巧详见位运算规则。再结合题目要求,进行操作即可。

你可能感兴趣的:(算法,数据结构,java)