LeetCode 217. Contains Duplicate 题解:Go 语言哈希表判重的工程化实现与源码解析
2026/9/11 20:42:29 网站建设 项目流程

LeetCode 217. Contains Duplicate 题解:Go 语言哈希表判重的工程化实现与源码解析

【免费下载链接】LeetCode-Go✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go

本篇技术指南以 LeetCode 217 题「Contains Duplicate(存在重复元素)」为核心,基于 LeetCode-Go 仓库中 leetcode/0217.Contains-Duplicate 的官方题解文档与 Go 源码实现,完整讲解哈希表判重的解题思路、Go 代码的工程化细节(map 预分配、逗号 ok 惯用法、提前返回)、时间空间复杂度分析以及仓库内的测试验证方式。读完本文,你将掌握一道面试高频「数组 + 哈希表」入门题的标准化 Go 实现,并能直接复用该模式解决其余判重类题目。

一、题目描述与示例

LeetCode 217 题「Contains Duplicate」是数组与哈希表类别下的经典入门题,原题如下:

Given an array of integers, find if the array contains any duplicates.

Your function should return true if any value appears at least twice in the array, and it should return false if every element is distinct.

即:给定一个整数数组,判断该数组中是否存在重复元素。只要任意一个数值在数组中出现至少两次,函数就返回true;若所有元素均互不相同,则返回false

原文档给出的三个标准示例:

// 示例 1:头部出现重复,返回 true Input: [1,2,3,1] Output: true // 示例 2:元素全部互异,返回 false Input: [1,2,3,4] Output: false // 示例 3:大量重复元素,返回 true Input: [1,1,1,3,3,4,3,2,4,2] Output: true

这三个用例恰好覆盖了三种典型场景:重复出现在数组起始位置全数组无重复重复元素密集且多次出现,是后续验证实现正确性的最小有效测试集。

二、题目大意

这是一道简单题:如果数组里面有重复数字就输出true,否则输出false。表面上是返回布尔值,本质考察的是**在一组数据中快速判断「是否出现过」**的能力——这是哈希表最典型、最基础的应用场景,也是后续大量滑动窗口、双指针、前缀和类题目的前置技能。

三、解题思路:哈希表(Map)判重

原文档给出的解题思路非常精炼:用 map 判断即可。核心思想是一条线性扫描的贪心判定:

  1. 遍历数组,逐个元素检查;
  2. 若当前元素已经存在于 map 中,说明此前出现过,立即判定存在重复;
  3. 若不存在,则将其记录进 map,继续向后扫描;
  4. 扫描完整数组仍未发现重复,则返回false

这种方法的关键优势在于:map 的读写操作平均时间复杂度为 O(1),因此整个算法只需一趟遍历即可完成判定,无需像排序法那样先付出 O(n log n) 的排序代价。

四、仓库 Go 源码实现深度解析

LeetCode-Go 仓库针对该题给出了一个非常简洁且工程化的实现,位于 leetcode/0217.Contains-Duplicate/217. Contains Duplicate.go:

package leetcode func containsDuplicate(nums []int) bool { record := make(map[int]bool, len(nums)) for _, n := range nums { if _, found := record[n]; found { return true } record[n] = true } return false }

代码虽短,却蕴含了三个值得细读的 Go 工程细节。

4.1 预分配 map 容量:make(map[int]bool, len(nums))

创建 map 时显式传入容量len(nums),这是 Go 中典型的性能优化写法。make的第二个参数指定了 map 的初始容量(bucket 数量),当后续插入的元素数量不超过该容量时,map 不会触发扩容(rehash),从而避免扩容带来的额外内存分配与元素重排开销。由于判重场景下 map 最坏会容纳全部 n 个元素,直接用len(nums)作为容量是既合理又省心的选择——一次分配到位,零扩容成本。

作为对比,若写成make(map[int]bool)而不指定容量,Go 会以很小的默认容量创建 map,插入过程中随元素增多会经历多次扩容,虽然时间复杂度量级不变,但常数开销更高。

4.2 逗号 ok 惯用法:if _, found := record[n]; found

Go 语言访问 map 时有两种取值形式:单值形式v := m[k]在键不存在时会返回零值(这里即false),无法区分「键不存在」与「键存在但值为 false」两种状态。因此判重时必须使用双值(comma ok)形式v, found := m[k],其中第二个返回值found明确指出键是否真实存在。

本实现中只关心「是否存在」,不关心已存的值,故用_丢弃第一个返回值。这是 Go 中判断元素是否存在的标准惯用法,几乎所有需要判重的 Go 代码都遵循这一模式。

4.3 提前返回(early return)与短路收益

循环内部一旦发现重复立即return true,不必继续扫描剩余元素。对于重复元素出现在数组前部的输入(如示例 1 的[1,2,3,1],第二个元素扫描到索引 3 即命中),这种提前返回能显著减少不必要的 map 写入操作,是算法效率的常驻优化点。

4.4 对应站点文档与实现一致性

该实现与仓库站点文档 website/content/ChapterFour/0200~0299/0217.Contains-Duplicate.md 中展示的代码完全一致,说明leetcode/目录下的题解源码即为站点文档的权威实现来源,两者保持同步,读者可放心对照学习。

五、复杂度分析

维度复杂度说明
时间复杂度O(n)单趟遍历数组,每次 map 读写平均 O(1)
空间复杂度O(n)map 最多存储 n 个键值对,每个键为int、值为bool

时间上,最坏情况(无重复)需要完整遍历 n 个元素;最好情况(第一个元素即重复)只需常数次操作。空间上,由于make预分配了容量len(nums),内存一次性到位,占用与输入规模线性相关。这也是哈希表判重方案在「以空间换时间」上的典型体现。

六、边界情况讨论

一个健壮的实现应当正确处理以下边界输入:

  • 空数组[]:循环体不执行,直接返回false——空数组不存在任何重复元素;
  • 单元素数组[1]:扫描唯一元素时 map 为空,将其记录后循环结束,返回false
  • 全相同元素[1,1,1]:第二个元素即触发found == true,立即返回true
  • 负数与零int作为 map 键天然支持任意整数值,负数、零均无需特殊处理;
  • 大数组:得益于 4.1 节的容量预分配,即便输入规模很大,也不会在扫描中途触发多次扩容。

上述结论均可由源码结构直接推断:该实现没有任何针对元素取值范围、正负号的假设,对任意[]int输入均成立。

七、测试与验证

7.1 测试用例设计

仓库为该题编写了对应的单元测试 leetcode/0217.Contains-Duplicate/217. Contains Duplicate_test.go,测试结构与 README 中的三个示例一一对应:

qs := []question217{ {para217{[]int{1, 2, 3, 1}}, ans217{true}}, // 示例 1:重复位于头部 {para217{[]int{1, 2, 3, 4}}, ans217{false}}, // 示例 2:全部互异 {para217{[]int{1, 1, 1, 3, 3, 4, 3, 2, 4, 2}}, ans217{true}}, // 示例 3:密集重复 }

测试采用question217/para217/ans217三层结构分别承载「题目整体、入参、期望答案」,并以表格驱动(table-driven)方式组织用例,是 Go 社区推荐的测试风格。测试运行时按【input】:%v 【output】:%v的格式打印每个用例的输入与输出,便于直观核对结果。

7.2 运行测试与覆盖率

在仓库根目录执行如下命令即可运行 leetcode 包下全部测试:

go test ./leetcode/...

如需单独验证本题,可指定包路径:

go test -v ./leetcode/0217.Contains-Duplicate/

仓库还提供了脚本 gotest.sh,其核心命令为:

go test -covermode=atomic -coverprofile=coverage.txt ./leetcode/...

该脚本对 leetcode 全部包一次性生成单一合法的覆盖率文件coverage.txt(仓库根目录可见)。脚本注释中说明:旧的按包逐个-coverprofilecat追加的写法会产生重复的mode: atomic头导致 Codecov 解析失败,Go 1.10+ 后改为一次命令直接产出单个合法 profile。这也是为什么本仓库能在 coverage.txt 中汇总全部题目的覆盖率数据——题解代码与测试共同构成了仓库「100% test coverage」声明的数据基础。

八、延伸:与其他判重方案的对比

作为算法知识延伸,除哈希表外,判重问题还有几种常见思路,各有适用场景(以下为通用算法常识,非仓库实现内容):

  • 排序后相邻比较:先排序再检查相邻元素是否相等。时间复杂度 O(n log n)、空间 O(1)(原地排序),适合对空间敏感且不介意排序开销的场景;
  • 暴力双重循环:O(n²) 时间、O(1) 空间,仅适用于极小规模输入;
  • 值域受限时的布尔数组:若元素取值有明确且有限的范围,可用[]bool代替 map,进一步压缩空间并提升缓存友好性。

对比之下,哈希表方案以 O(n) 时间和 O(n) 空间取得了最优的时间复杂度,且不依赖元素取值范围的任何假设,是通用性最强、面试中最推荐的写法——这也是 LeetCode-Go 仓库为该题选择 map 方案的根本原因。

九、总结

LeetCode 217「Contains Duplicate」虽然是一道简单题,但它浓缩了哈希表判重这一基础模式的完整套路:单趟扫描 + 集合记录 + 命中即返。LeetCode-Go 仓库的实现(源码)在 12 行代码内完成了预分配容量、comma ok 判存在、提前返回三项工程化优化,配合表格驱动的测试用例(测试文件)与覆盖率脚本(gotest.sh),为读者提供了一个可以直接照搬、可验证、可扩展的标准化模板。掌握本题后,可将同样的 map 判重思维迁移到「找第一个重复元素」「判断字符串是否含重复字符」「寻找只出现一次的元素」等一系列变体题目中。

【免费下载链接】LeetCode-Go✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询