要点备忘
递归的解题要点在于确定好边界范围和范围内要做的操作。
在除了《爬楼梯》之外的大部分题中,递归调用行通常是在操作之前,在思路不清前可以截取边界值外的数以此推导三层,但事不过三 (时间不够) 。不宜沉浸在推导中,记住前面两个操作。
LeetCode 70. 爬楼梯
- 题目:每次可以爬 1 或 2 个台阶,爬到第
n阶有多少种方法? - 递归公式:
f(n) = f(n-1) + f(n-2)(类似斐波那契数列)。 - 进阶:用记忆化递归或动态规划优化。
public int climbStairs(int n){
if(n==1){
return 1;
}
if(n==2){
return 2;
}
return climbStairs(n-1)+climbStairs(n-2);
}
以上是代码最少的递归,由于过程中偶尔会递归到重复数字,为了降低消耗(递归调用每次都会创建栈帧),可采用记忆化递归。
public static int climbStairs(int n, int[] memo) {
if(memo[n]>0){ return memo[n]; }
if(n==1){ return 1; }
else if(n==2){ return 2; }
else{
memo[n] = climbStairs(n-1, memo) + climbStairs(n-2, memo);
}
return climbStairs(n-1,memo)+climbStairs(n-2, memo);
}
public static void main(String[] args) {
int n=10;
int[] memo = new int[n+1];
System.out.println(climbStairs(n,memo));
System.out.println(Arrays.toString(memo));
}
这道题的迭代写法已属于动态规划范畴,捋得我晕头转向的所以这里先不贴了,本质只是个日常容易查看的记录贴,动态规划有缘再看。
LeetCode 206. 反转链表
- 题目:反转一个单链表。
- 递归解法:递归到链表末尾,然后逐层反转指针。
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode newHead = reverseList(head.next);
head.next.next = head;
head.next = null;
return newHead;
}
反转核心:head.next.next 置为当前 head,再设置当前 head.next 为空,将 当前节点与下一节点间的连接断开,返回递归调用的 newHead。
- 迭代解法:用双指针(经典迭代题)。
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode curr = head;
while(curr!=null){
ListNode next = curr.next;
curr.next = pre;
pre = curr;
curr = next;
}
}
LeetCode 24. 两两交换链表中的节点
- 题目:给定链表,两两交换相邻节点。
- 递归思路:
head.next = swapPairs(next.next); next.next = head;。
public ListNode swapPairs(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode newHead =head.next;
head.next = swapPairs(newHead.next);
newHead.next = head;
return newHead;
}
如前述要点备忘所说,这里的递归调用就是在 “范围内要做的事情” 之前,而 “要做的事情” 就是交换两个节点:head.next = head;,而 head.next 已经被递归执行的结果取代了。最后 return newHead 即可。
- 迭代解法
public ListNode swapPairs(ListNode head) {
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode temp = dummyHead;
while (temp.next != null && temp.next.next != null) {
ListNode node1 = temp.next;
ListNode node2 = temp.next.next;
temp.next = node2;
node1.next = node2.next;
node2.next = node1;
temp = node1;
}
return dummyHead.next;
}
迭代解法主要是增加了哑结点,避免对没有前驱结点的头结点的特殊处理。