LeetCode 206. 反转链表:迭代 + 递归双解法全解析

206. 反转链表

一、题目

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:

输入:head = [1,2,3,4,5]

输出:[5,4,3,2,1]

示例 2:

输入:head = [1,2]

输出:[2,1]

示例 3:

输入:head = []

输出:[]

提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000
    进阶:链表可以选用迭代递归方式完成反转。你能否用两种方法解决这道题?

二、思路

根据题目的进阶要求,我们接下来使用递归法和迭代法两种方法来解决这个算法题目。

递归法:

自己调用自己。识别方法:在reverse函数的代码身体里,又看到了reverse(...)这个名字。

  1. 判断终止条件
  2. 使用temp暂存下一步
  3. 执行反转
  4. 指针双双后移

迭代法:

使用循环,可以看到 for 或 while 循环。

  1. 初始化两个 cur 和 pre 指针
  2. 开启循环,只要cur还有值,就要一直循环
  3. 使用temp暂存下一步
  4. 执行反转
  5. 指针双双后移
  6. 循环结束时,返回pre,结束

三、代码

递归法:

javascript 复制代码
//递归辅助函数
function reverse(cur,pre){
 // 1. 终止条件 (Base Case)
 // 如果 cur 为空,说明已经越过了链表的最后一个节点。
 // 此时 pre 指向的就是原链表的最后一个节点(也就是新链表的头节点)。
 // 将其返回,这个返回值会像接力棒一样一层层传回主函数,return后终止了。
    if(!cur) return pre;
    // 2. 暂存下一步 (Save Next)
    // 因为接下来要切断 cur 和后面的联系,所以必须先把 cur.next 存起来,
    // 否则后面的一长串链表就丢了。
    const temp = cur.next;
    //3. 执行反转
    cur.next = pre;
    //进入下一层调用,指针向后移动
    //cur后移一位到temp
    //pre后移一位到cur
    return reverse(temp,cur)
}
//主函数
var reverseList = function(head) {
    //当前cur节点是head
    //当前pre节点是null
    return reverse(head,null);
};

迭代法:

javascript 复制代码
var reverseList = function(head){
    //初始化两个指针:pre前驱,cur当前
    let pre = null;
    let cur = head;
    //开启循环,只要cur还有值,就继续干活
    while(cur){
        //暂存下一步temp(防止断链)
        const temp = cur.next;
        //反转指针
        cur.next=pre;
        //将指针集体向后移动,准备处理下一个
        pre=cur;//pre移动到当前cur
        cur=temp;//当前cur移动到temp(下一位)
    }
    //循环结束时,cur变成了null,pre刚好停在新的头节点上
    return pre;
}

四、复杂度

递归法:

缺点:这种写法属于尾递归,如果链表特别长,可能会导致栈溢出错误。
时间复杂度:O(N)

  • 分析: N 是链表的长度。代码中的递归函数 reverse 会被调用 N+1N+1N+1 次(每个节点一次,最后 null 一次)。
  • 操作: 每次调用内部只做了常数次操作(赋值、指针修改),即 O(1)。

空间复杂度:O(N) ⚠️ (这是与迭代法最大的不同)

  • 分析: 递归的本质是函数调用栈 (Call Stack)。
  • 堆叠:
    • 当处理节点 1 时,函数没结束,等着节点 2 的结果;
    • 当处理节点 2 时,等着节点 3 的结果...
    • 这意味着系统内存中同时保留了 NNN 层函数的上下文(每一层都要存 cur, pre, temp 这些变量)。

迭代法:

时间复杂度:O(N)

结论:线性时间复杂度。

其中 N 是链表的长度(节点数量)。

为什么是 O(N)?

  • 一次遍历: 代码中有一个 while (cur) 循环。这个循环从链表的头节点 (head) 开始,顺着 next 指针一直走到尾部 (null)。
  • 不走回头路: 每个节点被访问且仅被访问一次。
  • 常数级操作: 在循环内部,不管是 temp = cur.next 还是指针的赋值操作,都是最基本的指令,耗时是固定的 O(1)O(1)O(1)。

空间复杂度:O(1)

结论:常数空间复杂度。

这是迭代法相对于递归法最大的优势。

为什么是 O(1)?

  • 变量数量固定: 无论链表有 10 个节点还是 10 万个节点,这段代码运行所需的额外内存空间是固定不变的。
  • 仅使用的变量: 我们只申请了 3 个指针变量的内存空间:
    1. pre (前驱)
    2. cur (当前)
    3. temp (临时存储)
  • 原地操作 (In-Place): 我们直接修改了原链表节点的 next 指针,没有创建新的链表,也没有像递归那样消耗调用栈(Call Stack)空间。

两种方法对比:

相关推荐
2401_8414956410 分钟前
【LeetCode刷题】二叉树的直径
数据结构·python·算法·leetcode·二叉树··递归
budingxiaomoli10 分钟前
优选算法-字符串
算法
我是咸鱼不闲呀25 分钟前
力扣Hot100系列19(Java)——[动态规划]总结(上)(爬楼梯,杨辉三角,打家劫舍,完全平方数,零钱兑换)
java·leetcode·动态规划
qq74223498429 分钟前
APS系统与OR-Tools完全指南:智能排产与优化算法实战解析
人工智能·算法·工业·aps·排程
A尘埃1 小时前
超市购物篮关联分析与货架优化(Apriori算法)
算法
.小墨迹1 小时前
apollo学习之借道超车的速度规划
linux·c++·学习·算法·ubuntu
不穿格子的程序员1 小时前
从零开始刷算法——贪心篇1:跳跃游戏1 + 跳跃游戏2
算法·游戏·贪心
大江东去浪淘尽千古风流人物1 小时前
【SLAM新范式】几何主导=》几何+学习+语义+高效表示的融合
深度学习·算法·slam
铉铉这波能秀1 小时前
LeetCode Hot100数据结构背景知识之列表(List)Python2026新版
数据结构·leetcode·list
重生之我是Java开发战士1 小时前
【优选算法】模拟算法:替换所有的问号,提莫攻击,N字形变换,外观数列,数青蛙
算法