class Solution {
/**
* 时间复杂度 O(n), 空间复杂度 O(1)
* 方法:异或
* 2 ^ 3 ^ 2 ^ 1 ^ 4 ^ 3 = 2 ^ 2 ^ 3 ^ 3 ^ 1 ^ 4 = 1 ^ 4
* 异或得到结果,值为 1 的位是两个只出现一次的数不相同的位,
* 例: 3 ^ 5 = 011 ^ 101 = 110
* 作用:通过获取一个不相同的位,通过 & 操作能区分这两个数
* 例: 011 ^ 010 = 1, 101 ^ 010 = 0,010 可以区分 3 和 5
* @param nums
* @return
*/
public int[] singleNumbers(int[] nums) {
// nums数组整体异或的结果,最终是两个出现一次的数异或的结果
// 因为数组中出现两次的数,即两个相同的数异或的结果是0
int k = 0;
for (int num : nums) {
k ^= num;
}
// 一个能区分两个出现一次的数的标志位
int mask = 1;
/*
也可以使用 k & (-k) 快速得到最小的标志位
例 3 = 0,011 -3 = 1,101 (补码表示法)
3 & -3 = 0,011 & 1,101 = 0,001
*/
while ((k & mask) == 0){
mask <<= 1;
}
/*
num & mask 将两个出现一次的数分到两个不同的组
而出现两次的数,即两个相同的数由于位的值都相同所以
一定会分到相同的组里,最后异或的结果便是每个组各自的
出现一次的数。
*/
int a = 0, b = 0;
for (int num : nums) {
if ((num & mask) == 0){
a ^= num;
} else {
b ^= num;
}
}
return new int[]{a, b};
}
}
11-13
342
12-06
291
01-09
648