1. 题目
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。
示例 1:
输入:nums = [1,2,1,3,2,5]
输出:[3,5]
解释:[5, 3] 也是有效的答案。
示例 2:
输入:nums = [-1,0]
输出:[-1,0]
示例 3:
输入:nums = [0,1]
输出:[1,0]
2.思路与代码
这道题需要运用到异或^
异或的性质:
- 相同则0不同则1
- 恒等律:X ⊕ 0 = X
- 归零律:X ⊕ X = 0
- 交换律:A ⊕ B = B ⊕ A
- 结合律:(A ⊕ B) ⊕ C= A ⊕ (B⊕ C)
根据异或的性质,我们可以知道有两个的数相互异或最后等于0
A ^ B ^ C ^ B ^ C ^ D ^ A
= A ^ A ^ B ^ B ^ C ^ C ^ D
= 0 ^ 0 ^ 0 ^ D
= 0 ^ D
= D
当要要查找的数字只有一个时,只需要所有数全部异或即可。 只出现一次的数
同样适用于其余数出现偶数次,找出出现奇数次的数
那当出现奇数次的数有两个怎么求解?
我们依旧将所有数异或:
A ^ B ^ C ^ B ^ C ^ D ^ A ^ E
= A ^ A ^ B ^ B ^ C ^ C ^ D ^ E
= 0 ^ 0 ^ 0 ^ D ^ E
= 0 ^ D ^ E
= D ^ E
最后得出的数是两个剩下的数的异或
此时题目就更改成了:
已知两个数的异或,怎么求出这两个数?
设EOR = D ^ E
,根据异或的性质可得出:D = EOR ^ E
,所以我们只需找到DE其中一个数,就可以找到另外一个数
DE两个数唯一已知的就是两个数不可能是一个数值,所以所求的异或肯定存在一位是1,假设这位数是8则D ^ E = 10000000
,说明DE两个数的第八位是不同的。
剩下的就很简单了:我们只需要将所有的第八位等于1或者0的所有数异或,就能得到D或者E其中一个
关于如何找到DE两个数不同的位数:
下图是关于如何找到右侧第一个1
看左神的视频里学到的orz,属实离谱
代码:
class Solution {
public int[] singleNumber(int[] nums) {
int eor = 0;
for(int tmp:nums){
eor ^= tmp;
}
int rightNum = eor & (~eor + 1);//找到最右侧的1
int eor1 = 0;
for(int tmp:nums){
if((rightNum & tmp) == 0){
eor1 ^= tmp;
}
}
int[] tmp = new int[2];//这个数组没必要,可以直接在nums写
tmp[0] = eor1;
tmp[1] = eor1^eor;
return tmp;
}
}