找单独的数 题目解析记录
原题目如下:
问题描述
在一个班级中,每位同学都拿到了一张卡片,上面有一个整数。有趣的是,除了一个数字之外,所有的数字都恰好出现了两次。现在需要你帮助班长小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),并且需要统计数字重复的次数,第一反应就想到了使用哈希表的数据结构。
哈希表
哈希表是根据关键码的值而直接进行访问的数据结构。数组就是一张哈希表。
一般哈希表可以用来快速判断一个元素是否出现在集合里。
Go语言中常用的哈希结构为Map。针对该题目,创建一个map[int]int类型,遍历cards数组,key值为card的数值,value值为出现的次数,遍历该数组的时间复杂度为O(n)。之后访问该map,查询map的时间复杂度为O(1),如果出现value值为1的元素,说明该元素为满足要求的数字。 Go语言实现代码如下:
package main
import "fmt"
func solution(cards []int) int {
countMap := make(map[int]int)
for _, card := range cards {
countMap[card]++
}
for card, count := range countMap {
if count == 1 {
return card
}
}
return -1
}
func main() {
// Add your test cases here
fmt.Println(solution([]int{1, 1, 2, 2, 3, 3, 4, 5, 5}) == 4)
fmt.Println(solution([]int{0, 1, 0, 1, 2}) == 2)
}
运行代码即可成功。
异或运算思路
使用MarsCode AI 中需要一点思路提示的功能,AI给了一种我没有想到的思路,即使用异或运算。使用异或运算的时间复杂度为O(n),但可以大大降低空间复杂度。 代码如下:
func solution(cards []int) int {
result := 0
for _, card := range cards {
result ^= card // 使用异或运算
}
return result
}
可以看到代码十分简洁。这得益于异或运算的性质。
- 任何数与自身异或的结果是0
- 任何数与0异或的结果是它自身
- 异或运算满足交换律和结合律
暴力运算
使用暴力运算当然也可以解决本题,即先对数组进行排序,然后遍历数组找到唯一一个不重复的数字。但是该算法的时间复杂度为O(n log n),不符合题目要求了,这里仅提供一种思路。参考代码如下:
import "sort"
func solution(cards []int) int {
sort.Ints(cards)
for i := 0; i < len(cards); i += 2 {
if i == len(cards)-1 || cards[i] != cards[i+1] {
return cards[i]
}
}
return -1
}
总结
检查重复元素有暴力运算、异或运算和哈希表三种可用的方式。 异或运算适合检查是否出现偶数次,而哈希表则可以检查出具体出现多少次,功能更强,但空间复杂度更高。