【算法】第一节课之位运算
1. 通过位运算来交换两个数。
public static void m1() {
int a = 10, b = 20;
a = a ^ b;
b = a ^ b;
a = a ^ b;
System.out.println(a);
System.out.println(b);
}
运行结果为:a=20,b=10。成功完成交换。
原理:位运算之异或。
关键步骤:
- a = a ^ b , 即 a = 10 ^ 20 , b = 20
- b = a ^ b , 即 a = 10 ^ 20 , b = 10
- a = a ^ b , 即 a= 20 , b = 10
注意:只有指向不同地址的变量才能这样交换,如果指向同一个地址,因为同一个地址的数据相互异或直接就变成了0.
2. 提取异或结果最右边的1
假设 eor = a ^ b
,并且 eor != 0
, 那么eor的二进制表达式(32位)中必然有一个位置上是1,那么我们如何提取出最右边的1呢?
int rightOne = eor & (~eor + 1);
只需通过这一步便可提取出最右边的1
解释:
- 假设 eor = 1010111100,那么 ~eor = 0101000011。
- 那么 ~eor + 1 = 0101000100
- 所以 rightOne 就等于 0000000100
这样就提出了eor最右边的1
3. 实践
数组中仅有两个数出现奇数次,其余的都出现偶数次,找出出现奇数次的两个数。
public void test() {
//假设两个出现过奇数次的数代号分别为a和b
int[] arrs = {0, 2, 0, 0, 3, 2, 4, 4, 3, 5};
int eor = 0;
for (int arr : arrs) {
eor ^= arr;
}
//此时 eor=a^b
//找到 eor最右边的1的位置(原码与上补码)
int rightOne = eor & (~eor + 1);
//此时可以将原数组分为两组(假设rightOne中1的索引位index)
//1.index位上为1的数
//2.index位上为0的数
//而a,b必定分属于这两组
int onlyOne=0;
for (int arr : arrs) {
//可以选择等于0或等于1
if((arr ^ rightOne)==0){
//最终可得onlyOne=a 或 onlyOne=b
onlyOne ^= arr;
}
}
System.out.println(onlyOne+" "+ (eor^onlyOne));
}