链表操作的递归思维

01 递归反转整个链表

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

  1. 示例 1
  2. 输入:head = [1,2,3,4,5]
  3. 输出:[5,4,3,2,1]
  4. 示例 2
  5. 输入:head = [1,2]
  6. 输出:[2,1]
  7. 示例 3
  8. 输入:head = []
  9. 输出:[]
  10. 提示:
  11. 链表中节点的数目范围是 [0, 5000]
  12. -5000 <= Node.val <= 5000
  13. 来源:力扣(LeetCode
  14. 链接:https://leetcode-cn.com/problems/reverse-linked-list
  15. 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

对于递归算法,最重要的就是要理解递归函数的定义。具体来说,reverseList 函数定义是这样的:

输入一个节点 head ,将以 head 为起点的链表反转,并返回反转之后的头节点。比如反转如下链表:

1.1.8 链表操作的递归思维 - 图1

那么输入 reverseList(head) 后,会在这里进行递归:

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

上述递归代码可以用如下图解释:

1.1.8 链表操作的递归思维 - 图2

执行完这行代码之后,整个链表就会变成这样:

1.1.8 链表操作的递归思维 - 图3

根据函数定义,该函数返回反转之后的头节点用变量 last 接收了。

ListNode* reverseList(ListNode* head) {
    if (head == nullptr || head->next == nullptr)
        return head;
    ListNode* last = reverseList(head->next);
    head->next->next = head;
    head->next = nullptr;
    return last;
}

// 补充:迭代法
ListNode* reverseList(ListNode* head) {
    if (head == nullptr || head->next == nullptr)
        return head;
    ListNode* pre, *cur, *nex;
    pre = nullptr;
    cur = nex = head;
    while (cur != nullptr) {
        nex = nex->next;
        cur->next = pre;
        pre = cur;
        cur = nex;
    }
    return pre;
}

02 反转链表前 N 个节点

题目要求如下:

// 将链表的前 n 个节点反转(n <= 链表长度)
ListNode* reverseN(ListNode* head, int n);

比如说对于下图链表,执行 reverseN(head, 3)

1.1.8 链表操作的递归思维 - 图4

思路和反转整个链表类似,需要添加一个后驱节点接收原头节点的 next。对于反转整个链表,最后原头节点的 next 指向 nullptr

ListNode* successor = nullptr;    // 后驱节点
// 反转以 head 为起点的 n 个节点,返回新的头节点。
ListNode* reverseN(ListNode* head, int n) {
    if (n == 1) {
        // 记录第 n + 1 个节点
        successor = head->next;
        return head;
    }
    ListNode* last = reverseN(head->next, n - 1);
    head->next->next = head;
    head->next = successor;
    return last;
}

03 反转链表的一部分

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。

示例 1:


输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]
示例 2:

输入:head = [5], left = 1, right = 1
输出:[5]


提示:

链表中节点数目为 n
1 <= n <= 500
-500 <= Node.val <= 500
1 <= left <= right <= n

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/reverse-linked-list-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

首先,如果 left == 1,就相当于反转链表开头的前 right 个元素:

ListNode* reverseBetween(ListNode* head, int left, int right) {
    if (left == 1)
        return reverseN(head, right);
}

如果 m != 1 怎么办?如果我们把 head 的索引视为 1,那么我们是想从第 m 个元素开始反转对吧;如果把
head->next 的索引视为 1 呢?那么相对于 head->next,反转的区间应该是从第 m - 1 个元素开始的;那么
对于 head->next->next 呢……

ListNode* reverseBetween(ListNode* head, int left, int right) {
    if (head == nullptr || head->next == nullptr)
        return head;
    if (left == 1) {
        return reverseN(head, right);
    }
    head->next = reverseBetween(head->next, left - 1, right - 1);
    return head;
}