名词解释
一.递归
1.什么是递归?
函数自己调用自己的情况
2.为什么要用递归?
本质 主问题->相同的子问题 子问题->相同的子问题
3.如何理解递归
1.递归展开的细节图
2.二叉树中的题目
3,宏观看待递归的过程
1.不要在意递归的细节展开图 2,把递归的函数当做一个黑盒(相信他能解决这个问题)
3.相信这个黑盒一定能完成任务
4.如何写好要给递归?
1.先找到一个相同的子问题!!!!->函数头的设计
2.只关心某一个子问题大的实现->函数体的设计
3.注意一下递归函数的出口即可(直到一个问题不能分割)
二.搜索vs深度优先搜索vs深度优先遍历vs宽度优先遍历vs宽度优先搜索vs暴搜
1.深度优先遍历vs深度优先搜索
宽度优先遍历vs宽度优先搜索
遍历是形式 搜索是目的
2.关系图
暴力枚举一遍所有的情况
搜索(暴搜){bfs,dfs}
3.拓展搜索问题
全排列(树状图)

三.回溯与剪枝
回溯的本质就是深搜
回溯就是在深搜的时候发现不通,然后回到上一个选择的过程
剪枝就是把不同的路径剪掉
递归
汉诺塔问题
1.题目描述
在经典汉诺塔问题中,有3根柱子以及N个不同大小的穿孔圆盘,盘子可以滑入任意一根柱子.一开始,所有的盘子自上而下按照升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面) 移动圆盘受到一下限制:1.每次只能移动一个盘子 2.盘子只能从柱子顶端滑出到下一个柱子 3.盘子只能堆在比他大的盘子上
请编写程序,用栈将所有盘子从第一根柱子移到最后一根柱子上
你需要原地修改栈
例如:
输入:A= [2,1,0] B= [] ,C=[]
输出: C=[2,1,0]
2.算法原理
1.如何解决汉诺塔问题:
N=1,A上只有一个盘子,直接把盘子转移到C上
N=2,把A上的第一个盘子放到辅助柱B,再把A上最后一个放到C上,最后把B上的盘子移动到C上
N=3 想要把上面的两个放到B上(N=2) 然后把最后一个放到C上,然后就需要把B上的两个放到C上(N=2)

N=4 思路:先把上面的3个盘子放到B上(N=3) 然后把最后一个盘子放到C上,然后再把B上到3个盘子按照类似的方法放到C上
N=n思路:把n-1个盘子放到辅助柱上
2.为什么可以用递归:
大问题->相同类型的子问题
小问题->相同类型的子问题
3.如何编写递归的问题:
1.重复子问题->函数头 将x柱子上的一堆盘子,借助y柱子,转移到z柱子上 void dfs(x,y,z,n)
2.只关心某一个子问题在做什么?->函数体
dfs(x,y,z,n-1)
x.back()->z
dfs(y,x,z,n-1)
3.递归的出口
n==1 只需要把盘子放到z柱上
3.代码实现
java
class Solution {
public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {
dfs(A,B,C,A.size());
}
public void dfs(List<Integer> A,List<Integer> B,List<Integer> C,int n){
if(n==1){
C.add(A.remove(A.size()-1));
return ;
}
dfs(A,C,B,n-1);
C.add(A.remove(A.size()-1));
dfs(B,A,C,n-1);
}
}
合并两个有序链表
1.题目描述
将两个升序链表合并为一个新的升序链表并返回,新链表是通过拼接给定的两个链表的所有节点组成的
实例:
输入: l1=[1,2,4] l2 = [1,3,4]
输出:[1,1,2,3,4,4]
2.算法原理
解法:递归
关键:找到重复的子问题
1.重复子问题->函数头的设计,合并两个有序链表 Node dfs(l1,l2)
2.只关心某一个问题在做什么事情->函数体的设计 1.先比较大小,选较小的那一个接在节点后面 他接dfs(l1.next,l2)
3.递归的出口,当l1指向空,返回l2,当l2指向空,返回l1
其实这道题也可以用循环来实现
这里我们可以得到以下结论

3.代码实现
java
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
return dfs(list1,list2);
}
public ListNode dfs(ListNode l1,ListNode l2){
if(l1==null){
return l2;
}
if(l2==null){
return l1;
}
if(l1.val>=l2.val){
l2.next = dfs(l1,l2.next);
return l2;
}else{
l1.next = dfs(l1.next,l2);
return l1;
}
}
}
反转链表
1.题目描述
给你单链表的头结点head,请你反转链表,并返回反转后的链表
实例:1->2->3->4->5
返回:5->4->3->2->1
2.算法原理
解法:递归
第一种视角:从宏观角度看待问题
1.让当前节点后面的链表先逆置,返回头结点
2.当前节点添加到逆置后的链表后面即可
第二种视角:将链表看成一颗树(只需要做一次后序遍历即可)

3.代码实现
java
class Solution {
public ListNode reverseList(ListNode head) {
if(head==null||head.next==null){
return head;
}
ListNode nH=reverseList(head.next);
head.next.next=head;
head.next=null;
return nH;
}
}
两两交换链表中的节点
1.题目描述
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头结点,你必须在不修改节点内部的值的情况下完成本题(只能进行节点交换)
输入:[1,2,3,4]
输出:[2,1,4.3]
2.算法原理
解法:递归
视角:宏观角度看待递归

3.代码实现
java
class Solution {
public ListNode swapPairs(ListNode head) {
if(head==null||head.next==null){
return head;
}
ListNode nH = swapPairs(head.next.next);
head.next.next = nH;
ListNode tmp = head.next;
head.next = nH;
tmp.next = head;
nH = tmp;
return nH;
}
}
Pow(x, n)
1.题目描述
实现Pow(x,n) 即计算x的整数n次幂函数
2.算法原理
解法一:暴力循环
解法二:快速幂
实现快速幂:1.递归 2.循环
1.相同子问题->函数头 int pow(x,n)
2.只关心每个子问题做了什么?->函数体
tmp = pow(x,n/2)
return n%2 == 0?tmp*tmp :tmp*tmp*x;
3.递归出口 n = 0 return1
细节问题:1.n有可能是负数 2.n有可能是无穷大
如果n是负数的话,我们直接返回1.0/pow(x,-n)
3.代码实现
java
class Solution {
public double myPow(double x, int n) {
return n<0?1.0/pow(x,-n):pow(x,n);
}
public double pow(double x,int n){
if(n==0) return 1;
double tmp=pow(x,n/2);
return n%2==0?tmp*tmp:tmp*tmp*x;
}
}