这是 LeetCode 上 2021-10-30 的每日一题:「260. 只出现一次的数字 III」
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. 解答
这道题和136. 只出现一次的数字解法类似。
异或运算:
0
和任何数异或=任何数本身- 任何数和自身异或=
0
- 异或满足交换律、结合律
知道了以上三条,就可以解题了。
总体思路,将nums
的数按照一定规则分成两组,相同的数必然会被分到一组,关键是两个不同的数一定要分在不同组。再每组进行异或,得到的结果就是原数组中两个不同的数,具体如下:
- 首先从0开始,与数组中所有的数进行异或,得到结果
temp
temp
等价为数组中两个不同的数异或的结果- 寻找
temp
中,为1
的最低的位k
。例如[1,2,1,3,2,5]
,得到temp=6
,也就是110
,110
的为1
的最低的位是第1位,则k=10
temp
为110
,是011
和101
异或的结果,为1
的最低位代表两个不同的数,在这一位上不同。那么让两个不同的数,和k
进行与操作,就能分开了- 分组,
011&10
,101&10
,可以将两个不同的数分在两组,其余相同的数肯定会被分在同一组 - 每组分别进行异或运算,得到两个答案
const singleNumber = nums => {
let temp = 0;
const len = nums.length;
for (let i = 0; i < len; i++) {
temp = temp ^ nums[i];
}
// 此时temp是两个不同的数异或的结果
// 寻找k,k是temp最低位为1、其余位是0的二进制数
let k = 1;
while ((temp & k) === 0) k = k << 1;
let [num1, num2] = [0, 0];
for (let i = 0; i < len; i++) {
// 分组,目的是将两个不同的数分开
if (nums[i] & k) {
num1 = num1 ^ nums[i];
} else {
num2 = num2 ^ nums[i];
}
}
return [num1, num2];
};