| 按位或:
参与运算的两数各对应的二进位相或.只要对应的二个二进位有一个为1时,结果位就为1
例如: 1|2 : 0001 | 0010 = 0011
9|5 : 1001 | 0101 = 1101 所以9|5=13
& 按位与:
参与运算的两数各对应的二进位相与.只有对应的两个二进位均为1时,结果位才为1,否则为0
例如: 1&2 : 0001 & 0010 = 0000
9&5 : 1001 & 0101 = 0001 所以9&5=1
^ 按位异或:
参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1
例如: 1^2 : 0001 ^ 0010 = 0011
9^5 : 1001 ^ 0101 = 1100 所以9^5=12
名称 | 符号 | 记忆技巧 |
按位或 | | | 2个中有一个为1,结果为1 |
按位与 | & | 2个中有两个都为1,结果为1 |
按位异或 | ^ | 2个中有两个都不相同,结果为1 |
(1)按位异或可以用来使某些特定的位翻转,如对数10100001的第2位和第3位翻转,可以将数与00000110进行按位异或运算。
10100001^00000110=10100111 //1010 0001 ^ 0x06 = 1010 0001 ^ 6
(2)通过按位异或运算,可以实现两个值的交换,而不必使用临时变量。例如交换两个整数a,b的值,可通过下列语句实现:
a=10100001,b=00000110
a=a^b; //a=10100111
b=b^a; //b=10100001
a=a^b; //a=00000110
(3)异或运算符的特点是:数a两次异或同一个数b(a=a^b^b)仍然为原值a.
(4)快速比较两个值
先让我们来一个简单的问题;
判断两个int数字a,b是否相等,你肯定会想到判断a - b == 0
,但是如果判断a ^ b == 0
效率将会更高.
(5)我们使用异或
来判断一个二进制数中1的数量是奇数还是偶数
例如:求10100001
中1的数量是奇数还是偶数; 答案:1 ^ 0 ^ 1 ^ 0 ^ 0 ^ 0 ^ 0 ^ 1 = 1
,结果为1
就是奇数个1,结果为0
就是偶数个1; 应用:这条性质可用于奇偶校验(Parity Check),比如在串口通信过程中,每个字节的数据都计算一个校验位,数据和校验位一起发送出去,这样接收方可以根据校验位粗略地判断接收到的数据是否有误.
(6)面试题:互换二进制数的奇偶位;
题目:写一个宏定义,实现的功能是将一个int型的数的奇偶位互换,例如6的2进制为00000110
,(从右向左)第一位与第二位互换,第三位与第四位互换,其余都是0不需要交换,得到00001001
,输出应该为9;
思路:我们可以把我们的问题分为三步(难道这也是分治法吗 -。-),第一步,根据原值的偶数位获取到目标值的奇数位,并把不需要的位清零;第二步,根据原值的奇数位获取到目标值的偶数位,并把不需要的位清零;第三步:把上述两个残缺的目标值合并成一个完整的目标值;
public static void main(String[] args) {
int n = 6;
System.out.println(n);
int end = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1);
System.out.println(end);
}
0xAAAA=1010 1010 1010 1010
0x5555=0101 0101 0101 0101
(通过按位与& ,2个相同的才会被保留下来)
6 = 00000110
& 0xAAAA = 0010 取出偶数位
6 = 00000110
& 0x5555 = 0100 取出奇数位
偶数位右移一位,奇数位左移一位(偶数位末尾为0,奇数为末尾可能会有值)
010 >> 1 = 0001 , 0100 << 1 = 1000
两者相加就得到了交换后的数字
1000 | 0001 = 1001
结果就为9
(7)最最常出现的面试题:一个整型数组里除了N个数字之外,其他的数字都出现了两次,找出这N个数字;
比如,从{1, 2, 3, 4, 5, 3, 2, 4, 5}
中找出单个的数字: 1
让我们从最简单的,找一个数字开始;
public static void SingleNumber() {
int[] a = { 1, 2, 3, 4, 5, 3, 2, 4, 5 };
int result = 0;
for (int i = 0; i < a.length; i++) {
result ^= a[i];
}
System.out.println(result);
}
当两对应的二进位相异时,结果为1。结果就会保留只出现一次的数字
比如,从{1, 2, 3, 4, 5, 3, 2, 4, 5,6}
中找出单个的数字: 1 6
同理如果有多个的话,就会出现这2个数的按位异或值
1 ^ 6 = 1 ^ 110 = 111 也就是 7
public static void SingleNumber2() {
int[] a = { 1, 2, 3, 4, 5, 3, 2, 4, 5, 6 };
int result = 0;
for (int i = 0; i < a.length; i++) {
result ^= a[i];
}
System.out.println(result);
}
直接找最低位1,最低为的1一定是这2个数的某一个(相同都为0了),那么将根据这个最低位为1的数,将数组分为二部分,这样就变成了每个数组单独求一个出现一次的数了(重复的肯定在同一个数组中,二个数相同)
public static void SingleNumber2() {
int[] a = { 1, 2, 3, 4, 5, 3, 2, 4, 5, 6 };
int result = 0;
for (int i = 0; i < a.length; i++) {
result ^= a[i];
}
int swap = result & ~(result - 1);// 算出最低位为1的数
int num1 = 0, num2 = 0;
for (int i = 0; i < a.length; i++) {
if ((a[i] & swap) == 0) {
num1 ^= a[i];
}
}
num2 = result ^ num1;
System.out.println(num1 + " " + num2);
}
已知:三个数的低位第一位为1的位置有三种情况,一种就是全相同,一种就是两个不同,一个相同,一种就是三个不同;
比如 第3位是最低位
全相同 | 000100 ^ 001100 ^ 010100 = 011100 | 结果成立 |
两个不同,一个相同 | 000100 ^ 011000 ^ 010000 = 001100 | 结果成立 |
三个不同 | 010000 ^ 010010 ^ 010001 = 010011 | 结果不成立 |
目前无法做
(8)实现一个方法,判断一个正整数是否是2的乘方(比如16是2的4次法,返回True;18不是2的乘方,返回False)
先观察一下
10进制 | 2进制 | 等于2的乘方 |
16 | 10000 | 是 |
32 | 100000 | 是 |
18 | 10010 | 不是 |
30 | 11110 | 不是 |
发现:
等于2的乘方的数,最高位为1,其余都为0.
不等2的乘方的数,最高位为1,其余有其他1.
现在这些数减去1
10进制 | 2进制 | N-1 | 等于2的乘方 | |
16 | 010000 | 15 | 001111 | 是 |
32 | 100000 | 31 | 011111 | 是 |
18 | 010010 | 17 | 010001 | 不是 |
30 | 011110 | 29 | 011101 | 不是 |
发现了没有,15,31的二进制全为1.如果他们与16,32做&运算
结果为 16 & 15 ➨ 010000 & 001111 = 0
31 & 32 ➨ 100000 & 011111 =0
而 18 和 17 做 & 运算
结果为 18 & 17 ➨ 010010 & 010001 = 010000 =16
为什么了?
因为满足2的乘方的数(除了最高为1,其他都为0),减去一后,全部位为1.做&运算全部被消除;而不满足2的乘方的数(除了最高位为1,其他位还有1),减去一后,做&运算不能全部都消除,最少会保留下最高位(即结果不为0)。
代码如下:
private static boolean Ispower(int n) {
return (n & n - 1) == 0;
}
时间复杂度 O(1)
(9)用位运算实现加法,(比如 1 和 2 相加不能用“+”符号, 1 ADD 2 = 3)
比如 2 + 3 = 5
2 3 5
↓ ↓ ↓
10 11 101
2 ^ 3 = 10 ^ 11 = 01 这里求到是没进位的值(还需要进位)
比如 2 ^ 1 = 10 ^ 01 = 11 (这个2个数就没有进位,就不用进位)
2 & 3 = 10 & 11 = 10 这里求到是 进位值
2 & 1 = 10 & 01 = 00 进位为0
那么进位左移一位,即为进位数(2 & 3) << 10 → 10 << 1 → 100
直到进位等于0为止.
进位值 和 没进位的值 做 ^运算 → 这里求到是没进位的值(程序不知道后面有没有进位)
100 ^ 01 = 101
进位值 和 没进位的值(旧) 做 &运算 →这里求到是 进位值
100 ^ 01 = 0
000 << 1 = 0000 → 进位左移一位
进位等于0 说明没有进位了
那么和值就为101 ,也就是这新值。
public static int add(int a, int b) {
int swap = 0;
while (b != 0) {
swap = a ^ b;
b = (a & b) << 1;
a = swap;
}
return swap;
}
参考来源:http://lijinma.com/blog/2014/05/29/amazing-xor/
参考来源:https://blog.csdn.net/kybd2006/article/details/3727218
参考来源:https://blog.csdn.net/Super_Me_Jason/article/details/79707992