1. 找单独的数题目解析
题目关键点:
- 班级中有 n 个学生,手中分别持有一个数字卡片。
- 除一个数字外,其余数字均出现两次。
- 我们需要在 O(n) 时间复杂度下,找出唯一的、不成对的数字。
- 额外空间尽量少,用异或操作是最佳选择。
思路解析
异或的特性:
- 相同数字异或结果为 0: x⊕x=0
- 任意数字与 0 异或结果为自身: x⊕0=x
- 异或运算满足交换律和结合律: a⊕b⊕c=c⊕a⊕b
因此,多个数字异或后,顺序无关。
算法核心:
- 利用异或操作将所有数字进行逐一累积。
- 重复出现的数字会因 x⊕x=0抵消。
- 最终结果就是唯一出现一次的数字。
代码详解
以下是代码及其步骤:
def solution(cards):
unique = 0 # 初始异或值设为0
for card in cards: # 遍历所有卡片
unique ^= card # 将当前卡片数字与累积值进行异或
return unique # 最终累积值即为唯一数字
代码解析:
-
初始值: 定义变量
unique = 0,表示当前的异或累积结果。 -
逐步异或: 遍历列表中的每个数字
card,将其与unique进行异或运算。- 如果数字出现两次,则其异或结果变为
0,对最终结果没有影响。 - 唯一出现一次的数字始终保留在
unique中。
- 如果数字出现两次,则其异或结果变为
-
返回结果: 遍历结束后,
unique即为最终答案。
图解过程
假设输入 cards = [1, 1, 2, 2, 3, 3, 4, 5, 5]:
| 步骤 | 当前卡片 (card) | 累积值 (unique) | 说明 |
|---|---|---|---|
| 初始值 | - | 0 | 初始化为 0 |
| 第一步 | 1 | 0⊕1=1 | 第一个 1 |
| 第二步 | 1 | 1⊕1=0 | 第二个 1 抵消 |
| 第三步 | 2 | 0⊕2=2 | 第一个 2 |
| 第四步 | 2 | 2⊕2=0 | 第二个 2 抵消 |
| 第五步 | 3 | 0⊕3=3 | 第一个 3 |
| 第六步 | 3 | 3⊕3=0 | 第二个 3 抵消 |
| 第七步 | 4 | 0⊕4=4 | 唯一数字 4 |
| 第八步 | 5 | 4⊕5=1 | 第一个 5 |
| 第九步 | 5 | 1⊕5=4 | 第二个 5 抵消 |
| 结果 | - | 4 | 唯一数字 |
测试样例
测试代码如下:
if __name__ == "__main__":
# 测试样例1
print(solution([1, 1, 2, 2, 3, 3, 4, 5, 5]) == 4) # True
# 测试样例2
print(solution([0, 1, 0, 1, 2]) == 2) # True
# 测试样例3
print(solution([7, 3, 3, 7, 10]) == 10) # True
样例结果:
- 输入
[1, 1, 2, 2, 3, 3, 4, 5, 5],输出4。 - 输入
[0, 1, 0, 1, 2],输出2。 - 输入
[7, 3, 3, 7, 10],输出10。
复杂度分析
时间复杂度:
- 遍历列表一次,时间复杂度为 O(n)。
空间复杂度:
- 只使用了一个变量
unique,空间复杂度为 O(1)。
两个常数的异或值计算
两个常数的异或值可以通过按位异或运算来计算。异或运算(XOR)是对二进制位进行操作的逻辑运算,规则如下:
- 如果对应位的数字相同,则结果为 0。
- 如果对应位的数字不同,则结果为 1。
例如,计算 5⊕3:
-
将数字转为二进制:
- 5 的二进制表示为 0101
- 3 的二进制表示为 0011
-
按位异或:
- 0⊕0=0
- 1⊕0=1
- 0⊕1=1
- 1⊕1=0
结果是 0110
-
将结果转回十进制: 0110 的十进制是 6。
因此,5⊕3=6
代码示例
在 Python 中,可以使用 ^ 运算符计算异或:
a = 5
b = 3
result = a ^ b
print(result) # 输出 6
这种按位异或适用于任何两个整数,计算过程相同。