news 2026/10/8 7:57:20

深入解析「第一个唯一偶数元素」:两趟遍历 + 哈希计数的标准解法(codeforces-go 实战)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深入解析「第一个唯一偶数元素」:两趟遍历 + 哈希计数的标准解法(codeforces-go 实战)
  • 科学计算

【免费下载链接】codeforces-go

算法竞赛模板库 by 灵茶山艾府 💭💡🎈

项目地址:https://gitcode.com/GitHub_Trending/co/codeforces-go
点击查看免费下载

本文以 LeetCode 第 178 场双周赛第一题 first-unique-even-element 的官方题解为骨架,结合开源算法模板库 codeforces-go 中该题的 Go 实现、测试用例与测试驱动,系统讲解"两趟遍历 + 哈希计数"这一基础而重要的计数范式。读完本文,你将掌握该题在 Python/Java/C++/Go 四种语言下的标准写法、复杂度推导,以及 codeforces-go 仓库如何用RunLeetCodeFuncWithFile驱动题解完成自动化评测。

一、题目背景与题意

该题出自 LeetCode 第 178 场双周赛 A 题,题目链接与函数签名可参见 a_test.go 中的注释。核心诉求可以概括为:

给定整数数组nums,返回第一个既是偶数、又在数组中恰好出现一次的元素;若不存在这样的元素,返回-1。

从仓库中的 a.txt 可以看到两个典型的评测样例:

[3,4,2,5,4,6] 2 [4,4] -1
  • 样例一:4出现了两次(索引 1、4),不满足"恰好出现一次";2是偶数且只出现一次,且是满足条件的第一个元素,故答案为2。
  • 样例二:4出现两次,没有满足条件的元素,返回-1。

题目有两个并列的筛选条件:数值为偶数(x % 2 == 0)与出现次数恰为 1。两个条件缺一不可,这是整个解法的核心约束。

二、核心思路:两趟遍历 + 哈希计数

官方题解(见 README.md)给出的思路非常朴素且高效:

  1. 第一趟遍历:遍历nums,用哈希表(也可以改用数组/计数桶)统计每个数的出现次数。一个可选的优化是:只对偶数x计数,因为奇数元素永远不可能是答案,提前过滤可以节省哈希表的键空间。
  2. 第二趟遍历:再次按原始顺序遍历nums,检查nums[i]是否满足nums[i] % 2 == 0且cnt[nums[i]] == 1,一旦命中立即返回nums[i]。
  3. 兜底逻辑:遍历结束仍未命中,返回-1。

为什么"第一趟"与"第二趟"缺一不可?

  • 单趟遍历无法判定"恰好出现一次"——必须等完整统计完成后,才知道某个数在全数组中的出现次数;
  • 第二趟必须按原始顺序扫描,才能保证返回的是"第一个"满足条件的元素,而不是任意一个;
  • 先统计后查询的次序,保证了结果与元素在数组中的相对顺序严格一致。

三、四语言标准实现

以下四段实现完整继承自官方题解 README.md,可直接作为各语言下的标准答案。

Python3(哈希表Counter)

class Solution: def firstUniqueEven(self, nums: List[int]) -> int: cnt = Counter(nums) for x in nums: if x % 2 == 0 and cnt[x] == 1: return x return -1

Java(HashMap+merge)

class Solution { public int firstUniqueEven(int[] nums) { Map<Integer, Integer> cnt = new HashMap<>(); for (int x : nums) { if (x % 2 == 0) { cnt.merge(x, 1, Integer::sum); } } for (int x : nums) { if (x % 2 == 0 && cnt.get(x) == 1) { return x; } } return -1; } }

C++(unordered_map)

class Solution { public: int firstUniqueEven(vector<int>& nums) { unordered_map<int, int> cnt; for (int x : nums) { if (x % 2 == 0) { cnt[x]++; } } for (int x : nums) { if (x % 2 == 0 && cnt[x] == 1) { return x; } } return -1; } };

Go(原生map)

func firstUniqueEven(nums []int) int { cnt := map[int]int{} for _, x := range nums { if x%2 == 0 { cnt[x]++ } } for _, x := range nums { if x%2 == 0 && cnt[x] == 1 { return x } } return -1 }

四段实现的关键差异点:

语言计数容器计数写入方式说明
Python3collections.Counter一行完成统计Counter(nums)对全部元素计数,未做偶数过滤
JavaHashMap<Integer,Integer>merge(x, 1, Integer::sum)仅统计偶数,写法最紧凑
C++unordered_map<int,int>cnt[x]++仅统计偶数,注意[]会自动插入默认值 0
Gomap[int]intcnt[x]++仅统计偶数,与仓库源码 a.go 完全一致

小提示:Java 的HashMap与 Go 的map都只对偶数做计数(第一趟里的if x%2 == 0过滤),而 Python 的Counter统计全部元素。前者节省空间,后者代码更短,两者在复杂度量级上一致。

四、复杂度分析

官方题解(README.md)给出的结论:

  • 时间复杂度:$\mathcal{O}(n)$,其中 $n$ 是nums的长度。两趟遍历各消耗 $\mathcal{O}(n)$,哈希表的插入与查询期望均为 $\mathcal{O}(1)$,故整体为线性时间。
  • 空间复杂度:$\mathcal{O}(n)$,最坏情况下(所有元素均为偶数且互不相同)哈希表需要存储 $n$ 个键。

若在 Python 中使用Counter(nums),则第一趟对全部 $n$ 个元素计数;其余语言实现只对偶数计数,实际键数量最多为偶数个数,但仍属于 $\mathcal{O}(n)$ 量级。

值得补充的一点:如果题目将数值范围限定得很小(例如 $|x| \le 10^5$),可以把哈希表替换为计数数组/桶,空间退化为 $\mathcal{O}(V)$($V$ 为值域大小),常数更小、无哈希冲突,这也是题解中"用哈希表(或者数组)"这一括注的用意所在。

五、codeforces-go 仓库中的源码级佐证

该题在仓库中的落位是 leetcode/biweekly/178/a/,共三个文件:题解 a.go、测试驱动 a_test.go、用例数据 a.txt。

5.1 题解实现与题解文档完全对齐

a.go 中的firstUniqueEven与 README 的 Go 版本逐字一致:先建map[int]int仅统计偶数频次,再按原序查找第一个频次为 1 的偶数,无果返回-1。文件头部还保留了出题/讲解者的 B 站空间注释,方便追溯讲解视频。

5.2 自动化评测链路:RunLeetCodeFuncWithFile

a_test.go 展示了仓库的通用评测范式——把题解函数与用例文件交给测试工具:

func Test_a(t *testing.T) { if err := testutil.RunLeetCodeFuncWithFile(t, firstUniqueEven, "a.txt", 0); err != nil { t.Fatal(err) } }

其中RunLeetCodeFuncWithFile定义于 leetcode/testutil/leetcode.go,它的工作流程可以拆解为:

  1. 读取用例文件a.txt;
  2. 通过trimSpaceAndEmptyLine剔除空行与首尾空白;
  3. 用反射(reflect.TypeOf(f))取得被测试函数的输入参数个数fNumIn与返回值个数fNumOut;
  4. 按"每fNumIn + fNumOut行一组"切分用例,即一行输入 + 一行期望输出为一组(a.txt中[3,4,2,5,4,6]与2构成一组,[4,4]与-1构成一组);
  5. 逐组调用RunLeetCodeFuncWithExamples执行题解并比对输出。

这种"源码函数 + 文本用例文件"的测试架构(同一套工具还支持类题、多输出、指定用例号、超时检测isTLE等能力,详见 leetcode/testutil/leetcode_test.go),让仓库中上千道题解都能以几乎零模板代码的方式获得回归测试保障——firstUniqueEven本身就是一个最小的可复现示例。

5.3 如何在本仓库运行该题的测试

在仓库根目录执行 Go 测试命令即可复现上述用例:

go test ./leetcode/biweekly/178/a/ -run Test_a -v

若两个样例全部通过,测试会逐条报告Case 1、Case 2的结果。你也可以向 a.txt 追加"输入行 + 期望输出行"的成对数据(需保证总行数能被 2 整除),来扩充自己的边界用例。

六、边界情况与易错点总结

基于解法本身与仓库用例,以下几点值得在实现与自测时重点核对:

  1. 无解返回 -1:[4,4]这类"唯一元素却不唯一"的输入最容易踩坑,务必保留兜底return -1。
  2. "第一个"的语义:第二趟必须按nums原序扫描,不能改为遍历哈希表键(哈希表无序,会破坏"第一个"的约束)。
  3. 偶数判定放在两处:计数阶段只统计偶数可以减小哈希表规模;查询阶段再次校验偶数可保证与计数口径一致(Python 的Counter版本因统计了全部元素,查询阶段尤其不能省略x % 2 == 0)。
  4. 大数组性能:$n$ 规模较大时,两趟线性扫描 + 期望 $\mathcal{O}(1)$ 的哈希操作即为最优复杂度,无需排序(排序会引入 $\mathcal{O}(n \log n)$ 并破坏原序语义)。

七、从一题看一类:计数 + 顺序扫描的通用范式

firstUniqueEven是"频次统计 + 顺序查询"这一双趟范式的典型样本,同类题目往往只是筛选条件不同:

  • 把"偶数"换成"奇数/正数/特定区间",思路不变;
  • 把"恰好出现一次"换成"出现次数为 k",只需把cnt[x] == 1改为cnt[x] == k;
  • 需要"任意一个"而非"第一个"时,第二趟甚至可以省略,直接遍历哈希键即可。

掌握了"先统计、后按序复查"的框架,再配合本仓库 copypasta 中丰富的模板(如计数桶、前缀和等基础数据结构),可以快速迁移到大量"存在性 + 频次"类题目上。

  • 科学计算

【免费下载链接】codeforces-go

算法竞赛模板库 by 灵茶山艾府 💭💡🎈

项目地址:https://gitcode.com/GitHub_Trending/co/codeforces-go
点击查看免费下载

相关推荐

上一篇:Bowtie2高级功能:局部比对与全局比对的应用场景
下一篇:突破硬件调试壁垒:SMU Debug Tool开源方案的底层控制革命

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 7:50:38

GPU物理层与驱动层可靠性实战指南

1. 这不是“AI基建”科普&#xff0c;而是一线工程师的生存手记“AI-Infra”这个词&#xff0c;最近半年在技术会议、招聘JD和投资人PPT里高频出现&#xff0c;听起来像某种高大上的新赛道。但如果你真蹲进一个正在跑千卡集群的AI训练中心&#xff0c;听运维同事凌晨三点在钉钉…

作者头像 李华
网站建设 2026/10/8 7:46:57

ponytail:数字人发型系统的跨引擎参数协议

1. “ponytail”不是网络热词&#xff0c;而是一个被严重误读的视觉符号系统最近在多个内容平台刷到“ponytail”被当作新晋网络热词反复推送——配图是扎马尾辫的二次元角色、AI生成的少女侧脸、甚至某品牌洗发水广告截图。但作为连续七年深度参与UI动效设计、三维角色绑定与A…

作者头像 李华