位运算的应用

位操作符

  • 与运算

& 与运算 两个位都是 1 时,结果才为 1,否则为 0,如
1 0 0 1 1
& 1 1 0 0 1
------------------------------
1 0 0 0 1

  • 或运算

| 或运算 两个位都是 0 时,结果才为 0,否则为 1,如
1 0 0 1 1
| 1 1 0 0 1
------------------------------
1 1 0 1 1

  • 非运算

~ 取反运算,0 则变为 1,1 则变为 0,如
~ 1 0 0 1 1
-----------------------------
0 1 1 0 0

  • 异或运算

^ 异或运算,两个位相同则为 0,不同则为 1,如
1 0 0 1 1
^ 1 1 0 0 1
-----------------------------
0 1 0 1 0

  • << 左移运算

<<  左移运算,向左进行移位操作,高位丢弃,低位补 0

int a = 8;
a << 3;
移位前:0000 0000 0000 0000 0000 0000 0000 1000
移位后:0000 0000 0000 0000 0000 0000 0100 0000

  • >> 右移运算

>> 右移运算,向右进行移位操作,对无符号数,高位补 0,对于有符号数,高位补符号位

unsigned int a = 8; a >> 3;

移位前:0000 0000 0000 0000 0000 0000 0000 1000

移位后:0000 0000 0000 0000 0000 0000 0000 0001 ​

int a = -8; a >> 3;

移位前:1111 1111 1111 1111 1111 1111 1111 1000

移位前:1111 1111 1111 1111 1111 1111 1111 1111

  • >>> 无符号右移运算

>>> 运算符将用0填充高位;>> 运算符用符号位填充高位,没有<<< 运算符

常见的位运算问题

  • 位操作交换两数

利用异或运算

第一步:a ^= b ---> a = (a^b);

第二步:b ^= a ---> b = b^(a^b) ---> b = (b^b)^a = a

第三步:a ^= b ---> a = (a^b)^a = (a^a)^b = b

//普通操作
    void swap(int &a, int &b) {
        a = a + b;
        b = a - b;
        a = a - b;
    }
    void swap(int &a, int &b){
        int t;
        t = a;
        a = b;
        b = t;
    }
    //位与操作
    void swap(int &a, int &b) {
        a =a ^ b;
        b =a ^ b;
        a =a ^ b;
    }
  • 判断奇偶数

只要根据数的最后一位是 0 还是 1 来决定即可,为 0 就是偶数,为 1 就是奇数。即原数据和1进行与运算来判断最后一位是 0 还是 1.

import java.util.Scanner;

public class t7 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int a = sc.nextInt();
        if((a & 1) == 1) System.out.println("奇数");
        else System.out.println("偶数");
    }
}
  • 位操作交换符号

交换符号将正数变成负数,负数变成正数

整数取反加1,正好变成其对应的负数(补码表示);负数取反加一,则变为其原码,即正数

import java.util.Scanner;

public class t7 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int a = sc.nextInt();
        System.out.println(~a + 1);
    }
}
  • 求绝对值

整数的绝对值是其本身,负数的绝对值正好可以对其进行取反加一求得,即我们首先判断其符号位(整数右移 31 位得到 0,负数右移 31 位得到 -1,即 0xffffffff),然后根据符号进行相应的操作

int abs(int a) {
  int i = a >> 31;
  return i == 0 ? a : (~a + 1);
}
  • 进行高低位交换 

给定一个 16 位的无符号整数,将其高 8 位与低 8 位进行交换,求出交换后的值 

34520的二进制表示:
10000110 11011000

将其高8位与低8位进行交换,得到一个新的二进制数:
11011000 10000110

只要将无符号数 a>>8 即可得到其高 8 位移到低 8 位,高位补 0;将 a<<8 即可将 低 8 位移到高 8 位,低 8 位补 0,然后将 a>>8 和 a<<8 进行或操作既可求得交换后的结果。 

import java.util.Scanner;

public class t7 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int a = sc.nextInt();
        System.out.println(Integer.toString(a, 2));
        System.out.println(Integer.toString((a >> 8) | (a << 8), 2));
    }
}
  •  位操作统计二进制中 1 的个数

方法一:

1左移i位后和原数据进行与运算,若结果等于1左移i位后的值,则该位数是1

int cnt1 = 0;
        for (int i = 0;i < 32;i++){//二进制最高32位
            if((n&(1<<i))==(1<<i)){//1左移i位与原数据进行与运算,若等于1左移i位的数,则第i位是1
                cnt1++;
            }
        }

方法二:

原数据右移i位后和1进行与运算并且高位用0补位,若结果为1,则该位是1

int cnt2 = 0;
        for (int i = 0;i < 32;i++){
            if(((n >>> i) & 1) == 1){//原数据右移i位,并高位用0补位,和1进行与运算,结果与1比较
                cnt2++;
            }
        }

方法三:

(原数据-1)& 原数据  此操作消去目前最高位的1,直至原数据变为零时,所有的1统计完毕

例:11001010   

第一次消去结果00110010

第二次消去结果00001100

第三次消去结果00000001

第四次消去结果00000000

int cnt3 = 0;
        while(n!=0){//原数据变为0,结束条件
            n = (n - 1) & n;//原数据-1,和原数据进行与运算,消去最高位的1    1100 --  0010  消去最高位的1
            cnt3++;
        }
  • 判断是否为2的次幂

2的次幂的特点

1

10

100

必有一个1,利用异或判断1的个数是否为1即可 

  • 3的n次方

例:求3的13次方 n = 13,二进制表示为1101,

那么 3 的 13 次方可以拆解为:   3^1101 = 3^0001 * 3^0100 * 3^1000。

可以通过 & 1>>1 来逐位读取 1101,该位为1时将该位代表的乘数累乘到最终结果

& 1  用来判断该位是否为1

>>1  移位,逐位读取

import java.util.Scanner;

public class t7 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int sum = 1;
        int ts = 3;
        while(n!=0){
            if((n & 1) ==1){
                sum = sum * ts;//判断位数为1时,累乘到最终结果
            }
            ts = ts * ts;
            n = n >> 1;//移位
        }
        System.out.println(sum);
    }
}
  • 找出不大于N的最大的2的幂指数

例如 N = 19,那么转换成二进制就是 00010011,那么我们要找的数就是,把二进制中最左边的 1 保留,后面的 1 全部变为 0。即我们的目标数是 00010000。

1、找到最左边的 1,然后把它右边的所有 0 变成 1

位运算的应用 

2、把得到的数值加 1,可以得到 00100000即 00011111 + 1 = 00100000。

3、把 得到的 00100000 向右移动一位,即可得到 00010000,即 00100000 >> 1 = 00010000。

n |= n >> 1;
n |= n >> 2;
n |= n >> 4;

通过把 n 右移并且做运算,可以实现把最左边的1的右边所有的0变成1的操作

int findN(int n){
    n |= n >> 1;
    n |= n >> 2;
    n |= n >> 4;
    n |= n >> 8 // 整型一般是 32 位,上面我是假设 8 位。
    return (n + 1) >> 1;
}
  • 找出重复的数

1-1000这1000个数放在含有1001个元素的数组中,只有唯一的一个元素值重复,其他均只出现一次,每个数组元素只能访问一次,设计一个算法,将他找出来。

主要利用异或运算,A ^ A = 0 ,  A ^ 0 = 1

先构造一个1^2^3^4...^1000  ,与原数据异或运算,则可以消去不重复的元素值,留下一个重复的值

import java.util.Random;

public class t1 {

    public static void main(String[] args) {
        int N = 1000;
        int []arr = new int[N];
        for(int i=0;i<N-1;i++){
            arr[i]=i+1;
        }
        arr[N-1] = new Random().nextInt(N-1);//在末尾产生一个随机数
        int index = new Random().nextInt(N-1);
        swap(arr,index,arr.length-1);//将随机数随即放入数组内,构成题目所需要求
        for (int i=0;i < N;i++){
            System.out.println(arr[i]);
        }
        int x1=0;
        for (int i = 0;i<N-1;i++){
            x1=x1^(i+1);
        }//构造1^2^3^4^5^6....
        for (int i=0;i<N;i++){
            x1=x1^arr[i];//与数组异或起来,最终只剩重复数字
        }
        System.out.println(x1);
    }
    public static void swap(int []arr,int index,int n){
        int t;
        t=arr[index];
        arr[index]=arr[n];
        arr[n]=t;
    }
}
  • 找出落单的数

一个数组除了某一个数字之外,其他的数字都出现了两次。请找出这个数

异或运算,将数组中每一个元素进行异或运算,消去出现两次的数字,留下落单的数字

import java.util.Random;

public class t2 {
    public static void main(String[] args) {
        int N = 11;
        int []arr = new int[N];
        for(int i=0;i<N/2;i++){
            arr[i]=arr[N/2+i]=i+1;
        }
        arr[N-1] = new Random().nextInt(100);//在末尾产生一个随机数

        for (int i=0;i < N;i++){
            System.out.println(arr[i]);
        }
        int x1=0;
        for (int k=0;k<N;k++){
            x1=x1^arr[k];//与数组异或起来,成对的全部消掉,只留下单个的
        }
        System.out.println(x1);
    }

}
  • 奇偶位互换

将一个整数的奇偶位互换

例:9的二进制表示位1001   奇偶互换0110 对应十进制为6

方法:

原数据和0xaaaaaaaa(1010 1010 1010 ...)进行与运算,取出原数据的偶数位

原数据和0x55555555(0101 0101 0101 ...)进行与运算,取出原数据的奇数位

将偶数位右移一位,奇数位左移一位,然后进行异或运算

import java.util.Scanner;

public class t5 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        System.out.println(Integer.toString(n,2));
        int ou = 0xaaaaaaaa & n; //(1010 1010 1010 ... ) & n 此项和原数据与运算,取出原数据的偶数位
        int ji = 0x55555555 & n; //(0101 0101 0101 ... ) & n 此项和原数据与运算,取出原数据的奇数位
        System.out.println(Integer.toString(ou,2));
        System.out.println(Integer.toString(ji,2));
        int ou1 = ou >> 1;//偶数位右移一位
        int ji1 = ji << 1;//奇数位左移一位
        System.out.println(Integer.toString(ou1 ^ ji1,2));
        System.out.println(ou1 ^ ji1);//异或运算
    }
}
  • 出现K次与出现1次

数组中只有一个数出现了1次,其他的数都出现了K次,请输出只出现了1次的数

K个相同的k进制数做不进位加法,结果为0

  • 进制的转化

1.手工取余法

2. Integer.toString(n,)

import java.util.Scanner;

public class t6 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n1 = sc.nextInt();
        float m = sc.nextFloat();
        zheng(n1);
        System.out.println();
        fu(m);
    }
    public static void zheng(int n){
        while(n != 0){
            System.out.print(n % 2);//整数取余
            n/=2;
        }
    }
    public static void fu(float m){
        StringBuilder sb = new StringBuilder("0.");//字符串拼接
        //乘2挪整
        while (m > 0){
            float f = m * 2;
            if(f >= 1){
                sb.append("1");
                m = f - 1;
            }
            else {
                sb.append("0");
                m = f;
            }
            if (sb.length() > 34){//二进制最大32位,算上符号位0.一共34
                System.out.println("ERROR");
                return;
            }
        }
        System.out.println(sb.toString());
    }

}

上一篇:HashMap集合存储学生对象并遍历


下一篇:【蓝桥杯】基础练习(Java)