找单独的数
问题描述
在一个班级中,每位同学都拿到了一张卡片,上面有一个整数。有趣的是,除了一个数字之外,所有的数字都恰好出现了两次。现在需要你帮助班长小C快速找到那个拿了独特数字卡片的同学手上的数字是什么。
要求:
- 设计一个算法,使其时间复杂度为 O(n),其中 n 是班级的人数。
- 尽量减少额外空间的使用,以体现你的算法优化能力。
测试样例
样例1:
输入:
cards = [1, 1, 2, 2, 3, 3, 4, 5, 5]
输出:4
解释:拿到数字 4 的同学是唯一一个没有配对的。
样例2:
输入:
cards = [0, 1, 0, 1, 2]
输出:2
解释:数字 2 只出现一次,是独特的卡片。
样例3:
输入:
cards = [7, 3, 3, 7, 10]
输出:10
解释:10 是班级中唯一一个不重复的数字卡片。
约束条件
- 1 ≤ cards.length ≤ 1001
- 0 ≤ cards[i] ≤ 1000
- 班级人数为奇数
- 除了一个数字卡片只出现一次外,其余每个数字卡片都恰好出现两次
解题思路
题目要求找出在数组中只出现一次的数字,其他数字都恰好出现两次。利用位运算的 异或操作 可以在 O(n) 时间复杂度内解决这个问题。异或操作的特点是:a ^ a = 0 和 a ^ 0 = a。在数组中,所有成对出现的数字异或后会抵消为 0,剩下的就是只出现一次的数字。
代码
def solution(inp):
# Edit your code here
n = len(inp) # 获得全部数字的个数
ans = inp[0] # 先将答案定为数组第一个数字
for i in range(1, n): # 从第二个数字开始遍历整个数组
ans = ans ^ inp[i] # 使用异或操作,两个一样的数字异或后为0,最后会只剩下只有一个的数字
return ans
if __name__ == "__main__":
# Add your test cases here
print(solution([1, 1, 2, 2, 3, 3, 4, 5, 5]) == 4)
print(solution([0, 1, 0, 1, 2]) == 2)
关键代码解释
-
ans = inp[0]: 初始化ans为数组的第一个元素。之后会用ans来存储最后的结果。 -
ans = ans ^ inp[i]: 遍历数组的每个数字,使用 异或运算 来抵消所有成对出现的数字。由于成对的数字异或后会变成 0,最后ans只会保留那个没有配对的数字。
时间和空间复杂度
-
时间复杂度:O(n),因为只遍历了一次数组。
-
空间复杂度:O(1),只用了常数空间来存储结果。
扩展:如何找到数组中只出现一次的数字,当其他数字都出现了三次
这是在其他数字出现两次的情况下的一个扩展,这里其他数字出现了三次,这样的话就不能用异或操作来做了,因为这样无论如何都不能抵消掉三个相同的数字。 可以使用位运算来解答此题。可以通过统计所有数字每一位的1的出现次数,若某一位1的总数不是3的倍数,那么独特数字在该位上是1。 扩展代码如下:
def solution(inp):
result = 0
for i in range(32): # 对每一位(从第 0 位到第 31 位)进行计算
bit_sum = 0
for num in inp:
bit_sum += (num >> i) & 1 # 统计第 i 位的1的数量
if bit_sum % 3 != 0: # 如果第 i 位的1的总数不是 3 的倍数
result |= (1 << i) # 说明独特数字在该位上有 1,更新到结果中
# 考虑 Python 整数的有符号处理
if result >= 2**31:
result -= 2**32
return result
-
for i in range(32): 遍历每一个二进制位(32 位整数)。 -
bit_sum += (num >> i) & 1: 计算所有数字在第i位上的 1 的数量。 -
if bit_sum % 3 != 0: 如果bit_sum不是 3 的倍数,说明该位上的 1 是由只出现一次的数字贡献的。 -
result |= (1 << i): 将第i位上的 1 更新到result中。 -
最后一步处理 Python 整数的有符号情况。