很多人刷第 3 天时,已经开始有点发怵了:数组的边界条件刚有点手感,题目突然全变成了一串 next、->,指针绕来绕去,仿佛换个了语言。我 Day3 就是在这个状态下度过的。如果你也在按代码随想录的题单刷 LeetCode,大概率这一天的节奏是开始进入链表,核心题目是 203. 移除链表元素、707. 设计链表、206. 反转链表。这篇文章不是把题解抄一遍,而是我完整刷完 Day3 之后想重新整理一遍的心得:哪些代码要能默写,为什么解法要那样从头设计,哪些边界不写根本不知道会错。
先说明一下,不同版本的代码随想录训练营在划分上有细微差别,有的把数组最后两题放到 Day3,但按经典主路线走,链表第一枪基本就是这个时候。不管题单怎么排,链表这一块始终绕不开,所以这篇文章把“Day3 链表三题”讲透,对大部分按计划刷题的人是可以直接参考的。
1. Day3 为什么从数组突然切到链表,这个节奏其实是刻意的
数组偏重“同一种数据在内存里连续摆放”,链表偏重“用指针把不同对象串起来”。连续摆放的东西好想,不连续的东西难想。所以很多算法新手在数组题里还能靠暴力写对,一到链表就连怎么遍历都会写错,原因不是不会语法,而是脑子里缺少一张“谁指向谁”的图。
1.1 Day3 核心题单速览
先给出这一天的常见题单,我建议按下面的顺序刷,不要乱换。因为 203 用于建立虚拟头节点的直觉,707 是加深对增删查边界理解的好工具,206 是第一次主动反转指针方向,三题之间的递进关系很明显。
| 题号 | 题目 | 核心考点 | 建议用时 | 优先级 |
|---|---|---|---|---|
| 203 | 移除链表元素 | 虚拟头节点、节点删除逻辑 | 30-45分钟 | 必须刷 |
| 707 | 设计链表 | 增删查接口、下标边界 | 50-70分钟 | 必须刷 |
| 206 | 反转链表 | 三指针迭代、递归写法 | 40-60分钟 | 必须刷 |
我之前见过有人把 206 放到最后刷,结果卡太久。建议无论如何都在 707 之后再碰 206,因为 707 会让你对 dummyHead、前驱节点、后继节点的关系建立感觉,这种感觉正是 206 需要的东西。
1.2 链表基础不过关,后面二叉树和相关题目会加倍难受
很多人以为链表只是个“数据结构基础”,刷几题就算完。真正等到刷二叉树的时候才意识到,二叉树节点就是 ListNode 的加强版,每个节点多了 left 和 right 两个指针,遍历和递归全部建立在指针操作之上。链表里的空指针判断、递归中止条件、指针交换顺序,跟二叉树里 root->left 的处理一模一样。
Day3 这波题,本质上就是一次“指针思维”的脱敏训练。从数组的 nums[i] 到链表的 cur->next,这套转换必须尽早完成。我刷完这一天的体会是:真正难的不是某一道题,而是能不能用指针想象出每一步之后的结构长什么样。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 203. 移除链表元素:先统一“删头”和“删中间”,再谈优化
题目很直白:给一个链表头节点 head 和一个整数 val,删除链表中所有值等于 val 的节点。但刷过的人都知道,看起来越简单的题,坑越容易埋在头节点上。
2.1 不用虚拟头节点时会遇到什么烦心事
如果你不用 dummyHead,删除逻辑自然分成两段:
- 如果被删除的是头节点,需要不断执行
head = head->next,因为下一个头节点可能仍然等于val。 - 如果被删除的是中间节点,需要让前一个节点的
next跨过当前节点,指向后继。
这两段逻辑虽然单独看都不难,但放到一个循环里必须写成分支。等你写完,回头再看,代码里到处是 head 和 cur 的分别处理,很容易漏掉某种情况。比如删除连续相同值的时候,如果每次删完都执行 cur = cur->next,那么相邻的等值节点就会漏掉一个。
Carl 在代码随想录里反复强调虚拟头节点,不是因为它能提升复杂度,而是它能把所有删除逻辑统一成一种写法:无论删头还是删中间,都变成“站在被删节点的前一个位置,检查 cur->next 是否要删”。
2.2 用虚拟头节点后的最终写法
这里我用 C++ 来写,注意 LeetCode 内置的 ListNode 已经有带 val 和 next 的构造函数,所以可以直接构造一个栈上的 dummyHead。
cpp复制cla
