反转链表有学问(递归、循环多方法)

学习笔记五 反转链表

本题来自:力扣206. 反转链表

题目描述

对于一个如图所示的链表,将其翻转过来。注意,要在原链表身上进行操作,不能新建链表。
alt

方法一:循环

第一种思路是最容易想到的,我们只需要两两交换相邻的节点指向,从头遍历到尾即可。

  • 首先考虑特殊情况,在链表内没有节点时,或者只有一个节点时不需要反转。
if (head == nullptr || head->next == nullptr) return head;
  • 先定义一个 cur 指针,指向头结点,cur 是我们要操作的中心节点
  • 再定义 prev 节点,指向空,这个节点是 cur 前面的一个节点。
ListNode* cur = head;
ListNode* prev = nullptr;

由于循环时的终止条件取决于后面我们进行反转操作的顺序,因此先不急着看,先写反转逻辑。

  • 如果 cur 节点完成反转,指向了前一节点,那么 cur->next 便会丢失,因此需要定义一个 next 指针保存 cur->next
  • 现在进行反转。令 cur 的 next 指向 prev 即可,然后 prev 和 cur 均前移(注意顺序)
while (  ) {
	ListNode* next = cur->next;
	cur->next = prev;
	prev = cur;         // 先前移prev
	cur = next;
}
  • 再回来看循环条件,由于 next 指针是在 while 判断完成后才赋值,因此只要 cur 不为空皆可。
while (cur != nullptr) {

}
  • 最后一个易错点是返回值,由于在进入最后一次循环时,cur 已经到达链表末尾;而循环过程中对 cur 和 prev 均前移了,因此此时 cur 指向空,prev 指向链表末尾(新头结点),因此应返回 prev
return prev;

完整代码如下:

/**
 * 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) {
        if (head == nullptr || head->next == nullptr) return head;
        ListNode* cur = head;
        ListNode* prev = nullptr;
        while (cur) {
            ListNode* next = cur->next;
            cur->next = prev;
            prev = cur;
            cur = next;
        }
        return prev;
    }
};

方法二:递归

递归的方法一般不易想,并且空间复杂度一般较高,但熟悉以后可以变成题目的 “ 秒杀 ” 利器。

递归解题三部曲:
1,找整个递归的终止条件:递归应该在什么时候结束?
2,找返回值:应该给上一级返回什么信息?
3,本级递归应该做什么:在这一级递归中,应该完成什么任务?

1,终止条件

递归是要一级一级跑到链表的边界去,因此需要一个限定条件说明它已经到底了。

if (head == nullptr || head->next == nullptr)  return head;

这样定义可以让 head 直接停在链表末尾,同时还可以判断链表为空或者只有一个节点的情况。

2,返回值

递归一定是倒着进行链表还原,也就是说,递归会先跑到链表末尾,然后从后往前的反转。那么递归逻辑里面传入的参数一定是 head->next ,这样每次调用递归时节点能不断向后移动。

ListNode* newhead = reverseList(head->next);

而在每一级递归完成后,说明其后的节点已经反转完成。以上面定义的 newhead 为界,左边是未反转部分(包括 newhead),右边是已反转部分。因此返回的便是 newhead。

return newhead;

递归中的下一级的返回值会作为上一级的参数。因此此时上一级的 head 就是 这个 newhead,以此出发再去反转上一级。

3,每一级递归的任务

每一级递归里面,只需要把上一级传回来的 newhead 作为 head ,反转 head 及 head->next 的指向即可。

head->next->next = head;
head->next = nullptr;       // 此时反转后的head->next在空间位置上其实是head的前一位

最后别忘了将反转后的 head->next 置空(断开 head 和它的连接),以便下一级递归直接进行反转。(同时最后也使链表结尾的 next 指向 nullptr)

4,完整代码

/**
 * 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) {
        if (head == nullptr || head->next == nullptr) return head;
        ListNode* newhead = reverseList(head->next);
        head->next->next = head;
        head->next = nullptr;
        return newhead;
    }
};
posted @   Leibnizzzzzz  阅读(92)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示