在问 · 算法题解

260. 只出现一次的数字 III

中等位运算异或分组力扣原题 ↗

题目描述

输入一个整数数组 nums。其中正好有两个数各只出现一次,其他每个数都恰好出现两次。

找出这两个只出现一次的数,放进数组返回,顺序不限。

算法必须是线性时间,并且只能使用常数大小的额外空间。

示例 1
输入:nums = [1,2,1,3,2,5]
输出:[3,5]

解释:3 和 5 各只出现一次,返回 [5,3] 也算对。

示例 2
输入:nums = [-1,0]
输出:[-1,0]

解释:两个数都只出现一次。

示例 3
输入:nums = [0,1]
输出:[1,0]

解释:0 和 1 各只出现一次,这里按 [1,0] 的顺序返回。

数据范围

  • 2 <= nums.length <= 3 * 10^4
  • -2^31 <= nums[i] <= 2^31 - 1
  • 除了那两个只出现一次的数,其余每个数都恰好出现两次。

解题思路:异或分组

先把所有数异或:成对的数抵消,得到 xor = a ^ b。因为 a ≠ b,xor 至少有一位是 1,用 xor & -xor 取出最低位的 1,记为 low——a 和 b 在这一位上必然不同。按“这一位是否为 1”把所有数分成两组:a、b 各在一组,而成对的数一定整对落在同一组。两组分别异或,就各剩下一个单独数。

为什么正确

同一个数在两次出现时这一位相同,所以成对的数总在同一组里互相抵消;每组恰好只含 a、b 中的一个。

复杂度分析

时间 O(n);空间 O(1)。

易错点

xor 为 -2^31 时,C++ 中对 int 取负会溢出,需用 unsigned 计算 xor & (~xor + 1);分组判断要写 (x & low) != 0,而不是 == 1,因为 low 不一定是 1。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        xor = 0
        for x in nums:
            xor ^= x
        low = xor & -xor
        a = b = 0
        for x in nums:
            if x & low:
                a ^= x
            else:
                b ^= x
        return [a, b]

载入编辑器,自己跑一遍 →

其他解法

哈希表计数用 O(n) 空间直接统计;异或分组利用两个答案在某一二进制位上不同,把数组分成两组分别异或,只用 O(1) 空间。

解法 2:哈希表计数 时间 O(n),空间 O(n)

用哈希表统计每个数出现的次数,再遍历哈希表,把出现恰好一次的数放入答案。

最直接,不需要位运算技巧,也适用于其他出现次数的变体。需要 O(n) 额外空间,不满足题目的常数空间要求。

from collections import Counter

class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        cnt = Counter(nums)
        return [x for x, c in cnt.items() if c == 1]

位运算 专题的其他题目

查看 位运算 全部题目 →