260 Single Number III
Given an array of numbers nums
, in which exactly two elements appear only once and all the other elements appear exactly twice. Find the two elements that appear only once.
Example:
Input: [1,2,1,3,2,5] Output: [3,5]
Note:
- The order of the result is not important. So in the above example,
[5, 3]
is also correct. - Your algorithm should run in linear runtime complexity. Could you implement it using only constant space complexity?
分析:
思路一:HashSet
顺序遍历数组,如果set中没有该数字,则放入该数字,如果有该数字,则移除该数字。遍历之后set中只剩下不重复的两个元素
public int[] singleNumber(int[] nums) {
Set<Integer> set = new HashSet<>();
for (int i = 0; i < nums.length; i++) {
if(set.contains(nums[i])) {
set.remove(nums[i]);
}else {
set.add(nums[i]);
}
}
int[] res = {0,0};
Iterator<Integer> iterator = set.iterator();
res[0] = (Integer)iterator.next();
res[1] = (Integer)iterator.next();
return res;
}
思路二:位操作
XOR:异或,当位不同的时候为1,相同的时候为0。如果两个数异或之后不为0,说明两个数不同。两个数异或的结果上如果有一位是1,则这个1一定来自于这两个数中的一个。
有一串二进制数为X,X&(-X) 的结果是 X 最右边的 1 的位置。详细见https://www.cnblogs.com/yzxag/p/12668034.html
求解思路:如果我们可以把数组内的元素进行分类,一类里面包含一个不同的元素和若干对相同的元素,另一类里面有另一个不同的元素和若干对相同的元素。这样,我们分别对两类元素相异或,得到的结果就是两个只出现了一次的元素。
下面对数组内的元素进行分类:
- 首先把所有的元素相异或,相同的元素互相消去,结果为两个不同元素的异或值,存为tmp。
- tmp 的二进制一定有一个或一个以上的 1,且这个 1 一定来自于两个数字中的一个。用
tmp & (-tmp)
选出一个 1,再次遍历数组,如果该位置上为 1 分入类别一,否则分为类别二。对两个类别中的元素进行异或,得到两个单独的数字。
public int[] singleNumber(int[] nums) {
int tmp = 0;
for (int num : nums) // 得到所有数字异或的结果
tmp ^= num;
int dif = tmp & (-tmp); // 选取出最右边的一个 1
int[] res = new int[2];
for (int num : nums) // 再次遍历,如果该位置上为 1,则进行异或,得到其中一个数字
if ((num & dif) != 0)
res[0] ^= num;
res[1] = tmp ^ res[0]; // tmp 为两个不同元素的异或结果,即 tmp = res[0]^res[1],
return res; // res[1] = res[0]^res[1]^res[0]
}