异或法找数
一. 给出一个数组,数组中仅有一个数出现奇数次,找出这个数
示例: 找到数组 [ 1, 2, 2, 1, 1 ] 中出现奇数次的数(1)
解决思路:
- 首先了解位运算的异或运算,例如:101 ^ 111 = 010(相同位上数字相同异或是0,相同位数字不同是1)
- 异或运算是满足交换律的,例如:a ^ b ^ c 与 b ^ c ^ a 的结果相同,(a ^ b)^ c 和 a ^ (b ^ c)相同
- 对于示例数组开始从左到右开始异或运算,1 ^ 2 ^ 2 ^ 1 ^ 1 = 1(偶数次出现的数字都被异或为0了,而0与任何数字 X 异或后都是 X)
- 此时就找到了要找的数字
代码示例:
public class Test {
public static void main(String[] args) {
int arr[] = { 1, 1, 2, 2, 2, 3, 3 };
int res = 0; // 0和任意的X异或都是X
for (int i: arr) {
res ^= i;
}
System.out.println(res);
}
}
得到的结果是:2
二. 给出一个数组,数组中有两个数出现奇数次(两个数不相同),找出这两个数
示例: 找到数组 [ 1,2,2,1,1,3,3,3 ] 中出现奇数次的两个数(2 和 3)
解决思路:
- 假设有一个数组[ a, b, a, a, b, b, c, c ] 此数组内部从第一个开始做异或运算到最后一个,会得出结果 result= a ^ b
- 由于a 和 b 是不同的,所以步骤1得到的result必定不为0。找到不同的某个位(假设此时的a为7(二进制为 111),b为6(二进制为 110),那么显然他们第3位是不同的)
- 此时根据不同的位进行把原数组进行划分,例如步骤2中知道了第3位是不同的,此时把原数组中所有第3位为1的归纳为一类(命名为A类),第3位不为1的归纳为另外一类(命名为B类),显然a(111)此时是属于A类的,b(110)属于B类
- 假设选出A类的所有数进行逐个异或操作,就能找到A类中出现次数为奇数次的数(a),把他记为 resA
- 把步骤4中得到的resA(resA此时存的为a)与result进行异或,过程为 a ^ a ^ b = b,此时 a 与 b 均找出
public class Test {
public static void find(int a[])
{
int ero = 0;
for (int i : a)
{
ero ^= i;
}
int rightOne = ero & (~ero + 1);
int resA = 0; // 用来存其中一个值
for (int cur: a){
if ((cur & rightOne) == 0) {
resA ^= cur;
}
}
System.out.println(resA + "和" + (ero ^ resA));
}
public static void main(String[] args) {
int arr[] = { 6, 6, 7, 7, 7, 5, 5, 5 };
find(arr);
}
}
结果为: 7 和 5