题目:一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
示例 1: 输入:nums = [4,1,4,6] 输出:[1,6] 或 [6,1]
思路1 :初始化一个集合,遍历判断新的元素是否在集合中,如果不在就添加,在则删除。最后剩下的就是一出现过一次的。
问题:时间复杂度是O(n),空间复杂度是O(n)。 空间复杂度不满足要求
def singleNumbers(nums):
repeat = set()
for i in range(len(nums)):
if nums[i] not in repeat:
repeat.add(nums[i])
else:
repeat.remove(nums[i])
return list(repeat)
因为要求空间复杂度是O(1),所以考虑位运算。
思路2:利用位运算异或和与 具体看代码中分析过程
复杂度:时间复杂度是O(n),空间复杂度是O(1)
PS:① 位中的&运算 具有位过滤作用 ② 3^4=4 3^3-0
'''
思路2:
解决找到一个数组只有出现过一次的数字
⭐ python位运算中的按位异或运算符: 两个对应的二进位相异时为1 相同时为0 故 a ^ a = 0 0 ^ b = b
⭐ 但是本题的难点是nums中有两个只出现过一次的数字 但是异或只能解决求一个数字。 故问题转移为如何将这两个数字分到不同的子数组? 然后再执行异或操作
如何将两个数字以及众多对数字分到不同的子数组
⭐ ① 针对不同数字对: 首先,我们并不在乎哪几个数字对必须在一起,但是一对数字必须在一起。我们如何做到让所有数字对分成两派(可以想到0和1 0或非0),且相同两个数字对分到一起呢?
按位与其实是有过滤作用,即只要和1相与保持原值 和 0相与 则变0。如 有两对数值分别为 0 1 1 0、0 1 1 0 和 1 0 0 1、 1 0 0 1 分别和 0 1 0 0 只有一位1的相与
会得到 0 1 0 0 和 0 0 0 0 两个答案.
⭐ ② 针对两个只出现过一次的数字: 根据 ①的经验,我们选取的只有一位1的二进制数只要可以让两个数分到两个不同子数组中即可。 如 两个数分别是 1 0 1 1 和 0 1 1 1 可以选取 0 1 0 0或者
1 0 0 0 二者的结果都是不同的。所以我们可以求得第一个不同位的位置 从而来设定来将所有值分组的数m。
复杂度: 时间复杂度是O(n),空间复杂度是O(1)
'''
# def singleNumbers2(nums):
def singleNumbers2(nums):
x, y, n, m = 0, 0, 0, 1
for num in nums: # 1. 遍历异或
n ^= num
while n & m == 0: # 2. 循环左移,计算 m 0 1 0 1 & 0001
m <<= 1
for num in nums: # 3. 遍历 nums 分组
if num & m:
x ^= num # 4. 当 num & m != 0
else:
y ^= num # 4. 当 num & m == 0
return x, y # 5. 返回出现一次的数字
路虽远,行则将至。事虽难,做则必成 。