一、四条基本法则
- 基准情形:基准情形下,无需递归就可以解出;
- 不断推进:每一次递归调用都必须要使状况朝向一种基准情形推进;
- 设计法则:假设所有的递归调用都能运行;
- 合成效益法则:在求解一个问题的同一实例时,切勿在不同的递归调用中做重复性的工作。
// LeetCode 206. 反转链表
ListNode reverse(ListNode head) {
if (head.next == null) return head; //基准情形
ListNode last = reverse(head.next); //不断推进;设计法则:调用递归时,不要再次跳进递归,直接用就好。
head.next.next = head;
head.next = null;
return last;
二、构造递归思路
- 明确递归函数定义:后面调用时直接使用,不要再次跳进递归;
- 调用已定义的递归函数:找到初始逻辑,对剩余部分调用递归函数,以满足“不断推进”;
- 补全递归函数逻辑;
- 补全基准情形:跳入递归,找到终止逻辑,便是基准情形;
// LeetCode 206. 反转链表
// 1.明确递归函数定义:输入一个节点 head,将「以 head 为起点」的链表反转,并返回反转之后的头结点。
ListNode reverse(ListNode head) {
if (head.next == null) return head; // 4.补全基准情形:跳入递归,找到基准情形。
ListNode last = reverse(head.next); // 2.调用已定义的递归函数:满足“不断推进”,链表的头节点head每次前进一个节点。
// 3.补全递归函数逻辑
head.next.next = head;
head.next = null;
return last;
三、经典题型解析
这类经典题型,可以直接套用对应的解题方法,不用再代入上面的通用方法。
3.1 遍历链表
- LeetCode 25. K 个一组翻转链表:这道题有助于理解初始逻辑和终止逻辑,好好品一下。
// 25. K 个一组翻转链表
ListNode reverseKGroup(ListNode head, int k) {
if (head == null) return null;
ListNode a, b;
a = b = head;
for (int i = 0; i < k; i++) {
if (b == null) return head; // 终止逻辑:不足 k 个,不需要反转,base case。
b = b.next;
}
ListNode newHead = reverse(a, b); // 初始逻辑:反转前 k 个元素。
a.next = reverseKGroup(b, k);
return newHead;
}
3.2 遍历二叉树
参考
labuladong的算法小抄
LeetCode 206. 反转链表
LeetCode 92. 反转链表 II
LeetCode 25. K 个一组翻转链表