给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,3,2]
输出: 3
示例 2:
输入: [0,1,0,1,0,1,99]
输出: 99
解题思路
这是之前Leetcode 136:只出现一次的数字(最详细的解法!!!)问题的扩展。我们直接使用xor
的方法不行,因为我们这里是三个元素,所以我们无法完成消除。但是我们可以通过dict
记录元素个数,最后判断出现次数为1
的是谁即可。
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
for key, val in nums_dict.items():
if val == 1:
return key
我们如果不是用额外空间怎么做呢?我们可以使用set
得到nums
中所有不重复的元素,通过这些不重复的元素和的三倍减去原来nums
的和,得到的结果就是单个元素的两倍。
class Solution:
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
return (3*sum(set(nums)) - sum(nums))//2
这个问题其实还是可以通过逻辑运算来解决。我们想一想我们之前Leetcode 268:缺失数字(最详细的解法!!!)中的做法,为什么要使用xor
?其本质就是想通过两个元素相互消除。那么我们这个问题其实本质就是希望三个元素相互消除。那么这要怎么做呢?我们不再是通过一个元素记录状态,我们可以通过两个元素来记录状态的转化,例如
a b
0 0 0
1 x 0
2 0 x
3 0 0
首先我们会定义两个变量a
和b
,当遍历nums
的时候,对于重复元素x
,第一次碰到x
的时候,我们会将x
赋给a
,第二次碰到后再赋给b
,第三次碰到就全部消除。赋值和消除的动作可以通过xor
很简单的实现。所以我们就可以写出这样的代码
a = (a^num)
b = (b^num)
但是上面写法忽略了,只有当a
是x
的时候,我们会将0
赋给b
,那要怎么做呢?我们知道如果b=0
,那么b^num
就变成了x
,而x&~x
就完成了消除操作,而此时a=x
,所以第二行写为
b = (b^num) & ~a
同理,我们应该将第一行改为
a = (a^num) & ~b
最后代码如下
class Solution:
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
ones, twos = 0, 0
for num in nums:
ones = (ones^num) & ~twos
twos = (twos^num) & ~ones
return ones
如果我们将问题继续推广成如果输入数组中每个元素出现k次,只有一个元素出现p次,那个出现p次的元素是?
看这篇寻找特定数字问题
reference:
我将该问题的其他语言版本添加到了我的GitHub Leetcode
如有问题,希望大家指出!!!