题目
给定一个整数数组 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 <= nums.length <= 3 * 104
-231 <= nums[i] <= 231 - 1
除两个只出现一次的整数外,nums 中的其他数字都出现两次
思路
下面两个原则(重要):
- 任何数字与0异或得到数字本身;两个相同的数字异或得0。依照这两个原则,假如一个数组中,有一个元素只出现一次,其他元素均出现两次,那么将数组中所有的数字异或即可得到只出现一次的元素的结果。
- z & (-z)得到的是z最低位为1的数:例如z为001100,那么-z:z首先取反110011,然后加1,得到110100,z和-z相与得到100。
接着说下这道题怎么解:
- 将数组中所有的数进行异或,得到结果z。那么z中的1位表示的是数组中两个不同的数字之间不同的位。因为是两个不同的数,因此一定至少存在一个不同的位。
- 通过z&(-z)取最低位1的数,例如100,结果表示为y。
- y可以将数组分成两个子数组(规模不一定相同),做法是将y和数组中每个数进行相与,如果为0,则说明与y相与的数,和y最高位的1对应的是0;如果为1,则说明与y相与的数,和y最高位的1对应的是1。上述数组中两个不相同的数,一定会划分到不同的子数组中。将子数组中所有的数分别进行异或,两个子数组最终得到的结果分别就是两个不同的数。
时间复杂度:
O
(
N
)
O(N)
O(N)
空间复杂度:
O
(
1
)
O(1)
O(1)
class Solution {
public int[] singleNumber(int[] nums) {
int res = 0;
for(int num : nums){
res = res ^ num;
}
res = res & (-res);
int[] arr = new int[2];
for(int num : nums){
if((num & res) == 0) {
arr[0] ^= num;
}else{
arr[1] ^= num;
}
}
return arr;
}
}