今天在刷Leetcode题(136. 只出现一次的数字)时发现有使用异或运算提高效率。就简单扩展了下,把其他按位运算也总结了起来。
类型:
按位与(&)
按位或(|)
按位非(~)
按位异或(^)
按位同或(^1)
按位左移(<<)
按位右移(>>)
按位与(&)
理解:同1为1,有0为0(若有一个不为1,则为0)
示例: 2 & 3
2的二进制: 0010
3的二进制:0011
二进制结果:0010
十进制结果:2
按位或(|)
理解:同0为0,有1为1(只要一个为1即为1)
示例: 2 | 3
2的二进制: 0010
3的二进制:0011
二进制结果:0011
十进制结果:3
按位非(~)
理解:0则1,1则0(把内存中的0和1全部取反)
使用not运算时要格外小心,你需要注意整数类型有没有符号。如果not的对象是无符号整数(不能表示负数),那么得到的值就是它与该类型上界的差,因为无符号类型的数是用00到$FFFF依次表示的
示例: ~2
// 转成二进制一定是32位数,不足补0
2的 二进制:00000000000000000000000000000010
~2的二进制:11111111111111111111111111111101
结果:1101 即 -3
按照10进制理解:2的反向就是-3(2,1,0,-1,-2,-3以0和-1中间为对称线,2对称的就是-3)
按位异或(^)
理解:不同为1,相同为0(若某位不同则该位为1, 否则该位为0)
异或运算(⊕)有以下三个性质:
- 任何数和 00 做异或运算,结果仍然是原来的数,即 a⊕0=a。
- 任何数和其自身做异或运算,结果是 00,即 a⊕a=0。
- 异或运算满足交换律和结合律,即 a⊕b⊕a=b⊕a⊕a=b⊕(a⊕a)=b⊕0=b。
示例: 2 ^ 3
2的二进制: 0010
3的二进制:0011
二进制结果:0001
十进制结果:1
应用1:不用临时变量交换值 a = 2, b = 3
int a = 2,b = 3;
a = a ^ b;
b = a ^ b;
a = a ^ b;
System.out.println(a);
System.out.println(b);
输出结果:
3
2
应用2:在其他均元素出现2次的int数组中,找到只出现1次的int元素(见Leetcode题: 136. 只出现一次的数字)。
int[] nums = new int[]{1, 4, 5, 5, 4}
int a = nums[0];
for (int i = 1; i < nums.length; i++) {
a = a ^ nums[i];
}
System.out.println(a);
输出结果:
1
按位同或(^1)
理解:相同为1,不同为0
公式理解:同或运算 = 异或运算 ^ 1
按位左移(<<)
理解:将二进制数左移N位(简单理解为乘以2的N次方)
示例1: 2 << 1 将2左移1位
2的二进制: 0010
2 << 1 二进制结果:0100
十进制结果:4
示例2: 2 << 3 将2左移3位
// 说明:由于2左移3位后,用4位二进制表示不能满足了,改用8位二进制表示。不足补0
2的二进制: 0000 0010
2 << 3 二进制结果:0001 0000
十进制结果:16
按位右移(>>)
理解:将二进制数右移N位(简单理解为除以2的N次方)
示例1: 2 >> 1 将2右移1位
2的二进制: 0010
2 >> 1 二进制结果:0001
十进制结果:1
示例2: 2 >> 3 将2右移3位
2的二进制: 0010
// 因为2的右移最多也就2位,超过右移位数结果就还是右移2位的结果。
2 >> 3 二进制结果:0000
// 十进制理解:右移1位:2 / 2 = 1,再右移1位:1 / 2 = 0;再右移1位:0 / 2 = 2.因为除数位0,所以结果怎么都0了
十进制结果:0