数组里很多数出现了两次,三个数出现一次,找出这三个数
思路
异或运算解决奇偶性问题。
所以把所有数进行异或即可得到一个只跟这三个数有关的值,记为flag
更精确的:
- 如果flag某一位是0,表示这三个数中这一位,1的个数为偶数
- 如果是1,表示对应位上,1的个数位奇数
那么,如果用flag与这三个数相异或,所得的三个结果记为r1,r2,r3,那么每一位上都只有偶数个1(r1^r2^r3==flag^a^flag^b^flag^c==0)。
我们就可以通过这个性质解决问题了,可以通过r1,r2,r3最低bit的位置来找出这三个数中的一个,记为b,因为一定有一个最低bit位与其余两个不同。
找到b了之后,我们让flag = flag^b,也就是a^c的值,这个时候我们随便找一位为1的位置去判断就可以分辨出a和c了
lowerbit函数表示取最低一个比特,最优雅的方式如下
public int lowBit(int x) {
return x & (-x);
}
最终代码如下
public void solution(List<Integer> nums) {
int len = nums.size();
int flag = 0;
//对所有的数进行异或,最终结果就是a^b^c
for (int i = 0; i < len; i++) {
flag ^= nums.get(i);
}
int symbol = 0;
//获得a,b,c中的第一个区分点,即flag^a flag^b flag^c中最低位不同的那一位,用于接下来找出不同的这一个数
for (int i = 0; i < len; i++) {
symbol ^= lowBit(flag ^ nums.get(i));
}
int b = 0;
//通过判断flag^x的最低位是否等于symbol来区分出3个数中的一个,记为b
for (int i = 0; i < len; i++) {
if (lowBit(nums.get(i) ^ flag) == symbol) {
b ^= nums.get(i);
}
}
//获得a^c并把b添加到序列里去
flag ^= b;
nums.add(b);
int a = 0, c = 0;
//把集合中的数分成两半,一半是跟a^c有同样最低位的数,一半是没有的。这两半中一半有a,一半有c
for (int i = 0; i < nums.size(); i++) {
if ((nums.get(i) & lowBit(flag)) == 0) {
a ^= nums.get(i);
} else {
c ^= nums.get(i);
}
}
System.out.println(MessageFormat.format("{0} {1} {2}", a, b, c));
}