news 2026/9/14 4:31:49

LeetCode-Go 题解:206. Reverse Linked List 反转单链表的迭代实现与源码剖析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LeetCode-Go 题解:206. Reverse Linked List 反转单链表的迭代实现与源码剖析

LeetCode-Go 题解:206. Reverse Linked List 反转单链表的迭代实现与源码剖析

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

本篇文章围绕 LeetCode 第 206 题 "Reverse Linked List"(反转单链表),以 LeetCode-Go 仓库中该题目的官方题解文档与 Go 源码为主线,讲解其迭代式反转算法的核心思路、逐行实现细节、时间复杂度分析,并结合仓库内的测试用例与structures工具包,展示如何在本地验证算法正确性。读完本文,你将掌握单链表反转这一高频基础操作的 Go 实现范式,以及 LeetCode-Go 仓库的代码组织与测试风格。

题目背景:Reverse Linked List

LeetCode 206. Reverse Linked List 是链表类问题中最基础、最高频的一道题,其题目描述非常简洁:

Reverse a singly linked list.(反转一个单链表。)

原题文档位于仓库的 website/content.en/ChapterFour/0200~0299/0206.Reverse-Linked-List.md,与之对应的还有中文说明文档 leetcode/0206.Reverse-Linked-List/README.md,其中用一句话概括了题意:翻转单链表

例如输入链表1 -> 2 -> 3 -> 4 -> 5 -> NULL,要求输出5 -> 4 -> 3 -> 2 -> 1 -> NULL。这道题虽然简单,却是后续大量复杂链表题目(如两两交换、k 个一组翻转、回文链表判断等)的基础,因此掌握其两种主流实现(迭代与递归)非常重要。

解题思路:按题意直接迭代翻转

原文档给出的解题思路只有一句话:"Just follow the problem statement."(按照题意做即可)。这句话背后对应的,正是链表反转最经典、最直接的迭代三指针法

  1. 使用三个指针完成就地翻转:behind(前驱,初始为nil)、head(当前节点,初始为链表头)、以及每轮循环中暂存的next(当前节点的下一个节点)。
  2. 每一轮迭代只做三件事:先保存head.Nextnext,再把head.Next指向前驱behind(完成当前节点的"掉头"),最后让behindhead各自前进一位。
  3. head走到链表末尾的nil时,behind恰好停在新链表的头节点上,直接返回behind即可。

整个过程不申请任何额外空间,仅靠修改指针指向完成反转,属于 O(1) 空间复杂度的就地算法。

源码实现:LeetCode-Go 中的 reverseList

仓库中的实际实现位于 leetcode/0206.Reverse-Linked-List/206. Reverse Linked List.go,与题解文档中的代码完全一致:

package leetcode import ( "github.com/halfrost/LeetCode-Go/structures" ) // ListNode define type ListNode = structures.ListNode /** * Definition for singly-linked list. * type ListNode struct { * Val int * Next *ListNode * } */ func reverseList(head *ListNode) *ListNode { var behind *ListNode for head != nil { next := head.Next head.Next = behind behind = head head = next } return behind }

逐行拆解核心循环

  • var behind *ListNode:声明前驱指针,零值初始化为nil,这正好作为反转后链表的尾部终止符。
  • for head != nil:遍历直到原链表末尾。
  • next := head.Next先保存后继。因为下一步会覆盖head.Next,若不先暂存,就会丢失尚未处理的链表剩余部分。
  • head.Next = behind:把当前节点的指针掉转方向,指向前驱。
  • behind = head:前驱指针前进到当前节点。
  • head = next:当前指针前进到原先的后继节点,继续下一轮。
  • return behind:循环结束时behind指向原链表的尾节点,也就是新链表的头节点。

以输入1 -> 2 -> 3为例,模拟过程如下:

轮次head操作后 head.Nextbehind说明
初始1nil
第 1 轮1nil(原为 2)11 变成新链表尾
第 2 轮21(原为 3)22 指向 1
第 3 轮32(原为 nil)33 指向 2
结束nil3返回 3,即3 -> 2 -> 1

关于 ListNode 类型别名的说明

值得注意的是,仓库在实现文件中对链表节点做了类型别名处理:

type ListNode = structures.ListNode

真正的节点定义统一放在独立工具包 structures/ListNode.go 中:

// ListNode 是链接节点 type ListNode struct { Val int Next *ListNode }

从源码结构看,这是 LeetCode-Go 仓库的一种工程化设计:所有链表类题目复用同一个ListNode定义,避免在每题目录里重复声明结构体;题解文档中的代码则保留了一份完整的ListNode定义,便于单独阅读时自洽。这种"文档中自包含、源码中统一复用"的组织方式,正是仓库代码整洁度的体现。

复杂度分析

  • 时间复杂度:O(n)。每个节点恰好被访问一次,循环次数等于链表长度 n。
  • 空间复杂度:O(1)。仅使用behindnext两个指针变量,无额外数据结构开销。

该实现满足题目"原地反转"的要求,是链表反转的空间最优解。

测试验证:仓库如何保证 100% 覆盖率

LeetCode-Go 仓库的口号是 "100% test coverage",206 题的测试用例位于 leetcode/0206.Reverse-Linked-List/206. Reverse Linked List_test.go:

package leetcode import ( "fmt" "testing" "github.com/halfrost/LeetCode-Go/structures" ) type question206 struct { para206 ans206 } // para 是参数 // one 代表第一个参数 type para206 struct { one []int } // ans 是答案 // one 代表第一个答案 type ans206 struct { one []int } func Test_Problem206(t *testing.T) { qs := []question206{ { para206{[]int{1, 2, 3, 4, 5}}, ans206{[]int{5, 4, 3, 2, 1}}, }, } fmt.Printf("------------------------Leetcode Problem 206------------------------\n") for _, q := range qs { _, p := q.ans206, q.para206 fmt.Printf("【input】:%v 【output】:%v\n", p, structures.List2Ints(reverseList(structures.Ints2List(p.one)))) } }

该测试用例的设计体现了仓库的通用测试套路:

  1. 用数组描述输入输出para206ans206分别用[]int表示输入链表与期望的反转结果,可读性好且便于扩展更多用例。
  2. 借助 structures 工具做类型转换structures.Ints2List(p.one)[]int构造成链表,structures.List2Ints(...)把结果链表再转回[]int用于对比输出。这两个工具的实现在 structures/ListNode.go:
// List2Ints convert List to []int func List2Ints(head *ListNode) []int { // 链条深度限制,链条深度超出此限制,会 panic limit := 100 times := 0 res := []int{} for head != nil { times++ if times > limit { msg := fmt.Sprintf("链条深度超过%d,可能出现环状链条。请检查错误,或者放宽 l2s 函数中 limit 的限制。", limit) panic(msg) } res = append(res, head.Val) head = head.Next } return res } // Ints2List convert []int to List func Ints2List(nums []int) *ListNode { if len(nums) == 0 { return nil } l := &ListNode{} t := l for _, v := range nums { t.Next = &ListNode{Val: v} t = t.Next } return l.Next }

其中List2Ints内置了 100 层深度的防环保护:一旦遍历次数超过 100 次即 panic,提示"链条深度超过 100,可能出现环状链条"。这一设计既防止了环形链表导致死循环,也间接验证了reverseList不会产生环。

本地运行测试

仓库根目录的 gotest.sh 提供了全量覆盖率测试脚本:

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

若只单独验证本题,可在仓库根目录执行:

go test -v -run Test_Problem206 ./leetcode/0206.Reverse-Linked-List/

运行后可以看到形如【input】:[1 2 3 4 5] 【output】:[5 4 3 2 1]的调试输出,且通过go test的 PASS 判定。需要注意的是,运行测试依赖 go.mod 中声明的本地模块替换(replace github.com/halfrost/LeetCode-Go/structures => ./structures),因此请在仓库根目录下执行命令,以保证structures包能正确解析。

拓展思考:递归解法与边界情况

递归版本

除了迭代,链表反转还有经典的递归实现,与迭代法对比能加深理解:

func reverseListRecursive(head *ListNode) *ListNode { if head == nil || head.Next == nil { return head } newHead := reverseListRecursive(head.Next) head.Next.Next = head head.Next = nil return newHead }

递归思路是:先反转以head.Next为头的子链表,拿到新的头newHead;然后让原后继节点的Next指回当前节点(head.Next.Next = head),再断开head.Next并返回newHead。递归版代码更简洁,但栈深度为 O(n),在链表极长时存在栈溢出风险,这也是 LeetCode-Go 仓库选择迭代实现作为标准答案的合理解释。

边界情况

  • 空链表(head 为 nil):循环体一次都不执行,直接返回behind(即 nil),正确。
  • 单节点链表:只执行一轮循环,behind指向该节点本身,正确。
  • 两个节点:第二轮循环后顺序互换,正确。

从代码逻辑看,迭代实现天然覆盖上述所有边界,无需额外特判,这也是其简洁性的体现。

总结

LeetCode 206 "Reverse Linked List" 是链表题目的入门基石。通过 LeetCode-Go 仓库的题解文档、源码与测试用例,本文完整梳理了迭代反转的"三指针"实现:保存后继、掉转指针、双指针前进,最终以 O(n) 时间、O(1) 空间的代价完成原地反转。仓库通过 structures/ListNode.go 统一封装节点定义与数组/链表互转工具,配合 206. Reverse Linked List_test.go 的用例驱动方式,为读者提供了一套可直接运行、可复用的标准范式。掌握本题后,再面对链表相关的进阶题目时,你就有了最坚实的底子。

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

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

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

STC89C52驱动TC35发中文短信的嵌入式实现

简介:本资源是一个基于STC89C52单片机实现中文短信发送的嵌入式开发项目,面向电子工程、物联网及单片机初学者与实践者,解决在资源受限MCU上处理中文编码、串口通信与GSM模块AT指令交互等典型难题。压缩包共25个文件,含3个核心C源…

作者头像 李华
网站建设 2026/9/14 4:30:38

声纹识别中的self-attention:从注意力池化到工程落地

简介:基于深度学习的声纹识别(自注意力机制)算法资源,专注于说话人识别任务,代码为Python编写,覆盖高斯混合模型、GMM-UBM、i-vector等传统统计方法,以及基于自注意力的深度学习方法&#xff0c…

作者头像 李华
网站建设 2026/9/14 4:30:28

WorkBuddy金融版实测:金融行业Agent落地与合规破局

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 4:29:34

LLM Wiki:大语言模型驱动的知识协同范式

1. “LLM Wiki”不是个工具名,而是一类知识协同范式的代号你搜“llm wiki”,出来的结果五花八门:有飞书文档链接、Obsidian笔记截图、Dify配置页面、甚至还有“英灵神殿Wiki”“后室Wiki”这类亚文化站点。这恰恰暴露了一个关键事实——当前根…

作者头像 李华
网站建设 2026/9/14 4:26:57

社交网络推荐系统实践:从用户行为建模到算法落地

简介:这是一份面向计算机相关专业毕业设计的完整项目资料,围绕社交网络中用户行为分析与推荐算法展开。项目可真实运行,不仅覆盖关注、转发、点赞、评论、评分等典型行为特征提取,还给出基于用户行为的推荐模型设计与实现&#xf…

作者头像 李华
网站建设 2026/9/14 4:26:28

Apache Fesod替代EasyExcel的性能原理与迁移实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华