问题描述
在一个班级中,每位同学都拿到了一张卡片,上面有一个整数。有趣的是,除了一个数字之外,所有的数字都恰好出现了两次。现在需要你帮助班长小C快速找到那个拿了独特数字卡片的同学手上的数字是什么。
要求:
- 设计一个算法,使其时间复杂度为 O(n),其中 n 是班级的人数。
- 尽量减少额外空间的使用,以体现你的算法优化能力。
问题分析
在一个班级中,每位同学都持有一个卡片,上面有一个整数。除一个数字外,所有的数字都恰好出现两次。我们需要在 O(n) 的时间复杂度内找出那个唯一出现一次的数字。并且要求尽量减少额外空间的使用。根据给定的条件,班级人数为奇数,除了一个数字外其他数字都出现了两次。
解决思路
根据题目的要求,我们需要在 O(n) 时间内找到唯一的数字,并且空间复杂度需要控制在 O(1)(即不使用额外的空间)。这提示我们可以使用 异或(XOR)运算 来解决这个问题。
异或运算的性质
- 交换律:
a ^ b = b ^ a
- 结合律:
(a ^ b) ^ c = a ^ (b ^ c)
- 任何数与0异或,结果是数本身:
a ^ 0 = a
- 任何数与自身异或,结果是0:
a ^ a = 0
利用上述性质,假设我们对所有数字进行异或操作,因为相同的数字会互相抵消(即 a ^ a = 0
),所以最终剩下的结果就是那个唯一出现一次的数字。
步骤
- 初始化一个变量
result
为 0。 - 遍历所有卡片上的数字,对
result
进行异或操作。 - 最后,
result
的值就是唯一的那个数字。
时间复杂度和空间复杂度
- 时间复杂度:每个数字都参与一次异或操作,因此时间复杂度是 O(n),其中 n 是卡片的数量。
- 空间复杂度:我们只使用了一个额外的变量
result
,因此空间复杂度是 O(1)。
Golang 代码实现
package main
import "fmt"
func findUniqueNumber(cards []int) int {
result := 0
for _, card := range cards {
result ^= card
}
return result
}
func main() {
cards1 := []int{1, 1, 2, 2, 3, 3, 4, 5, 5}
cards2 := []int{0, 1, 0, 1, 2}
cards3 := []int{7, 3, 3, 7, 10}
fmt.Println("Unique number in cards1:", findUniqueNumber(cards1)) // 输出:4
fmt.Println("Unique number in cards2:", findUniqueNumber(cards2)) // 输出:2
fmt.Println("Unique number in cards3:", findUniqueNumber(cards3)) // 输出:10
}
代码解析
findUniqueNumber(cards []int)
:该函数遍历卡片数字数组,通过异或操作得到唯一的数字。- 主函数
main()
中,测试了多个样例,分别输出唯一数字。
测试样例
- 输入:
cards = [1, 1, 2, 2, 3, 3, 4, 5, 5]
输出:4
- 输入:
cards = [0, 1, 0, 1, 2]
输出:2
- 输入:
cards = [7, 3, 3, 7, 10]
输出:10
UML 设计
在算法实现中,我们可以使用 UML 绘制 活动图(Activity Diagram) 来表示算法的执行流程。
Activity Diagram (活动图)
活动图用于表示算法的执行过程,我们可以通过以下步骤来展示该算法:
- 初始化一个
result = 0
。 - 遍历每一个卡片数字并对
result
进行异或操作。 - 最后返回
result
。
解释:
Start
和End
标识了活动图的开始和结束。Initialize result = 0
表示初始化操作。For each card in cards
是一个循环,每次从卡片列表中取出一个数字进行异或运算。- 最后返回
result
,这个结果就是唯一的数字。
通过这个活动图,我们能够直观地理解异或算法的执行流程。
总结
通过本题的求解,我们展示了如何利用异或运算快速地找到唯一的数字。该算法在时间复杂度和空间复杂度上均得到了优化,符合 O(n) 时间复杂度和 O(1) 空间复杂度的要求。通过 UML 活动图,我们可以清晰地表示出该算法的流程,帮助更好地理解和学习该算法。