介绍递归和动态规划

Posted 不做油腻的中年大叔

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了介绍递归和动态规划相关的知识,希望对你有一定的参考价值。

一、官方介绍

暴力递归:

  1. 把问题转化为规模缩小了的同类问题的子问题

  2. 有明确的不需要继续进行递归的条件(base case)

  3. 有当得到了子问题的结果之后的决策过程

  4. 不记录每一个子问题的解

动态规划:

  1. 从暴力递归中来

  2. 将每一个子问题的解记录下来,避免重复计算

  3. 把暴力递归的过程,抽象成了状态表达

  4. 并且存在化简状态表达,使其更加简洁的可能

二、求n!的结果

这道题大家很熟悉了,在递归行为中,

n! = n * (n-1)! = n * (n-1) * (n-2)! = n * (n-1) * ... * 1

最后的状态F(1) = 1,不依赖于任何其他条件

所以动态规划是什么?

这道题的动态规划是 1 * 2 * ... * (n-1) * n = n!这样求结果

 public static long getFactorial1(int n){
       if(n == 1){
           return 1;
      }
       return (long) n * getFactorial1(n-1);
  }

   public static long getFactorial2(int n){
       long result = 1;
       for(int i = 1; i <= n; i++){
           result *= i;
      }
       return result;
  }

三、再看一道题

给你一个二维数组,二维数组中的每个数都是正数,要求从左上 角走到右下角,每一步只能向右或者向下。沿途经过的数字要累 加起来。返回最小的路径和。

直接上暴力递归:

 public static int minPath1(int[][] matrix){
       return process1(matrix, 0, 0);
  }

   public static int process1(int[][] matrix, int i, int j){
       //base-case
       if(i == matrix.length - 1 && j == matrix[0].length - 1){
           return matrix[i][j];
      }
       //行到底了
       if(i == matrix.length - 1 && j != matrix[0].length - 1){
           return matrix[i][j] + process1(matrix, i, j+1);
      }
       //列到底了
       if(i != matrix.length - 1 && j == matrix[0].length - 1){
           return matrix[i][j] + process1(matrix, i+1, j);
      }
       //行列都没到底
       return matrix[i][j] + Math.min(process1(matrix, i+1, j), process1(matrix, i, j+1));
  }

暴力递归就是只看当前,当前值和右边位置或左边位置到右下角的最小距离,就是结果,但是其中会出现很多的状态重叠

四、递归改动态规划

动态规划本来就是从递归中来的,所以很多人都只会见过的题目,没见过的就一点思路也没有了,那么,遇到一道题时,需要先写出递归的版本,然后离开题目,只根据暴力递归的代码改动态规划,步骤如下:

  1. 找递归函数的可变参数,几个就是几维表

  2. 确定最终状态(目标)

  3. 根据base-case确定初始状态(直接能算并没有依赖的状态)

  4. 分析依赖(递归如何调用),递归的返回值确定了数组中的状态变化

  5. 根据依赖的顺序,逆序求表。

拿上题为例,看看下图的分析:

由递归改动态规划的代码:

 public static int minPath2(int[][] matrix){
       if(matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0){
           return 0;
      }
       int row = matrix.length;
       int column = matrix[0].length;
       int[][] result = new int[row][column];
       result[row - 1][column - 1] = matrix[row - 1][column - 1];
       for(int i = column - 2; i >= 0; i--){
           result[row - 1][i] = matrix[row - 1][i] + result[row - 1][i + 1];
      }
       for(int i = row - 2; i >= 0; i--){
           result[i][column - 1] = matrix[i][column - 1] + result[i + 1][column - 1];
      }
       for(int i = row - 2; i >= 0; i--){
           for(int j = column - 2; j >= 0; j--){
               result[i][j] = matrix[i][j] + Math.min(result[i+1][j], result[i][j+1]);
          }
      }
       return result[0][0];
  }



以上是关于介绍递归和动态规划的主要内容,如果未能解决你的问题,请参考以下文章

初级算法-动态规划

算法初级面试题08——递归和动态规划的精髓阶乘汉诺塔子序列和全排列母牛问题逆序栈最小的路径和数组累加成指定整数背包问题

递归和动态规划算法的复盘

动态规划专题1:斐波拉契数列问题的递归和动态规划

递归与动态规划

左神算法第八节课:介绍递归和动态规划(汉诺塔问题;打印字符串的全部子序列含空;打印字符串的全排列,无重复排列;母牛数量;递归栈;数组的最小路径和;数组累加和问题,一定条件下最大值问题(01背包))