leetcode刷题笔记————位运算

一.Java中常见的位运算

1.引子

位运算是针对二进制的每一位进行的运算,它是专门针对数字0和1进行的操作。程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算是直接对整数在内存中的二进制位进行操作码,位运算即可以节约内存,同时使程序速度更快效率更高。

2.java中的位运算

Java位用算运算符又可以分为 逻辑运算符 和 位移运算符 ;
逻辑运算符有:按位与 &、按位或 |、取反 ~、按位异或^;
位移运算符有:左移 <<、右移 >>、无符号右移 >>>;
在所有的位运算符中,除~以外,其余均为二元运算符操作数,并且位运算的运算对象为整型和字符型数据

二.逻辑运算符

1^(亦或运算)

运算规则:针对二进制,相同的为0,不同的为1

public static void main(String[] args) {
    System.out.println("3^4运算的结果是 :"+(3^4));
    //3 =======>011
	//4 =======>100
	//异或结果为:7(111)
}

特点:
1.任何一个数字异或他自己都等于0;
2.任何一个数字与0异或都是它自己;
3.a ^ b就是a和b相加之后,该进位的地方不进位的结果;
4.与指定位全是1进行^ 异或运算,可实现该位反转(1变0,0变1);

2&(与运算)

运算规则:两数相与,只要有一个为0,就为0,同时为1才为1.

public static void main(String[] args) {
    System.out.println("3&4运算的结果是 :"+(3&4));
    //3 =======>011
	//4 =======>100
	//与结果为:0(000)
}

特点:
1.与对应数据位全1的数&与运算,可取得该位本身的值;
2. 与对应数据位全0的数&与运算,可将该位置0。

3|(或运算)

运算规则:两数相或,只要有一个为1,就为1,同时为0才为0.

public static void main(String[] args) {
    System.out.println("3|4运算的结果是 :"+(3|4));
    //3 =======>011
	//4 =======>100
	//或结果为:7(111)
}

特点:
1.与对应数据位全1的数|与运算,则将该位置1

4 ~ (取反运算)

运算规则:取反运算是只针对一个数据进行操作,如果二进制是0,则取反为1,如果二进制是1,则取反为0;

public static void main(String[] args) {
    System.out.println("~3运算的结果是 :"+(~3));
    //3 =======>11
	//非结果为:0(00)
}

三.位移运算符

1.<<(向左位移)

运算规则:针对二进制,转换成二进制后向左移动,低位用0补齐,高位溢出则丢弃,遇到符号位,符号位保持不变,不参与运算

public static void main(String[] args) {
         System.out.println("2<<3运算的结果是 :"+(2<<3));
         //打印的结果是:   2左移3位的运算的结果是 :16
     }

特点:1.左移1位相当于*2。左移几位相当于乘几个2.

1.>> (有符号向右位移)

运算规则:针对二进制,转换成二进制后向右移动,遇到符号位,符号位保持不变,不参与运算,低位溢出并舍弃,并用符号位补丢失的高位[即负数补1,正数补0];

public static void main(String[] args) {
         System.out.println("4>>2运算的结果是 :"+(4>>2));
         //打印的结果是:   4右移两位的运算的结果是 :1
     }

特点:1.右移1位相当于/2。右移几位相当于/几个2.

3.>>> (无符号向右位移)

运算规则:无符号右移运算是将操作数所有二进制值逐位右移若干位,包括最高位符号位,也跟着右移,低位溢出并舍弃,高位补0;
注意,无符号右移(>>>)中的符号位(最高位)也跟着变;

public static void main(String[] args) {
         System.out.println("4>>>2运算的结果是 :"+(4>>2));
         //打印的结果是:   4无符号右移两位的运算的结果是 :1
     }

例如
00000100>>>2 结果00000001
00000111>>>2 结果00000001
10000100>>>2 结果00100001
10000111>>>2 结果00100001

四.leetcode经典位运算题目

1. 位1的个数

题目:

编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 ‘1’ 的个数。(leetcode191)

解题思路:

观察一下 n 与 n-1 这两个数的二进制表示:对于 n-1 这个数的二进制来说,相对于 n 的二进制,它的最末位的一个 1 会变成 0,最末位一个 1 之后的 0 会全部变成 1,其它位相同不变。
比如 n = 8888,其二进制为 10001010111000
则 n - 1 = 8887 ,其二进制为 10001010110111
通过按位与操作后:n & (n-1) = 10001010110000
也就是说:通过 n&(n-1)这个操作,可以起到消除最后一个1的作用。
所以可以通过执行 n&(n-1) 操作来消除 n 末尾的 1 ,消除了多少次,就说明有多少个 1 。

public static int findOne(int n)
    {
        int countOne = 0;
        while (n > 0){
            count++;
            n = (n - 1) & n;
        }
        return countOne;
    }

2. 2的幂

题目:

给定一个整数,编写一个函数来判断它是否是 2 的幂次方(LeetCode231)。

解题思路:
  1. 首先,先来分析一下 2 的次方数的二进制写法:
    0:0
    2:10
    4:100
    8:1000

    仔细观察,可以看出 2 的次方数都只有一个 1 ,剩下的都是 0 。根据这个特点,只需要每次判断最低位是否为 1 ,然后向右移位,最后统计 1 的个数是否为1即可判断是否是 2 的次方数。

public static boolean isPowOfTwo(int n) {
		int CountOne = 0;
		while(n > 0) {
			CountOne += (n & 1);
			n >>= 1;
		}
		return CountOne == 1;
	}
  1. 再次观察,例如16的二进制是10000,15的二进制是01111,则两数相与结果为0.也就是说,若n是2的次幂,则n & (n-1) 必然得0.代码如下
public static boolean isPowOfTwo(int n) {
		return ((n&(n-1))==0);
	}

3. 数字范围按位与

题目:

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

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

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

解题思路:

由于是按位与,那么某位一旦出现0,结果该位肯定是0。所以只需要考虑m,n都是1的位置。那么直接从高位开始,往低位走,直到遇到该位对应的数字不相等,将其后及其以后的数为都置为0,即为[m,n]之间所有的数字按位与的结果。

1.那么则先让俩数向右移位,然后判断两数是否相等,并记录移动的位数,当移动至两数相等时,则找到了两数左边的公共部分,则再让该数末尾补零至与原来的数长度相同即可。

public static int solution(int m , int n) {
		int i = 0;
		while(m != n ) {
			m = m>>1;
			n = n>>1;
			i++;
			
		}
		
		return (m<<i);
	}

2.也可以先建立一个 32 位都是 1 的最大数,然后每次向左移一位,比较 m 和 n 是否相同,不同再继续左移一位,直至相同,然后把 m 和 int_max相与就是最终结果。


	public static int solution2(int m,int n) {
		  int int_max = Integer.MAX_VALUE ;//2147483647
	        while ((m & int_max) != (n & int_max)) {
	        	int_max <<= 1;
	        }
	        return m & int_max;
	    }

4.颠倒二进制位(leetcode190)

题目:

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

示例 1:
输入: 00000010100101000001111010011100
输出: 00111001011110000010100101000000
解释: 输入的二进制串 00000010100101000001111010011100 表示无符号整数 43261596,
因此返回 964176192,其二进制表示形式为 00111001011110000010100101000000。

解题思路:

对于这道题,我们只需要把要翻转的数从右向左一位位的取出来,如果取出来的是1,我们将结果res左移一位并且加上1;如果取出来的是0,我们将结果res左移一位,然后将要翻转的数n右移一位即可,代码如下

public static int reverseBit(int n) {
		int res = 0;
		for (int i = 0; i < 32; ++i) {
            if ((n & 1) == 1) {
                res = (res << 1) + 1;
            } else {
                res = res << 1;
            }
            n = n >> 1;
        }
        return res;
	}

你可能感兴趣的:(编程算法)