递归法扳断二叉树 和动态规划争老大 以及解决数学问题

1、leetcode 104.二叉树的最大深度

2、leetcode 62.不同路径

3、剑指 Offer 16. 数值的整数次方

f(递归法){
return f(递归法-1) +f(递归法-2);
}

一、二叉树的最大深度

在这里插入图片描述

先要说一点:所有二叉树的套路都是一样的

void traverse(TreeNode root) {
    // 前序遍历
    traverse(root.left)
    // 中序遍历
    traverse(root.right)
    // 后序遍历
}

二叉树的算法思想的运用广泛,甚至可以说,只要涉及递归,都可以抽象成二叉树的问题。

在这道题中,traverse(TreeNode root)就是maxDepth(TreeNode root)

而递归需要找到边界条件:当root为空,也即是到达叶子节点的时候结束,对应代码if(root == null) return 0;

如果root不为空,那么就应该遍历他的左右子节点的深度,选择返回值最大的,然后再加上自身,对应代码时Math.max(left, right) + 1

知道了这两个条件代码就可以写出来啦

class Solution {
    public int maxDepth(TreeNode root) {
      //递归终止条件
        if(root == null) {
            return 0;
        } else {
          //左子树的最大深度
            int left = maxDepth(root.left);
          //右子树的最大深度
            int right = maxDepth(root.right);
          //选择左右子树最大的深度再加一
            return Math.max(left, right) + 1;
        }

    }
}

写完代码,再来想一想时间复杂度和空间复杂度都是多少把

因为是左右子树都遍历了,每个节点都遍历一遍了,所以时间复杂度是O(n)

递归函数需要栈空间,栈空间取决于递归的深度,所以空间复杂度等于二叉树的最大高度,O(height)

二、不同路径

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-29Vfw9Ql-1621171633026)(递归刷题.assets/image-20210516201812781.png)]

递归第一步就是先找到边界条件,也就是什么时候结束递归:当机器人走到右下角的时候,递归结束。

为了更形象一些,添加了两个参数i和j,代表机器人所在的位置,m和n就是网格右下角,也就是目标位置。

所以现在递归的边界条件很清楚,就是当i=m && j==n的时候,机器人到达右下角的时候,递归结束 ,对应代码是:if (i == m && j == n) return 1; ,机器人不移动也算一条路径所以要返回1。

如果机器人越界了,则返回0,对应代码:if (i > m || j > n) return 0;

递归嘛,写完边界条件,就要继续加递归函数了,因为机器人从左上角往右下角走,所以他只有两种移动方向,向下走和向右走。

函数中的map,可以先忽略不看,不影响

机器人向下走有几条路径:int down = path(i + 1, j, m, n, map);

机器人向右走有几条路径:int right = path(i, j + 1, m, n, map);

总的路径就是向下和向右加起来,对应代码int sum = down + right;

hxdm,看到在这里是不是觉得就完事了,写代码的手已经跃跃欲试了,都在喊我可以我可以,结果一些代码就扑街了……😂

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y1VbL5sx-1621171633027)(递归刷题.assets/image-20210516201756935.png)]

这里的超出时间限制,因为有大量的重复计算,直接上图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qwbD4bO2-1621171633028)(递归刷题.assets/image-20210516202249500.png)]

如果我们要计算f(6),其实只要计算一次 f(5) f(4) f(3) f(2) f(1) ,但是在图中可以看到我们有好多重复的计算。

为了解决重复计算,就用Map把计算好的存起来,如果用过就直接从Map里拿,如果Map里没有,再计算然后放到Map里面,(此处@MySQL和Redis两位好兄弟)

流程图来梳理一遍逻辑:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i6fLYIpq-1621171633029)(递归刷题.assets/image-20210516204007798.png)]

代码:

class Solution {
    public int uniquePaths(int m, int n) {
        //表示从坐标(1,1)到(m,n)有几条路径
        return path(1, 1, m, n,new HashMap<>());
    }

    public int path(int i, int j, int m, int n, Map<String, Integer> map) {
        if (i > m || j > n) {
            return 0;
        }
        if (i == m && j == n) {
            return 1;
        }
        String key = i + "-" + j;
      //如果已经遍历过,就直接返回,不必继续计算了
        if (map.containsKey(key)) {
            return map.get(key);
        }
        //向下走有几条路
        int down = path(i + 1, j, m, n, map);
        //向右走有几条路
        int right = path(i, j + 1, m, n, map);
        //向下的和向右的加起来 就是总路数
        int sum = down + right;
      //放入map中,避免重复计算
        map.put(key, sum);
        return sum;
    }
}

最后虽然通过了,但是效率很低……

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RENZmvLH-1621171633029)(递归刷题.assets/image-20210516204157107.png)]

关键时刻还得看动态规划,这次递归法是小弟,动态规划是大哥😁

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5VZTVocF-1621171633030)(递归刷题.assets/image-20210516204414778.png)]

class Solution {
    public int uniquePaths(int m, int n) {
    int[][] dp = new int[m][n];
    //第一列都是1
    for (int i = 0; i < m; i++) {
        dp[i][0] = 1;
    }
    //第一行都是1
    for (int i = 0; i < n; i++) {
        dp[0][i] = 1;
    }

    //递推公式
    for (int i = 1; i < m; i++)
        for (int j = 1; j < n; j++)
            dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
    return dp[m - 1][n - 1];
    }
}

三、数值的整数次方

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f60vuA0w-1621171633030)(递归刷题.assets/image-20210516211920340.png)]

这道题是要把幂次n分类讨论了:

  1. 如果n==0,返回1 x0 =1
  2. 如果n小于0,结果是1/(x-n) 比如x-2,就看做1/x2
  3. 如果n为奇数,结果是x*(xn-1) 比如x5 看做x * x4
  4. 如果n为偶数,结果是(x*x)n/2 比如x4 看做 x2 * x2

分类讨论,然后递归即可

class Solution {
    public double myPow(double x, int n) {
     			//n为0
        if (n == 0) {
            return 1;
          //n小于0
        } else if (n < 0) {
            return 1 / (x * myPow(x, -n - 1));
          //n为奇数
        } else if (n % 2 == 1) {
            return x * myPow(x, n - 1);
          //n为偶数
        } else {
            return myPow(x * x, n / 2);
        }
    }
}

递归几次复杂度就是多少

n为偶数,递归logn(2为底数)次

n为奇数,需要递归,让n减一次变成偶数

所以,时间复杂度和空间复杂度都是O(logn)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值