D

当前:LC217 · 存在重复元素 · 首次出现于 Day 2 · 路径:顶栏「56天打卡」→ 点击 LC 题号 → 逐题动画

数组哈希集合去重

LC217 · 存在重复元素

给定一个整数数组 nums,如果任意值出现至少两次,返回 true;如果每个元素都不同,返回 false。

这题不是让你找出所有重复数字,而是只要判断“有没有重复”。

示例
输入 nums = [1,2,3,1]
输出 true

解释:数字 1 出现了两次。

step 1

为什么暴力比较慢

先展示暴力法:每个数字都和后面的数字比较,时间复杂度 O(n²)。

如果数组很长,前面的数字会反复回头找很多次;比较次数会像双层循环一样增长。
step 2

为什么用 set

不回头反复比较,而是把已经见过的数字放进 set。

每次扫描新数字,只问一句:它以前出现过吗?

core idea

核心概念区:set 是登记册

set 是“已见过数字登记册”

不记录出现次数

只关心某个数字是否出现过

Go 里可以用 map[int]struct{} 模拟 set

animation

动画实验台:传送带 + 安检登记台

1 / 10
左侧:数组传送带 [1,2,3,1]i 准备就位
1
0
2
1
3
2
1
3
中间:当前扫描指针 i / 检查口
等待第一个数字进入检查口
右侧:set 登记册set = {}
空登记册
当前步骤说明
初始化:数组 [1,2,3,1] 出现,set = {}

从左到右扫描数组,set 记录已经见过的数字。

动画:传送带就位,右侧 set 登记册还是空的。

Go 代码同步高亮
01func containsDuplicate(nums []int) bool {
02 seen := make(map[int]struct{})
03
04 for _, num := range nums {
05 if _, ok := seen[num]; ok {
06 return true
07 }
08 seen[num] = struct{}{}
09 }
10
11 return false
12}
solution

Go 题解区

Go 代码同步高亮
01func containsDuplicate(nums []int) bool {
02 seen := make(map[int]struct{})
03
04 for _, num := range nums {
05 if _, ok := seen[num]; ok {
06 return true
07 }
08 seen[num] = struct{}{}
09 }
10
11 return false
12}
2 行 · 初始化 set 登记册

seen := make(map[int]struct{}) 高亮时,右侧 set 登记册初始化为空。

5 行 · 查询登记册

if _, ok := seen[num]; ok 高亮时,动画表现为“查询登记册”。ok 为 true 就说明以前见过。

6 行 · 重复命中,提前结束

return true 高亮时,动画表现为“重复命中,提前结束”。不用再看后面的数字。

8 行 · 加入登记册

seen[num] = struct{}{} 高亮时,动画表现为“加入登记册”。struct{}{} 只占集合位置,不额外表达次数。

11 行 · 扫完没命中

return false 高亮时,用另一个输入 [1,2,3,4] 展示“扫完没命中”。

return false 分支:输入 [1,2,3,4]
1234
扫完没命中,最后高亮 return false。
invariant

正确性解释

不变量:扫描到 nums[i] 之前,set 中保存的是 nums[0...i-1] 里所有出现过的数字。

如果 nums[i] 在 set 中,说明它之前出现过,返回 true。
如果 nums[i] 不在 set 中,把它加入 set,继续扫描。
如果扫描完还没有命中,说明不存在重复元素。
cost

复杂度区

时间复杂度 O(n):每个数字只扫描一次。
空间复杂度 O(n):最坏情况下所有数字都不同,set 要保存 n 个数字。
pitfalls

常见误区区

  1. 1.命中重复后不需要继续扫。
  2. 2.set 不是记录次数,只记录是否出现过。
  3. 3.map[int]bool 可以写,但 map[int]struct{} 更像集合,且不额外存 bool。
  4. 4.排序也能做,但会改变数组顺序,时间复杂度通常是 O(n log n)。
interview

面试回答区

30 秒回答模板
这题我会用哈希集合。遍历数组时,用 set 记录已经出现过的数字。每次遇到一个数字,先判断它是否已经在 set 中。如果在,说明重复出现,直接返回 true。如果不在,就加入 set。遍历结束还没有发现重复,就返回 false。时间复杂度 O(n),空间复杂度 O(n)。