Luna Tech

Tutorials For Dummies.

Singly linked list(单链表)- 1

2021-07-20


1. 单链表

Reference: 递归反转链表的一部分

目标:练习单链表的题目是为了训练递归思维,对递归有个直观的认识。

单链表的结构

// 单链表节点的结构
public class ListNode {
    int val;
    ListNode next;
    ListNode(int x) { val = x; }
}

相关题目

206.反转链表

反转单链表可以通过迭代实现,也可以通过递归实现。

这篇文章先聊聊如何解决整个链表反转的问题,下一篇我们来看一下如何只反转单链表中的一部分节点。


2. 递归思路

为什么可以用递归?

  1. 大问题可以拆成两个子问题(head 和剩余部分的反转)
  2. 子问题的求解方式和大问题一样(也是 head 和剩余部分的反转)
  3. 存在最小子问题

代码实现

ListNode reverse(ListNode head) {
    if (head.next == null) return head; // base case
    ListNode last = reverse(head.next); // recursive
    head.next.next = head;
    head.next = null;
    return last;
}

这个 reverse function 是干嘛的呢?

只要输入节点 head,将会把以 head 为起点的链表进行反转,并且返回反转后的 head 节点。

Base case

递归必须需要有 base case,它是我们的最小子问题,也是递归的结束点,在这个例子里就是「只有一个节点的时候直接返回自己」。

图解

这个视频讲的很清晰:递归实现 (qq.com)

总的来说,我们要先进行「递」的操作,也就是下图右边的箭头,我们不断的重设 head 节点,并传递到下一层。

到了最后一层(base case)的时候,我们开始进行「归」的操作。

base case 只有一个 head node,所以我们直接返回这个 head node。

倒数第二层,我们的 head node = 78,head.next = 99,我们的目标是让 head node = 99,head.next = 78。

怎么做到这个反转呢?

首先,我们要让 99 指向 78,也就是 head.next.next = head;

接着,因为 78 现在是最后一个 node,它的 next 应该是 null,于是我们进行 head.next = null 的操作。

注意顺序!!!

我们必须先让 99 指向 78,再重设 78 的 next,不然 78 和 99 之间就没法建立起关系了(它俩唯一的联系就是 78.next = 99)。

复杂度比较

递归解法:时间复杂度 = O(N),空间复杂度 = O(N)

迭代解法(本文暂不分析):时间复杂度 = O(N),空间复杂度 = O(1)

递归思路的空间复杂度更高,因为需要堆栈,所以考虑效率的话还是用迭代算法更好。


3. 关于递归的思考

想要判断一个问题是否能用递归解决,我们必须知道 base case(最小问题)以及大问题和小问题之间的递进关系。

也就是说,先从底层开始思考,再向上移动两层,看看这个问题是不是可以重复用同一个方式来解决,如果可以的话,就可以用递归~

递归都是这个思路:先看边界条件怎么解。在这个问题中就是只有一个 node 的情况。第 2 行就处理了。然后假设 n-1 的情况函数已经能处理了,该如何解决 n 的情况,也就是第 4 和第 5 行。