7 递归——206. 反转链表 ★

7 递归

206. 反转链表

给你单链表的头节点head,请你反转链表,并返回反转后的链表。
示例 1:
在这里插入图片描述
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

算法设计

可以充分利用原有的存储空间,通过修改指针实现单链表的就地逆置。相当于将所有的箭头反向,头指针指向原链表的尾部。如图所示。
在这里插入图片描述
反转链表可以采用迭代法和递归算法两种方法实现,原理是一样的,只是实现方式不同而已。

1. 迭代法

以上面单链表为例,展示单链表就地逆置过程。
(1)首先处理第一个节点,反转后该节点是单链表的尾节点,其next指针指向空。
在这里插入图片描述
特别注意:在修改指针之前,一定要用一个辅助指针,记录断点,否则后面这一部分就会遗失到外太空,再也找不到了。
因此可以设置3个指针:p、q、r,p指向前一个节点,q指向当前节点,r指向下一个节点(断点)。

  • 初始时,p = nullptr,q = head。
  • 如果q非空,修改q的next指向p,即q->next = p;在修改之前,一定要先用r记录断点,r = q->next。
    在这里插入图片描述
  • 然后p、q两个指针后移。
    在这里插入图片描述
    (2)反转下一个节点。如果q非空,重复4个操作:记录断点、反转指针、两个指针后移。
    r = q->next; //记录断点
    q->next = p; //反转指针
    p = q; //后移一位
    q = r; //后移一位

在这里插入图片描述
(3)当处理完最后一个节点时,q为空,p指向的节点为头节点,返回头指针p即可。
在这里插入图片描述

算法实现

// LeetCode206 反转链表
ListNode* reverseList(ListNode* head) { // 迭代法
    ListNode *p = nullptr,*q = head, *r; // 前一个节点,当前节点,下一个节点
    while (q) {
        r = q->next; //记录断点
        q->next = p; //反转指针
        p = q; //后移一位
        q = r; //后移一位
    }
    return p; //返回反转链表的头指针
}

2. 递归算法

设计递归函数三部曲:
(1)函数名和参数
定义递归函数名为reverseList(),因为要反转链表head,因此需要设计一个参数head。
(2)递归的结束条件
考虑特殊情况,如果链表为空(head = nullptr),或者只有一个节点(head->next = nullptr),则不需要反转链表,直接返回head即可。
(3)自调用
在函数内部调用自身,调用自身时函数名相同。可以宏观考虑,如果第一个节点不动,后面所有节点完成了反转操作,反转第一个节点之后的链表可以调用同名函数reverseList(head->next)实现。
在这里插入图片描述
只需要两个操作:① 将该链表尾节点和第一个节点链接;② 修改第一个节点的next为空即可。后面链表反转后的尾节点正是head->next,修改其next指针指向第一个节点,即head->next->next = head。再修改第一个节点的next为空,head->next = nullptr。
在这里插入图片描述
最后,返回新链表的头指针r,r是自调用reverseList(head->next)的返回值。

算法实现

// LeetCode206 反转链表
ListNode* reverseList(ListNode* head) { // 递归算法
    if (head == nullptr || head->next == nullptr) //递归出口
        return head;
    ListNode *r = reverseList(head->next); //递归调用
    head->next->next = head; //反转指针
    head->next = nullptr;
    return r; //返回反转链表的头指针
}

算法分析

迭代法和递归算法的时间复杂度均为O(n)。递归的空间复杂度为O(n),迭代法的空间复杂度为O(1)。

### 反转单向链表算法实现 反转单向链表是一个经典的算法问题,在 LeetCode 的第 206 题中有详细的描述。以下是该问题的核心思路和两种常见的实现方法。 #### 思路分析 要反转一个单向链表,可以通过迭代或者递归来完成。无论是哪种方式,其核心目标都是改变每个节点的 `next` 指针方向,使得原本指向下一个节点的方向改为指向前一个节点[^1]。 --- #### 方法一:迭代法 迭代法是一种直观且高效的解决方案。通过维护三个指针变量——前驱节点 (`prev`)、当前节点 (`curr`) 和临时存储下一节点的变量 (`temp`),逐步更新链表中的指针关系。 ##### 实现代码 ```python class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next def reverseList(head: ListNode) -> ListNode: prev = None # 初始化前驱节点为空 curr = head # 当前节点从头节点开始 while curr is not None: # 循环直到当前节点为空 temp = curr.next # 保存当前节点的下一个节点 curr.next = prev # 修改当前节点的 next 指向前驱节点 prev = curr # 更新前驱节点为当前节点 curr = temp # 移动到下一个节点 return prev # 返回新的头节点(原链表的尾节点) ``` 这种方法的时间复杂度为 O(n),其中 n 是链表的长度;空间复杂度为 O(1)。 --- #### 方法二:递归递归法虽然逻辑上稍显复杂,但它提供了一种优雅的方式来解决问题。递归的关键在于定义好终止条件以及如何处理每一层递归返回的结果。 ##### 实现代码 ```cpp // C++ 版本 ListNode* reverseList(ListNode* head) { if (head == nullptr || head->next == nullptr) { // 终止条件:到达链表末尾 return head; } ListNode* newHead = reverseList(head->next); // 递归调用,获取新链表的头部 head->next->next = head; // 改变当前节点与其后续节点之间的连接 head->next = nullptr; // 清空当前节点的 next 指针 return newHead; // 返回新链表的头部 } ``` 递归方法同样具有时间复杂度 O(n),但由于需要额外的栈空间来支持函数调用,因此空间复杂度为 O(n)[^3]。 --- ### 对比与总结 - **迭代法** 更加高效,适合大规模数据场景下的应用。 - **递归法** 虽然简洁易懂,但在极端情况下可能会因为栈溢出而导致程序崩溃。 对于初学者来说,建议先掌握迭代法再尝试理解递归法。如果希望进一步学习链表操作的相关技巧,可以参考《王道数据结构考研复习指导》或其他经典教材[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

趣学算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值