《算法》系列—递归

引言

学妹问我递归是什么?应该怎么写?

递归就是在运行过程中调用自己。

构成递归需具备的条件:

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.清理当前层数据(如需要的话)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值