LeetCode--递归--概念总结和经典题目解析汇总(Java版)

目录

递归的概念总结:

1.递归的基础概念

2.递归的Java代码模板

3.递归思维要点:

题目一:斐波那契数列

题目二:爬楼梯

题目三:二叉树的最大深度

题目四:检验二叉搜索树

题目五:括号生成


递归的概念总结:

1.递归的基础概念

我之前的博客总结

https://blog.csdn.net/yezonghui/article/details/106437881进行过递归的总结。

2.递归的Java代码模板

(模板一定要牢记,灵活使用)

public void recur(int level , int param){
    // terminator
    if(level > MAX_LEVEL){
        return;
    }
    // process current logic
    process(level, param);
    // drill down
    recur(level:level+1 , newParam);
    // restore current status
}

3.递归思维要点:

 1)不要人肉进行递归(又费时间又费脑子,还不好写程序,不如直接进行递归代码

 2)找到最近最简方法,将其拆解成可重复的问题(重复子问题

 3)数学归纳法思维

题目一:斐波那契数列

(题目链接:https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof/)

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下:

F(0) = 0,   F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

示例 1:

输入:n = 2
输出:1
示例 2:

输入:n = 5
输出:5

方法一:自己的方法:采用递归

Java源代码:

class Solution {
    public int fib(int n) {
        return (f(n));

    }
    public static int f(int a){
        if(a ==1){
            return 1;
        }else if(a ==0){
            return 0;
        }
        else{
            return (f(a-1)+f(a-2))%1000000007;
        }
    }
}

【注意:】上述方法超时,需要进行改进或者另寻它法

方法二:改进的递归方法:由于上面的递归中会重复计算,因此我们可以把一些一些计算过的值存起来。

LeetCode--递归--概念总结和经典题目解析汇总(Java版)_第1张图片

我们看到上面相同颜色的都是重复计算,当n越大,重复的越多,所以我们可以使用一个map把计算过的值存起来,

每次计算的时候先看map中有没有,如果有就表示计算过,直接从map中取,如果没有就先计算,计算完之后再把结果存到map中。

即:采用hashmap结构把已经计算过的值用hashmap存储下来

Java源代码:

import java.util.HashMap;
import java.util.Map;

public class fblx {
    public static void main(String[] args) {
        System.out.println(f(5,new HashMap<>()));
    }

    public static int f(int n, Map map){
        if(n<2){
            return n;
        }
        if(map.containsKey(n)){
            return map.get(n);
        }
        int first = f(n-1,map);
        map.put(n-1,first);
        int second = f(n-2,map);
        map.put(n-2,second);
        int result = (first+second)%1000000007;
        map.put(n,result);
        return result;
    }
}

【注意】:return 的各处返回,到底如何返回

【注意】:1)hashmap要定义为全局变量

                  2)每次f(n),f(n-1),f(n-2)计算出来的结果都要随时存储到hashmap中。

class Solution {
    Map map = new HashMap();
    public int fib(int n) {
        if(n==0 ){
            return 0;
        }else if(n==1){
            return 1;
        }else{
            if(map.containsKey(n))
            {
                return map.get(n);
            }
            int first = fib(n-1);
            map.put(n-1,first);
            int second = fib(n-2);
            map.put(n-2,second);
            int result = (first+second)%1000000007;
            map.put(n,result);
            return result;
        }
    }
}

题目二:爬楼梯

(链接:https://leetcode-cn.com/problems/climbing-stairs/)

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定 n 是一个正整数。

示例 1:

输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1.  1 阶 + 1 阶
2.  2 阶
示例 2:

输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1.  1 阶 + 1 阶 + 1 阶
2.  1 阶 + 2 阶
3.  2 阶 + 1 阶

方法:递推公式f(n) =  f(n-1) + f(n-2) (注意理解和记忆)

for(int i =3;i<=n;i++){
                f3 = f1 + f2;
                f2 = f3;
                f1 = f2;
}

public class climbstair {
    public static void main(String[] args) {
        int n =5;
        int f1 = 1;
        int f2 =2;
        int f3 = 0;
        if(n<=2) {
            System.out.println(n);
        }
        else{
            for(int i =3;i<=n;i++){
                f3 = f1 + f2;
                f2 = f3;
                f1 = f2;
            }
        }
        System.out.println(f3);
    }
}

[注意]:此处利用for循环,进行记忆。

和题目一一样,我们可以采用同样的代码来解题

class Solution {
    Map map = new HashMap();
    public int climbStairs(int n) {
        if(n <=2){
            return n;
        }else{
            if(map.containsKey(n)){
                return map.get(n);
            }
            int first = climbStairs(n-1);
            map.put(n-1, first);
            int second = climbStairs(n-2);
            map.put(n-2, second);
            int result = first+second;
            map.put(n,result);
            return result;
        }
    }
}

题目三:二叉树的最大深度

(链接:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/)

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

示例:
给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7
返回它的最大深度 3 。

思路:不要人力递归,要从数学角度和递归思想出发。

          每次都是比较左右两个子树的深度,那边子树深度大,选择那边。

         延续这个思想,一直递归下去。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    int l = 1;
    int r =1;
    public int maxDepth(TreeNode root) {
        if(root == null){
            return 0;
        }
        int l = maxDepth(root.left);
        int r = maxDepth(root.right);
        return Math.max(l,r)+1;
    }
    
}

记住上面递归的代码,类似于分治的思想,牢记这好的代码套路

题目四:检验二叉搜索树

(链接:https://leetcode-cn.com/problems/validate-binary-search-tree/)

给定一个二叉树,判断其是否是一个有效的二叉搜索树。

假设一个二叉搜索树具有如下特征:

节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树

思路:按照中序递归的想法。

         思维点:每棵子树也需要满足中序,因此需要返回每次递归的结果。

                       即只要有某一颗子树不满足条件,立即返回false

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    TreeNode prev ;
    public boolean isValidBST(TreeNode root) {
        if (root == null)
        return true;
    //访问左子树
    if(!isValidBST(root.left) ){
        return false;
    }
    //if (!isValidBST(root.left))
    //    return false;
    //访问当前节点:如果当前节点小于等于中序遍历的前一个节点直接返回false。
    if (prev != null && prev.val >= root.val)
        return false;
    prev = root;
    //访问右子树
    if(!isValidBST(root.right) ){
        return false;
    }
    return true;
    //return isValidBST(root.right);
    //if (!isValidBST(root.right))
      //  return false;
    //return true;
   
    }
    
}

或者类似的代码

class Solution {
   
    public long pre = Long.MIN_VALUE;
    public boolean isValidBST(TreeNode root) {
        return inorder(root);
    }
    public boolean inorder(TreeNode root){
        if(root == null){
            return true;
        }
        
        if(!inorder(root.left)){
            return false;
        }
        if(pre >= root.val){
            return false;
        }
        pre=root.val;
        return inorder(root.right);

    }
}

题目五:括号生成

链接:https://leetcode-cn.com/problems/generate-parentheses

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

示例:

输入:n = 3
输出:[
       "((()))",
       "(()())",
       "(())()",
       "()(())",
       "()()()"
     ]

思路:对于每一个位置,有两种情况,要不生成左括号,那么生成右括号

          因此可以采用递归的思想

关键:是否可以生成左括号:当左括号数量没有达到题目要求的上界

          是否可以生成右括号:当右括号数量小于左括号的数量。

class Solution {
    List res = new ArrayList();
    public List generateParenthesis(int n) {
        generate1(0,0,n,"");
        return res;

    }
    public void generate1(int left, int right, int n, String s){
        if(left == n && right == n){
            res.add(s);
            //System.out.println(s);
            return;
        }
        if(left 

[注意]:对于这种会生成许多中间结果,并且需要把中间结果放置在最终结果里面,

我们一般把最终结果定义为全局变量,把中间结果放在递归函数里面,充当递归函数的一个参数。

(另外注意中间结果的类型:是基本数据类型,还是引用数据类型,后续递归是否会引起中间结果的动态改变)

题目六:数值的整数次方

(题目链接https://leetcode-cn.com/problems/shu-zhi-de-zheng-shu-ci-fang-lcof/)

实现函数double Power(double base, int exponent),

求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。

思路:采用递归思想:要求Math.pow(x,n),我们先要求出Math.pow(x,n/2)

两个注意的地方:

1)n可能为负数,所以要进行预处理,当n为负数的时候,x变为1/x,n取绝对值

2)n取绝对值之后要分奇数和偶数两种情况套路

class Solution {
    public double myPow(double x, int n) {
        if(n<0){
            x = 1/x;
            n = -n;
        }
        return recur(x,n);
    }
    public double recur(double x, int n){
        if(n==0){
            return 1.00000;
        }
        double half = recur(x,n/2);
        if(n%2 ==0){
            return half * half;
        }else{
            return half*half*(double)x;
        }
    }
}

题目六:搜索二维矩阵||

(题目链接:https://leetcode-cn.com/problems/search-a-2d-matrix-ii/)

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:

每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
 

LeetCode--递归--概念总结和经典题目解析汇总(Java版)_第2张图片
输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5
输出:true

思路:采用不断减少搜索范围的方法:即把一个大问题慢慢变成一个小问题

我们从表格的最左下脚(视为当前元素)出发,当该元素大于目标值的时候,我们

把当前元素往上一行,反之,当当前元素小于目标值的时候,我们把当前元素向

右边移动一列。

class Solution {
    boolean flag = false;
    public boolean searchMatrix(int[][] matrix, int target) {
        search(matrix,target,matrix.length-1,0);
        return flag;
    }
    public void search(int[][] matrix, int target,int x,int y) {
        if(x>=0 &&x< matrix.length && y>=0 && y< matrix[0].length){
            if(matrix[x][y] == target){
                flag = true;
            }else if(matrix[x][y] > target){
                search(matrix,target,x-1,y);
            }else if(matrix[x][y] < target){
                search(matrix,target,x,y+1);
            }
        }  
    }
}

【注意1】:我们可以设定一个flag标志,当找到了标志为true,否则一直不改变flag的值;

【注意2】:思维误区:二维数组array[x][y]和矩阵matrix[x,y]对应坐标是不一样的表示呀!!!

                                     例如,最左下脚二维数组表示:array[array.length][0]但是用矩阵表示为:

                                     matrix[0,array[0].length-1]

 

你可能感兴趣的:(leetCode)