Leetcode 260:只出现一次的数字 III(最详细的解法!!!)

给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。

示例 :

输入: [1,2,1,3,2,5]
输出: [3,5]

注意:

  1. 结果输出的顺序并不重要,对于上面的例子, [5, 3] 也是正确答案。
  2. 你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?

解题思路

类似问题

Leetcode 136:只出现一次的数字(最详细的解法!!!)

Leetcode 137:只出现一次的数字 II(最详细的解法!!!)

首先想到的解法就是像137问题一开始提出的那样,使用dict

class Solution:
    def singleNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        nums_dict = {}
        for num in nums:
            nums_dict[num] = nums_dict.get(num, 0) + 1
		
        result = list()
        for key, val in nums_dict.items():
            if val == 1:
                result.append(key)
                
        return result

但是如果使用O(1)的空间复杂度要怎么做呢?我们很快想到通过位运算。一个非常简单的思路就是我们按照136问题中的方法,直接对每个nums的元素做xor,最后我们得到的结果就是两个单一元素abxor。因为ab不相同,所以它们之间必定会存在至少一个bit不同,也就是说a xor b的结果中至少有一个bit1。我们从这么多的bit中挑选出一个,然后其余位置为0,那我们就构成了这样的一种mask。例如

00...100

这样的maskab中元素与元素的话,必定有一个结果是0,另外一个结果是mask,这样我们就将ab给分开了。那么问题就变成了,怎么构建这样的mask?我们使用一个简单的策略就是a xor b的最右边的1作为flag。那要怎么得到最右边的1呢?这就涉及到补码的概念,我们知道负数在计算机中使用补码表示的,也就是反码加1

num:5
原码:0101
反码:1010
---------
num:-5
补码:1011

那么我们通过num&-num就可以取出最右边的1了。现在我们就可以遍历nums然后,通过mask就可以判断nums中的那些元素的右边第一位是1(根据上面例子),我们将这些数分成一类,将右边第一位是1的数分成为另外一类,并且我们的ab也就被分到不同的组中。这两组数字的个数不一定相同,但是最后一定是可以相互通过xor消除,最后只剩ab

from functools import reduce
from operator import xor
class Solution:
    def singleNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        mask = reduce(xor, nums)
        mask &= -mask
        result = [0]*2
        for num in nums:
            if num & mask:
                result[0] ^= num
            else:
                result[1] ^= num

        return result

我将该问题的其他语言版本添加到了我的GitHub Leetcode

如有问题,希望大家指出!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值