leetcode56.题目描述
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。

解题思路:要求时间复杂度为o(n),空间复杂度O(1),如果没有这些要求,这题很简单,直接用set去重,遍历数字用字典统计个数,输出个数为1 的key,结束。显然空间复杂度并不满足。
别问我为什么会这题,只能告诉你我之前看过,一直觉得位运算的题都很巧妙,没见过确实很难想出来。从本题的初级版本的开始吧。
一个整型数组 nums 里除一个数字之外,其他数字都出现了两次。要求时间复杂度是O(n),空间复杂度是O(1)。
举个例子,比如输入[1,2,1,2,3]需要输出3,这里需要用到位运算异或的性质和异或的交换性
1
异
或
2
异
或
1
异
或
2
异
或
3
=
1
异
或
1
异
或
2
异
或
2
异
或
3
=
3
1异或2异或1异或2异或3=1异或1异或2异或2异或3=3
1异或2异或1异或2异或3=1异或1异或2异或2异或3=3
这里^符号打出来就报错,用中文代替了,可以看出相同的数异或就变成0了,遍历一次两次的数都能为0,最后剩下一个数字和0异或还是本身。代码如下。这里用到reduce高阶函数,对可迭代对象进行逐个的输入到函数中,然后把上次的结果和当前值输入到lambda函数中去,lambda函数是一个异或函数。
from functools import reduce
def find_onetimenum(nums):
res = reduce (lambda x,y:x^y, nums)
return res
有了上面的经验,我们可以将所有的数也遍历异或一次。得到的结果就是剩下的两个次数为1的数a和b,即a^b,光知道这两个数的异或结果,也无法知道a和b分别是多少。
如果能通过这个结果,把这个数组分为两个数组多好,其中一个数组包含a,和若干次数为2的数;另外一个包含b,和剩下次数为2的数。即可实现下图的效果,最后分别再进行一次遍历异或就能分别得到a,b呀

以下图为例子,4和5的异或结果为001,如果把所有数和001求与,结果是不是就两种情况,要么为0,要么为1,结果0的分为一组,1的分为一组,可以完美的把这nums分为两组,而且4和5能分开,次数为2的数也在同一组,因为相同的数对001求与结果肯定相同。

from functools import reduce
class Solution:
def singleNumbers(self, nums: List[int]) -> List[int]:
#获取nums数组a,b的异或结果
res = reduce(lambda x,y:x^y,nums)
h = 1
#找到res最高位数为1的数
while((res&h)==0):
h<<=1
a = 0
b = 0
#分组并异或遍历,得到最终异或
for i in nums:
if i&h:
a ^= i
else:
b ^= i
return [a,b]
