Description:
Given a non-empty array of integers, every element appears three times except for one, which appears exactly once. Find that single one.
Note:
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?
Example 1:
Input: [2,2,3,2]
Output: 3
Example 2:
Input: [0,1,0,1,0,1,99]
Output: 99
思路:
本题在Single Number的基础上略微改变,仍然可以按照方法一求解,对于方法二,只需要改成3*(a+b+c) - (a+b+a+b+a+b+c) = 2 * c,依然可以使用。但由于题目在线性复杂度的要求上提出不使用额外空间,因此重点在于使用位运算的第三种方法。
下面重新分析:
(注:以下方法非本人原创,但许多解释过于简单,有些涉及到真值表或卡诺图的化简,这里尽量用最简单的方法解释)
在原题中,我们采用一个变量one来记录数字a是否出现过,计算one = one ^ a,出现一次就记录数值,出现两次数值清零,以one是否为零能很清晰地判断数字出现的次数。在本题中,如果仍采用这种方法,无法判断当one不为0时,数字出现了一次还是三次。因此采用one来记录出现了一次的数字,用two记录出现了两次的数字,用three记录出现了三次的数字。
-
只有当two为1,并且当前又出现一次数字a时,才表示数字出现了3次,所以有
-
数字出现2次(two==1)只有当上一时刻one为1,并且当前又出现一次数字a,或者当上一时刻two为1,并且当前出现数字不为a时,并且当数字出现了3次之后,需要将two的数值清零,所以有
-
数字出现1次(one==1),只有当上一时刻one为0,且当前又出现一次数字a,同样当数字出现了3次后,需要将one的数值清零
-
接下来还有一个问题,三个更新公式的先后顺序如何?首先由于one、two都要依赖当前three的数值来判断当前是否清零,所以three的更新应该在最前面;其次,two的更新依赖于上一时刻的one,所以two的更新应该在one的前面。
-
题目要求找出只出现过一次的数字,即one==1。
总体代码如下:
def singleNumber(self, nums: List[int]) -> int:
one = two = 0
for a in nums:
three = two & a
two = (two & ~a) | (one & a) & (~three)
one = one ^ a & (~three)
# 假如特殊的数不确定出现了一次还是两次
# return one | two
# 假如特殊的数确定只出现了一次
return one