递归法

在遍历链表时,使用的都是相同操作来操作链表的不同部分,此时可以采用递归法。

在双指针法(🔗)中的反转链表,两两交换链表中的节点都可以使用递归法,以两题为例:

反转链表

递归方法一:
递归法相对抽象一些,但是其实和双指针法是一样的逻辑,同样是当cur为空的时候循环结束,不断将cur指向pre的过程。
关键是初始化的地方,可能有的同学会不理解, 可以看到双指针法中初始化 cur = head,pre = NULL,在递归法中可以从如下代码看出初始化的逻辑也是一样的,只不过写法变了。

  1. class Solution {
  2. /**
  3. * 递归法(从前向后)
  4. *
  5. * @param head
  6. * @return
  7. */
  8. public ListNode reverseList(ListNode head) {
  9. return reverse(null, head);
  10. }
  11. private ListNode reverse(ListNode pre, ListNode cur) {
  12. // 当cur为空时停止递归
  13. if (cur == null) {
  14. return pre;
  15. }
  16. // 保存下一个节点
  17. ListNode tmp = cur.next;
  18. // 转换指针指向
  19. cur.next = pre;
  20. // 继续调用递归
  21. return reverse(cur, tmp);
  22. }
  23. }

递归方法二:

  1. // 从后向前递归
  2. class Solution {
  3. ListNode reverseList(ListNode head) {
  4. // 边缘条件判断
  5. if(head == null) return null;
  6. if (head.next == null) return head;
  7. // 递归调用,翻转第二个节点开始往后的链表
  8. ListNode last = reverseList(head.next);
  9. // 翻转头节点与第二个节点的指向
  10. head.next.next = head;
  11. // 此时的 head 节点为尾节点,next 需要指向 NULL
  12. head.next = null;
  13. return last;
  14. }
  15. }

两两交换链表中的节点

递归法

  1. class Solution {
  2. /**
  3. * 递归法(从右到左)
  4. *
  5. * @param head
  6. * @return
  7. */
  8. public ListNode swapPairs(ListNode head) {
  9. // 递归结束的条件
  10. if (head == null || head.next == null) {
  11. return head;
  12. }
  13. // 获取头结点的下一个节点
  14. ListNode nextNode = head.next;
  15. // 调用递归进行循环
  16. ListNode newNode = swapPairs(nextNode.next);
  17. // 转换指针指向
  18. nextNode.next = head;
  19. head.next = newNode;
  20. // 返回nextNode用来连接上一个节点
  21. return nextNode;
  22. }
  23. }

92. 反转链表 II

递归法

https://labuladong.github.io/algo/2/18/18/

  1. public ListNode reverseBetween(ListNode head, int left, int right) {
  2. if (left == 1) {
  3. return reverseN(head, right);
  4. }
  5. head.next = reverseBetween(head.next, left - 1, right - 1);
  6. return head;
  7. }
  8. ListNode successor = null;
  9. public ListNode reverseN(ListNode node, int n) {
  10. if (n == 1) {
  11. // 停止递归,并记录下一个节点
  12. successor = node.next;
  13. // 反转本身
  14. return node;
  15. }
  16. ListNode last = reverseN(node.next, n - 1);
  17. node.next.next = node;
  18. node.next = successor;
  19. return last;
  20. }

迭代法

https://leetcode.cn/problems/reverse-linked-list-ii/solution/fan-zhuan-lian-biao-ii-by-leetcode-solut-teyq/
迭代法思路不难,就是找到中间的链表,置null后,单独反转后在连接。
image.png

  1. public ListNode reverseBetween(ListNode head, int left, int right) {
  2. ListNode dummy = new ListNode(-1);
  3. dummy.next = head;
  4. ListNode pre = dummy;
  5. // 找到开始交换链表的前一个节点
  6. for (int i = 0; i < left - 1; i++) {
  7. pre = pre.next;
  8. }
  9. ListNode rightNode = pre;
  10. for (int i = 0; i < right - left + 1; i++) {
  11. rightNode = rightNode.next;
  12. }
  13. ListNode succ = rightNode.next;
  14. ListNode leftNode = pre.next;
  15. // 注意置为null,再反转
  16. rightNode.next = null;
  17. pre.next = null;
  18. // 第 4 步:同第 206 题,反转链表的子区间
  19. reverse(leftNode);
  20. pre.next = rightNode;
  21. leftNode.next = succ;
  22. return dummy.next;
  23. }
  24. public void reverse(ListNode head) {
  25. ListNode cur = head, pre = null;
  26. while (cur != null) {
  27. ListNode temp = cur.next;
  28. cur.next = pre;
  29. pre = cur;
  30. cur = temp;
  31. }
  32. }