
文章目录
一、基础认知
1. 看待递归
我们对于递归,按照三步理解
理解递归展开图-->做二叉树的题-->用宏观视角看待递归(不在意细节、递归看成未知惊喜、相信这个惊喜能够返回正确结果)
2. 写好递归
- 首要:将大问题细分,找到相同的子问题,这关乎函数头(参数)的设计
- 其次:我们只关心每一个子问题是怎么解决的,这关乎函数体的书写
- 最后:设置递归出口,即设计地柜的结束条件,避免死递归
3. 深度优先遍历——DFS&宽度优先遍历——BFS
对于DFS
顾名思义,就是一条路走到底,像我们之前写的二叉树的三种遍历就是这样
只有遇到空节点,我们才开始返回
之前在网上听到的回溯,本质上就是指的深搜时的返回情况
比较经典的有全排列问题以及迷宫问题
对于BFS
我们之前写过层序遍历,就是一层一层抽丝剥茧,逐步深入
二、小试牛刀
1. 汉诺塔问题
题目链接
这题我们之前有讲过,我们这里再讲一遍

因此我们在递归的时候,柱子的顺序是会变的
但是递归后,我们每一步的操作还是一样的,就是借助一个中间柱子,把一个盘子从一个柱子移动到另外一个柱子
class Solution {
public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {
hanotaChild(A,B,C,A.size());
}
private void hanotaChild(List<Integer> A, List<Integer> B, List<Integer> C,int count){
if(count == 1){
C.add(A.remove(A.size()-1));//只剩一个盘子的时候直接:A-->C
return;
}
hanotaChild(A,C,B,count-1);//把A柱上的n-1个盘子:A-->C-->B(C是辅助)
C.add(A.remove(A.size()-1));//此时A柱上只有一个大盘子:A-->C
hanotaChild(B,A,C,count-1);//此时再把B柱上的n-1个盘子:B-->A-->C(A是辅助)
}
}
2. 合并两个有序链表
题目链接
这一题核心就是寻找两个指针的最小值
两个指针分别是两个链表头节点
我们函数体就是
如果list1指针的值大于list2指针的值,我们直接list1.next = dfs(list1.next,list2)
反之,我们list2.next = dfs(list1,list2.next)
最后出口如果一个指针空了,返回另一个指针,如果都空了,递归就结束了
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
//终止条件
if(list1 == null){
return list2;
}else if(list2 == null){
return list1;
}
if(list1.val >= list2.val){
list2.next = mergeTwoLists(list1,list2.next);
return list2;//作为新的头节点
}else{
list1.next = mergeTwoLists(list1.next,list2);
return list1;//作为新的头节点
}
}
}
3. 反转链表
题目链接
这一题我们要递归到最后一个节点
然后让最后一个节点作为新的头节点
修改每个节点指针指向,然后一直返回这个新的头节点就好

/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null){
//head == null处理的是空链表的情况,而head.next == null才是递归终止的真正条件
return head;
}
//注意newHead是在递归最深处被确定的,之后回归的时候不会再改变
ListNode newHead = reverseList(head.next);
//反转链表操作
head.next.next = head;
head.next = null;
//注意每一次return的newHead都是同一个节点,即原链表的尾节点,从未改变
return newHead;
}
}
4. 两两反转链表中的节点
题目链接
这一题我们需要记录当前的两个节点,而且回溯的时候返回两个节点反转后的节点
而且不要忘记最先要记录两个节点后面的一个节点
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode swapPairs(ListNode head) {
if(head == null || head.next == null){
return head;
}
//先从最后一个子问题开始
ListNode tmp = swapPairs(head.next.next);
//两两反转
ListNode node = head.next;
node.next = head;
//再把后面剩下链表接上
head.next = tmp;
//再返回当前的反转后的第一个链表
return node;
}
}
5. 快速幂计算
题目链接
这一题我们可以把一个幂进行拆分
3^8 = 3^4*3^4 = 3^2*3^2 = 3^1*3^1*3^1*3^1 = 3^0*3^0*3^0*3^0*3^0*3^0*3^0*3^0
如果我们次方不是二的倍数,比如3^5,我们可以拆分成3^4*3即可
如果是负数次幂呢?我们直接1/正数次幂计算结果
class Solution {
public double myPow(double x, int n) {
return n < 0 ? 1.0/pow(x,n) : pow(x,n);
}
private double pow(double x,int n){
if(n == 0){
return 1.0;
}
double tmp = pow(x,n/2);
return n % 2 == 0 ? tmp*tmp : tmp*tmp*x;
}
}
1268

被折叠的 条评论
为什么被折叠?



