要设计一个时间复杂度为 O(n) 且尽量节省额外空间的算法来找到班级中唯一一个出现一次的数字,我们可以使用位运算的技巧。具体地,我们可以利用 XOR(异或)运算的性质来解决这个问题。
XOR 运算的性质
- 交换律:a⊕b=b⊕a
- 结合律:(a⊕b)⊕c=a⊕(b⊕c)
- 自反性:a⊕a=0
- 零的性质:a⊕0=a
由于除了一个数字之外,所有数字都恰好出现了两次,我们可以将所有数字进行异或运算。成对出现的数字会相互抵消(因为 a⊕a=0),最后剩下的就是只出现一次的数字。
算法步骤
- 初始化一个变量
result为 0。 - 遍历所有同学手上的数字,对每个数字进行异或运算并更新
result。 - 遍历结束后,
result就是那个只出现一次的数字。
XOR运算的性质
我们已经提到了一些XOR运算的关键性质,现在让我们更详细地看看它们:
-
交换律:
a ^ b = b ^ a- 这意味着XOR运算的顺序不重要。
-
结合律:
(a ^ b) ^ c = a ^ (b ^ c)- 这允许我们分组或重新排列XOR运算的顺序。
-
自反性:
a ^ a = 0- 任何数与自身进行XOR运算的结果都是0。
-
零的性质:
a ^ 0 = a- 任何数与0进行XOR运算的结果都是它本身。
-
唯一解:如果
a ^ b = c,那么a ^ c = b和b ^ c = a。- 这意味着如果我们知道两个数和它们的XOR结果,我们可以找到第三个数。
应用XOR运算找到唯一数字
现在,让我们看看如何使用这些性质来找到那个只出现一次的数字。
假设我们有一个数组 numbers,其中除了一个数字之外,所有数字都恰好出现了两次。
-
初始化:我们创建一个变量
result并将其初始化为0。这个变量将用于存储最终的唯一数字。 -
遍历数组:我们遍历数组中的每个数字,并对
result和当前数字执行XOR运算。python复制代码 result = 0
for number in numbers: result ^= number -
理解XOR过程:
- 当我们遇到成对出现的数字时(比如
a和a),result ^ a ^ a会变成result ^ 0(因为a ^ a = 0),最终结果是result本身(因为x ^ 0 = x)。 - 当我们遇到只出现一次的数字时(比如
b),result ^ b会将b添加到result中(因为x ^ b就是将x的每一位与b的相应位进行XOR运算)。
- 当我们遇到成对出现的数字时(比如
-
结束遍历:当我们遍历完整个数组后,
result将包含那个只出现一次的数字,因为所有成对出现的数字都已经相互抵消了。
示例
假设数组是 [2, 3, 2, 3, 1]:
- 初始时,
result = 0。 - 遍历到第一个
2,result = 0 ^ 2 = 2。 - 遍历到第一个
3,result = 2 ^ 3 = 1(二进制:010 ^ 011 = 001)。 - 遍历到第二个
2,result = 1 ^ 2 = 3(二进制:001 ^ 010 = 011)。 - 遍历到第二个
3,result = 3 ^ 3 = 0(二进制:011 ^ 011 = 000)。 - 遍历到
1,result = 0 ^ 1 = 1。
最终,result 是 1,这就是那个只出现一次的数字。
总结
通过使用XOR运算,我们能够高效地找到那个只出现一次的数字,而不需要额外的空间(除了一个用于存储结果的变量)。这种方法的时间复杂度是 O(n),其中 n 是数组的长度,非常符合题目的要求。