位运算简介与相关技巧

本文详细介绍了计算机内存中二进制表示的位运算,包括与、或、异或、左移和右移等运算的基本概念、运算规则、优先级以及在实际编程中的技巧和应用场景,强调了位运算在优化空间需求和提高执行效率方面的价值。
摘要由CSDN通过智能技术生成

1. 概述    

程序中的所有数在计算机内存中都是以二进制的形式储存的。即 0、1 两种状态,位运算就是直接对整数在内存中的二进制位进行操作。由于是底层运算,如果运用得当可以降低空间需求提高执行效率

2. 位运算总览

符号描述运算规则实例
&两位都为1时,结果才为1。

1&1=1

1&0=0

0&0=0

|两位都为0时,结果才为0。

1|1=1

1|0=1

0|0=0

^异或

两位相同为0,不同为1。

常称之为不进位加法。

1^1=0

1^0=1

0^0=0

~0变1,1变0。

~1=0

~0=1

<<左移所有二进制位左移n位,高位舍弃,低位补0。

1<<2=4,此时n=2,

即0001<<2=0100。

>>

右移

(算数右移)

1.对于无符号数,所有二进制位右移n位,高位补0。

2.对于有符号数,所有二进制位右移n位,高位补符号位。

4>>2=1,此时n=2,

即0100>>2=0001。

-10>>1=-5,此时n=1,

即1111 0110>>1=1111 1011。

>>>

无符号右移  

(逻辑右移)

所有二进制位右移n位,高位补0。

5>>>2=1,此时n=2,

即0101>>>2=0001。

3. 位运算优先级

        由于位运算符的优先级比较低,在与别的运算符一起使用时需注意顺序,若不太清楚就加小括号保底。

优先级运算符结合方向
1-,~,++,--从右到左
2*,/,%从左到右
3+,-从左到右
4<<,>>从左到右
5>,<,<=,>=从左到右
6==,!=从左到右
7&从左到右
8^从左到右
9|从左到右

4. 位运算技巧

4.1 按位与运算符 -- ( & ) 

4.1.1 获取某指定位上的数
// 获取24的低四位上的数
// 只需要另找一个数,令tmp的低4位为1,其余位为0

int target = 24;
int tmp = 15;

//   target    11000   
//&  tmp     & 01111
---------------------
//             01000 
target & tmp = 8
4.1.2 判断奇偶
(targrt & 1) == 0
if((targrt % 2) == 0){
    // TODO
}

// 可替换为
if((targrt & 1) == 0){
    // TODO
}
 4.1.3 提取数据的最右侧的1
a & (~a + 1)
 a      110010000
~a      001101111
~a+1    001110000

      a     110010000
& (~a+1)    001110000
-------------------------
            000010000

4.2 按位或运算符 -- ( | )

4.2.1 将一个数据的指定位上置1
target | (1 << index)
// 将第三位上的数置1
int target = 24;
int index = 3;

//   11000  
// | 00100
-------------------
//   11100
target | (1 << index) = 28

4.3 异或运算符 -- ( ^ )

异或运算符满足一下性质:

  • 恒等律:任何值与自身异或结果为0,任何值与0异或结果为其自身。
  • 交换律x^y^z = x^z^y,这意味着异或操作的顺序可以任意交换,结果不会改变。
  • 结合律x^y^z = x^(y^z),这表明可以先计算一部分再与剩余部分进行异或操作。
  • 自反性x^x = 0 和 x^0 = x,这可以用来在某些情况下简化表达式。
4.3.1 不使用额外空间交换两个变量( 慎用 )
void swap(int[] arr, int i, int j){
    arr[i] =  arr[i] ^ arr[j];
    arr[j] =  arr[i] ^ arr[j];
    arr[i] =  arr[i] ^ arr[j];
}

注:切记 i 和 j 不能是同一位置,即两数据是在不同内存区域,否者结果可能会出错。

4.4 左移和右移运算符 -- ( <<,>> )

通常用于平替关于2^n的乘除法

a << 1  <==>   a * 2
a >> 1  <==>   a / 2

5. 位运算运用

5.1 有一种数出现了奇数次,其余数出现了偶数次,求出现了奇数次的那种数。

// arr { 1 1 1 2 2 3 3}
// 循环异或,更具性质 1^1^1^2^2^3^3
// 偶数次异或结果是0    --- --- ---
// 结合律可得        1^  0 ^ 0 ^ 0 = 1
void printOddTimesNum(int[] arr) {
    int eor = 0;
    for (int i = 0; i < arr.length; i++) {
        eor ^= arr[i];
    }
    System.out.println(eor);
}

5.2 有俩种数出现了奇数次,其余数出现了偶数次,求出现了奇数次的俩种数。

// arr { 6   10   6   6   4   4   12   12  3  3 }
//   6      10     4     3    
// 00110  01010  00100 00011
//
// eor = a ^ b = 6 ^ 10 = 01100
//
// rightOne = 00100
// 通过rightOne分成两组
// one   = 6 ^ 6 ^ 6 ^ 4 ^ 4 ^ 12 ^ 12 = 6
// other = eor ^ one =  10

public static void printOddTimesNum2(int[] arr) {
    void printOddTimesNum(int[] arr) {
    int eor = 0;
    for (int i = 0; i < arr.length; i++) {
        eor ^= arr[i];
    }
    // eor = a ^ b

    // eor!=0
    // 提取出最右的1
    int rightOne = eor & (~eor + 1);

    int one = 0;
    for (int i = 0; i < arr.length; i++) {
        // 通过rightOne分成两组
        if ((arr[i] & rightOne) != 0) {  
            one ^= arr[i];
        }
    }
    System.out.println(one + " " + (eor ^ one));
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值