存在重复元素 图解题解
数组里有没有重复数字?一个 Set,一次扫描,不用排序,答案就出来了。
就像在一堆快递单号里查是否有重复——与其拿起每张单去和其余每张逐一比对,不如备一个「已见单号本」:扫到一张就先翻本查,没查到再记进去,一旦查到直接喊「重了!」。Set 就是这本随查随记的单号本,每次查和记都是 O(1),整个扫一遍就搞定,不需要任何回头重扫。
这道题到底在问什么
- 输入
- nums = [5,9,3,7,1,8,2,6,4,9]
- 输出
- true(末尾的 9 和前面的 9 重复)
最优解:一步一步想明白
- 3核心就一个集合 seen:来一个数先查再存。查到已存在 = 重复,直接收工;全扫完没查到 = 没有重复。
- 4开始扫描前:集合 seen 是空的,指针 i 还没出发,去找有没有重复。
- 5指针 i 走到下标 0,值是 5。先去集合 seen 里查一下 5 之前出现过没有。
- 6查的结果是:集合 seen 里还没有 5,所以 5 是第一次出现,不算重复。
- 7把 5 放进集合 seen(高亮新增那行),这样以后再遇到 5 就能查出来。继续扫下一个。
- 8指针 i 走到下标 1,值是 9。先去集合 seen 里查一下 9 之前出现过没有。
- 9查的结果是:集合 seen 里还没有 9,所以 9 是第一次出现,不算重复。
- 10把 9 放进集合 seen(高亮新增那行),这样以后再遇到 9 就能查出来。继续扫下一个。
- 11指针 i 走到下标 2,值是 3。先去集合 seen 里查一下 3 之前出现过没有。
- 12查的结果是:集合 seen 里还没有 3,所以 3 是第一次出现,不算重复。
- 13把 3 放进集合 seen(高亮新增那行),这样以后再遇到 3 就能查出来。继续扫下一个。
- 14指针 i 走到下标 3,值是 7。先去集合 seen 里查一下 7 之前出现过没有。
- 15查的结果是:集合 seen 里还没有 7,所以 7 是第一次出现,不算重复。
- 16把 7 放进集合 seen(高亮新增那行),这样以后再遇到 7 就能查出来。继续扫下一个。
- 17指针 i 走到下标 4,值是 1。先去集合 seen 里查一下 1 之前出现过没有。
- 18查的结果是:集合 seen 里还没有 1,所以 1 是第一次出现,不算重复。
- 19把 1 放进集合 seen(高亮新增那行),这样以后再遇到 1 就能查出来。继续扫下一个。
- 20指针 i 走到下标 5,值是 8。先去集合 seen 里查一下 8 之前出现过没有。
- 21查的结果是:集合 seen 里还没有 8,所以 8 是第一次出现,不算重复。
- 22把 8 放进集合 seen(高亮新增那行),这样以后再遇到 8 就能查出来。继续扫下一个。
- 23指针 i 走到下标 6,值是 9。先去集合 seen 里查一下 9 之前出现过没有。
- 24集合里已经有 9 了(高亮那行),说明 9 前面来过一次,这次是第二次——找到重复,立刻返回 true。
- 25整趟扫到下标 6 就提前结束了:9 是第二次出现,所以答案是 true——数组里存在重复元素。
⚠️ 容易写错的地方
✗ 错:用两层循环两两比较判重
✓ 对:用集合一遍扫描判重
两两比较是 O(n²),数据一大就超时;集合查找接近 O(1),整体 O(n)
✗ 错:先把整个数组装进集合,再比长度
✓ 对:边扫边查,撞上重复立刻返回
比长度也能做,但要等全装完;边扫边查能在第一处重复就提前结束,更省
✗ 错:把集合 set 错用成列表 list 来查 in
✓ 对:判重容器一定用 set(或哈希表)
list 的 in 查找是 O(n),套在循环里又退化成 O(n²),集合才是 O(1)
完整代码(Python / C++ / Java)
Python
def containsDuplicate(nums):
seen = set() # 记录见过的值
for x in nums:
if x in seen: # 已经见过 → 重复
return True
seen.add(x) # 没见过 → 存进去
return False # 全程没撞上C++
bool containsDuplicate(vector<int>& nums){
unordered_set<int> seen;
for (int x : nums) {
if (seen.count(x)) return true;
seen.insert(x);
}
return false;
}Java
public boolean containsDuplicate(int[] nums) {
Set<Integer> seen = new HashSet<>();
for (int x : nums) {
if (!seen.add(x)) return true;
}
return false;
}复杂度
时间
O(n)
数组从头扫到尾,每个数的「查 + 存」在集合里都接近常数时间
空间
O(n)
最坏情况(全不重复)集合要装下所有 n 个数
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 存在重复元素 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
集合 seen 里到底存的是什么?+
存的是「到目前为止已经见过的所有不同数值」,只记在不在、不记出现几次也不记顺序。每来一个新数先查它,再决定要不要存。
如果数组本身就没有重复,会怎么走?+
每个数查集合都查不到,于是逐个被存进集合,循环正常走到结尾,最后返回 false。这也是空间最坏的情况,集合装下了全部 n 个数。
除了集合,还有别的解法吗?+
可以先排序再看相邻两个是否相等,时间 O(n log n)、空间 O(1);集合法是时间 O(n)、空间 O(n)。面试里通常先给集合法,被追问省空间再提排序法。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄把 存在重复元素 一步步讲透(全站已上线 905 份讲透,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
把这道题真正拿下
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。