引言
学妹问我递归是什么?应该怎么写?
递归就是在运行过程中调用自己。
构成递归需具备的条件:
1.子问题须与原始问题为同样的事,且更为简单;
2.不能无限制地调用本身,须有个出口,化简为非递归状况处理。
当我们遇到一个递归问题的时候,不要人肉递归!不要人肉递归!不要人肉递归!
方法
1.找最近的重复子问题;
学妹:什么是重复的子问题呢?
我:就是找到解决问题的过程中的【重复的事件】,这个【重复的事件】可以理解为我们的递归函数,虽然递归函数的处理逻辑是一样的,但是所用到的参数的值不一样(参数的值,在递归函数中变化后,作为下一次递归的参数)
2.数学归纳法
先证明一个起点结论正确,用相同的方法(递归函数)推导后续的结论也正确,就像下面的骨牌一样,推倒前一个,后面的也会倒。
学妹:那我应该怎么去写递归函数呢?
我:这里有一个递归函数的代码模板,可以记一下,遇到递归的问题的时候,就能够第一反应把模板写出来。
递归函数模板
public void recur(int level,int param){
//1.递归终结条件
if(level>MAX_LEVEL){
//处理结果
return;
}
//2.处理当前层逻辑
process(level,param);
//3.下探到下一层(递归调用),level和param可能在第二步变化了
recur(level+1,newParam);
//4.清理当前层
}
学妹:那可以举几个例子吗?
我:当然可以了,我们从力扣上找几个递归的问题,用上面的模板套用一下!
括号生成问题
这个问题首先可以把它转换为一个递归问题,就是往一个数组里循环去放左括号或者右括号。
代码如下:
public void generateParenthesis(int n) {
//开始调用递归,初始左括号数量是0,右括号数量是0,结果字符串是""
recur(0,0,n,"");
}
/**
*
* @param left 左括号数量
* @param right 右括号数量
* @param n 括号数
* @param s 字符串
*/
public void recur(int left, int right, int n, String s) {
//1.递归终结条件
if(left==n&&right==n){
//处理结果(这里直接输出结果,直观看到结果)
System.out.println(s);
return;
}
//2.处理当前层逻辑(往格子里添加括号,当前格子既可以添加左括号,也可以添加右括号)
String s1 = s+"(";
String s2 = s+")";
//3.下探到下一层(递归调用)
//左括号没有放完,先放左括号
if(left<n){
recur(left+1,right,n,s1);
}
//右括号比左括号少的时候,放右括号
if(right<left){
recur(left,right+1,n,s2);
}
//4.清理当前层数据
}
输出结果:
上面这个例题,就套用了递归的模板进行求解,循环往格子里放左括号或者右括号,就是重复子问题,然后因为既可以放左括号,也可以放右括号,所以处理当前层逻辑时,有两次递归,参数[s]在每一次递归调用中,都会添加一个左括号或者右括号,当左括号和右括号放完时,即得到一种结果。
二叉树的最大深度
先来整理一下思路:查找二叉树的最大深度,可以分解为,查找左子树的最大深度,查找右子树的最大深度,然后返回两个值中最大的一个,即为二叉树的最大深度。
这个问题的重复子问题:查找左子树的最大深度,查找右子树的最大深度,当节点为空时,返回当前节点的深度。
下面来看一下代码
public int maxDepth(TreeNode root) {
int max = 0;
return calculate(root,max);
}
/**
* 递归函数:计算当前节点的最大深度
* @param root 当前节点
* @param depth 当前节点的深度
* @return 最大深度
*/
public int calculate(TreeNode root, int depth) {
if(root!=null){
//2.处理当前层逻辑(如果当前节点不为空,深度+1)
depth++;
//3.下探到下一层,递归调用
//查找左子树的最大深度
int leftMaxDepth = calculate(root.left, depth);
//查找右子树的最大深度
int rightMaxDepth = calculate(root.right, depth);
//整合结果,返回最大的深度
return Math.max(leftMaxDepth,rightMaxDepth);
}else {
//1.递归终止条件,root==null
return depth;
}
}
学妹:学长好厉害呀!
我:低调低调
学妹:晚上可以一起去看个电影吗?[害羞]表情
我:啊,我晚上还要写代码…
月老:我要用你的红线去绑螃蟹!
总结
1.找到问题的最近重复子问题;
2.数学归纳法;
3.递归函数模板套用;
4.递归终止条件;
5.处理当前层逻辑;
6.下探一层(递归调用);
7.清理当前层数据(如需要的话)。