翻转链表就像重新排列人生轨迹,需要一点技巧和勇气
大家好,我是你们的技术小伙伴FogLetter,今天我们来聊聊链表的反转——这个在面试中出现频率堪比“请自我介绍”的经典问题。
场景引入:当链表想要“重新来过”
想象一下,你有一条单向链表,就像一列单向行驶的小火车,每个车厢只能看到前面的车厢,却回不了头。但某天,链表突然想要“重新审视人生”,看看来时的路——这就是链表反转要解决的问题。
在真实世界中,链表反转的应用无处不在:撤销操作、回文判断、两数相加……掌握它,你就是那个能帮链表“翻旧账”的魔法师。
迭代法:一步一个脚印的翻转术
先来看迭代解法,这是最直观也最容易被面试官追问细节的方法:
function reverseList(head) {
let prev = null; // 前女友:已经是过去式
let cur = head; // 现任:当前正在处理的关系
while (cur) {
const next = cur.next; // 备胎?不,是下一个要处理的对象
cur.next = prev; // 勇敢转身,面向过去
prev = cur; // 前女友更新为现任
cur = next; // 现任变成下一个目标
}
return prev; // 最终,第一个变成最后一个,最后一个成为新开始
}
来,我们一步步拆解这个“情感关系重组过程”
假设我们有一条链表:A → B → C → null
第一轮循环:
prev = null,cur = A- 保存
next = B(记住下家) A.next = null(A与过去切断联系)prev = A(A成为新的“前任标准”)cur = B(移情别恋到B)
此时链表状态:null ← A B → C → null
第二轮循环:
prev = A,cur = B- 保存
next = C B.next = A(B回头指向A)prev = Bcur = C
状态:null ← A ← B C → null
第三轮循环:
prev = B,cur = C- 保存
next = null C.next = B(C回头指向B)prev = Ccur = null
最终:null ← A ← B ← C
循环结束,返回 prev 即 C,成为新链表的头节点。
迭代法的精髓
- 时间复杂度:O(n),每个节点只访问一次
- 空间复杂度:O(1),只用了三个指针,堪称节约内存的典范
- 核心思想:逐个翻转指针方向,像翻书一样一页页翻转链表
递归法:相信未来的自己
如果说迭代法是勤劳的蚂蚁,那么递归法就是相信"未来的自己一定能搞定"的智者:
function reverseListRecursive(head) {
// 递归结束条件:空链表或只有一个节点
if (!head || !head.next) {
return head;
}
// 相信递归:后面的事情交给下一个我
const newHead = reverseListRecursive(head.next);
// 回溯时的魔法时刻
head.next.next = head; // 让下一个节点指向自己
head.next = null; // 斩断自己原来的指向
return newHead; // 新的头节点一路传递回来
}
递归的"信任链"是如何工作的?
还是以 A → B → C → null 为例:
递去阶段:
- reverse(A) 调用 reverse(B)
- reverse(B) 调用 reverse(C)
- reverse(C) 发现 C.next 为 null,返回 C
归来阶段:
从 C 返回给 B:
- newHead = C
- B.next.next = B 即 C.next = B(C指向B)
- B.next = null(B切断原有指向) 现在:A → B ← C,返回 C
从 B 返回给 A:
- newHead = C
- A.next.next = A 即 B.next = A(B指向A)
- A.next = null(A切断原有指向) 现在:A ← B ← C,返回 C
最终得到 C → B → A → null
递归法的哲学
- 信任递推:我相信我调用的函数能解决子问题
- 精致回溯:在回来的路上完成指针翻转
- 空间代价:O(n)的栈空间,用空间换来了思维的简洁
两种方法的对比:不同场景下的选择
| 特性 | 迭代法 | 递归法 |
|---|---|---|
| 空间复杂度 | O(1) | O(n) |
| 代码可读性 | 直观易懂 | 抽象优雅 |
| 适用场景 | 内存受限环境 | 代码简洁优先 |
| 面试表现 | 容易解释 | 展现思维深度 |
实战小贴士
- 边界处理:空链表和单节点链表直接返回
- 指针安全:在修改 next 前一定要保存原值
- 递归深度:超长链表可能栈溢出,了解你的环境限制
- 多解法掌握:面试时先给迭代解,被追问再展示递归功力
思维延伸
链表反转其实体现了计算机科学中一个很重要的思想:局部操作可以产生全局效果。我们不需要一次性处理整个链表,只需要处理好当前节点与周边的关系,最终整个链表就会自然翻转。
这种思想在分布式系统、函数式编程中随处可见——做好本地正确的事,全局自然会正确。
总结
链表反转就像人生中的转折点,有时候需要勇敢地转身面向过去,才能开启新的方向。迭代法教会我们脚踏实地,递归法则告诉我们相信未来的力量。
下次面试遇到这个问题,希望你能自信地说:"这个问题我有两种理解方式……"
技术如人生,懂得何时坚持、何时转向,才是真正的智慧。