# 只出现一次的数字(260)
# 题目
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。
# 示例
输入: [1,2,1,3,2,5]
输出: [3,5]
# 注意
- 结果输出的顺序并不重要,对于上面的例子, [5, 3] 也是正确答案。
- 你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?
# 算法
# Map
使用Map()
来存储每个数字出现的次数
export const singleNumber = (nums) => {
const map = new Map();
nums.forEach((item) => map.set(item, map.has(item) ? map.get(item) + 1 : 1));
const res = [];
for (const [k, v] of map) {
if (v === 1) res.push(k);
}
return res;
};
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 位运算
我们对所有数字进行异或运算,因为x^x===0
,所以最后的结果是两个只出现一次的数字的异或结果,但我们无法求出这两个数字。此时我们可以思考将nums
分为两个数组,只出现一次的两个数分别出现在两个数组中,这样我们分别对两个数组异或运算就可以求出这两个数字了
这里我们使用x & (-x)
运算(-x
相当于~x + 1
),结果保留二进制中最右边的一个1
:对于数字12
二进制表示为1100
,12 & (-12) = 4
,即二进制100
,保留了最右边一个1
由于异或结果的这一位是1
,说明只出现一次的两个数字的二进制的这一位是不同的,我们对数组中所有数数字与这一位进行按位与运算&
,则可以将数组按照二进制这一位是否为1
分成两个数组,然后我们对每一个数组进行异或运算^
(这里其实简化成了136题),就可得到只出现一次的两个数字
export const singleNumber = (nums) => {
let bitMask = 0;
for (let i = 0; i < nums.length; i++) {
bitMask ^= nums[i];
}
let diff = bitMask & -bitMask;
let x = 0;
for (let i = 0; i < nums.length; i++) {
if ((nums[i] & diff) !== 0) x ^= nums[i];
}
return [x, bitMask ^ x];
};
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12