📌 题目链接:206. 反转链表 - 力扣(LeetCode)
🔍 难度:简单 | 🏷️ 标签:链表、递归、迭代、指针操作
⏱️ 目标时间复杂度:O(n)
💾 空间复杂度:O(1)(迭代) / O(n)(递归)
✅ 本题为 后端开发高频面试题,常出现在字节跳动、腾讯、阿里、美团等大厂的算法笔试和面试中。
🔥 考点覆盖:链表结构理解、指针操作、递归思维、空间优化意识。
🧠 掌握本题,你将学会如何在有限空间内高效操作链表,并为后续“回文链表”、“K 个一组翻转链表”等难题打下坚实基础!
🧩 题目分析
给你单链表的头节点
head,请你反转链表,并返回反转后的链表。
🎯 示例说明
- 输入:
head = [1,2,3,4,5] - 输出:
[5,4,3,2,1]
🔍 关键观察
- 单向链表只支持从头到尾遍历,无法直接访问前驱节点。
- 反转操作本质是 修改每个节点的 next 指针方向。
- 必须保存当前节点的下一个节点,否则会丢失链表信息。
❗ 常见误区
- 忽略边界情况:空链表或只有一个节点的情况。
- 递归时忘记断开原链表连接,导致环形链表。
- 使用额外数组存储节点,破坏了“原地操作”的最优解思想。
🔍 核心算法及代码讲解
✅ 方法一:迭代法(推荐)
🎯 核心思想:
通过 三指针技巧 实现原地反转:
prev:记录前一个节点(初始为 nullptr)curr:当前处理的节点(初始为 head)next:临时保存下一个节点,防止断链
每一步执行:
next = curr->next; // 保存下一个节点
curr->next = prev; // 反转指针方向
prev = curr; // 移动 prev
curr = next; // 移动 curr
📌 为什么叫“三指针”?
因为我们在一次循环中同时维护三个关键位置:
- 当前节点
- 上一个节点
- 下一个节点
这确保我们不会丢失任何链表元素。
✅ 优势:
- 时间复杂度 O(n)
- 空间复杂度 O(1)
- 易于理解和实现
- 面试官喜欢看到这种清晰、稳定的写法
💡 小贴士:
在面试中,如果被问“能不能不用递归?”——立刻答:“当然可以,用迭代法原地反转即可。”
✅ 方法二:递归法(思维训练)
🎯 核心思想:
利用 递归的天然反向特性 来完成反转:
假设从 head->next 开始的子链表已经被成功反转,那么:
- 新的头节点就是
reverseList(head->next)返回的结果 - 我们只需要让
head->next->next = head,即把当前节点接到后面 - 并设置
head->next = nullptr避免环路
🧠 递归逻辑拆解:
reverseList(head):
if head == null or head->next == null:
return head // 基础情况:空链表或单节点
else:
newHead = reverseList(head->next) // 先反转后面的
head->next->next = head // 把自己接上去
head->next = nullptr // 断开旧连接
return newHead // 返回新的头
⚠️ 注意事项:
- 递归深度等于链表长度 → 栈溢出风险
- 必须手动断开
head->next,否则形成环! - 不适合超长链表(如 10^6 节点),但用于小规模数据或思维训练很有效
💡 面试加分点:
“虽然递归简洁优雅,但在生产环境中我会优先选择迭代法,因为它更稳定、无栈溢出风险。”
🧠 解题思路(分步解析)
📌 步骤 1:判断边界条件
if (!head || !head->next) return head;
- 如果链表为空或只有一个节点,无需反转,直接返回。
📌 步骤 2:初始化三指针
ListNode* prev = nullptr;
ListNode* curr = head;
prev指向已反转部分的最后一个节点(初始为空)curr指向待处理的当前节点
📌 步骤 3:循环处理每个节点
while (curr) {
ListNode* next = curr->next; // 保存下一个节点
curr->next = prev; // 反转指针
prev = curr; // 更新 prev
curr = next; // 更新 curr
}
- 每轮将当前节点插入到
prev前面 - 最终
prev指向新链表的头节点
📌 步骤 4:返回结果
return prev;
- 因为
prev是最后一个被处理的节点,也就是新链表的头
📊 算法分析
| 方法 | 时间复杂度 | 空间复杂度 | 是否原地 | 是否推荐 |
|---|---|---|---|---|
| 迭代 | O(n) | O(1) | ✅ 是 | ✅ 强烈推荐 |
| 递归 | O(n) | O(n) | ❌ 否 | 仅用于学习 |
📌 面试考察维度:
- 基础能力:是否能正确处理链表指针
- 边界思维:是否考虑空链表、单节点等情况
- 空间意识:能否写出 O(1) 的解法
- 递归理解:是否掌握递归的“先深入再返回”机制
- 工程判断:知道何时该用迭代 vs 递归
💻 代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
// Definition for singly-linked list.
struct ListNode {
int val;
ListNode *next;
ListNode() : val(0), next(nullptr) {}
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode *next) : val(x), next(next) {}
};
class Solution {
public:
// 方法一:迭代法(推荐)
ListNode* reverseList(ListNode* head) {
ListNode* prev = nullptr;
ListNode* curr = head;
while (curr) {
ListNode* next = curr->next; // 保存下一个节点,防止断链
curr->next = prev; // 反转当前节点的指针
prev = curr; // 移动 prev 指针
curr = next; // 移动 curr 指针
}
return prev; // prev 是新链表的头
}
// 方法二:递归法(思维训练)
ListNode* reverseListRecursive(ListNode* head) {
if (!head || !head->next) {
return head;
}
ListNode* newHead = reverseListRecursive(head->next); // 先反转后面的部分
head->next->next = head; // 将当前节点接到后面
head->next = nullptr; // 断开原连接,避免环
return newHead;
}
};
// 测试
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
// 构造测试链表: 1 -> 2 -> 3 -> 4 -> 5
ListNode* head = new ListNode(1);
head->next = new ListNode(2);
head->next->next = new ListNode(3);
head->next->next->next = new ListNode(4);
head->next->next->next->next = new ListNode(5);
Solution sol;
// 测试迭代法
ListNode* reversed = sol.reverseList(head);
cout << "反转后链表: ";
while (reversed) {
cout << reversed->val << " ";
reversed = reversed->next;
}
cout << endl;
// 清理内存(实际项目中需注意)
// 这里省略,仅作演示
return 0;
}
✅ 输出结果:
5 4 3 2 1🔧 注意:实际使用时请记得释放动态分配的内存,避免内存泄漏。
🌟 本期完结,下期见!🔥
👉 点赞收藏加关注,新文更新不迷路。关注专栏【算法】LeetCode Hot100刷题日记,持续为你拆解每一道热题的底层逻辑与面试技巧!
💬 欢迎留言交流你的解法或疑问!一起进步,冲向 Offer!💪
📣 下一期预告:LeetCode 热题 100 第4题 —— 234. 回文链表(简单)
🔹 题目:给定一个单链表,判断它是否为回文结构(正读反读相同)。
🔹 核心思路:
- 使用 快慢指针 找到链表中点
- 反转后半部分链表
- 比较前后两部分是否相等
- 恢复链表结构(可选,取决于要求)
🔹 考点:
- 快慢指针(寻找中点)
- 链表反转(复用本题技巧)
- 空间优化(O(1) vs O(n))
- 边界处理(奇偶长度)
🔹 难度:中等偏上,但属于经典链表问题,面试必考!
💡 提示:不要用数组存储所有值,那样空间复杂度为 O(n),不够优雅!
📌 记住:当你在刷题时,不要只看答案,要像写这篇文章一样,深入思考每一步背后的原理、优化空间和面试价值。这才是真正提升算法能力的方式!
📌 附注:本文内容基于 LeetCode 官方题解与常见面试经验整理,适用于准备技术面试的开发者。建议结合手写模拟 + 白板推导练习,巩固记忆。
🌟 结语:从“会做”到“讲透”,才是算法的真正通关
刷题不是为了背答案,而是为了在面试官问“为什么这样写?”时,你能从容不迫地拆解每一步设计背后的权衡与智慧。
反转链表看似简单,却浓缩了指针操作的精髓、递归思维的优雅、空间优化的工程意识——这正是大厂考察的核心:你是否具备将问题抽象、分解并高效实现的能力。
今天你掌握的不仅是一道题,而是一把钥匙:
🔑 它能打开“K 个一组翻转链表”的大门,
🔑 能助你轻松应对“判断回文链表”的变体,
🔑 更能在系统设计中让你对数据结构的操作游刃有余。
所以,请带着这份理解继续前行。
每一道 Hot100,都是通往 Offer 的一块基石。
💬 你在面试中被问过这道题吗?有没有更巧妙的解法?欢迎在评论区分享你的故事或疑问!
👉 关注专栏【算法】LeetCode Hot100刷题日记,我们下期一起拆解 回文链表 的双指针+反转技巧!
🔥 刷百题如破一关,悟一理可通万法。坚持,你离理想 Offer 只差 100 道题的距离。