死磕算法-递归和动态规划

暴力递归

  1. 把问题转化为规模缩小了的同类问题的子问题
  2. 有明确的不需要继续进行递归的条件(base case)
  3. 有当得到了子问题的结果之后的决策过程
  4. 不记录每一个子问题的解

那么,怎么最终确定它能不能用“递归”做呢?
看当N取1或2之类最简情况时,问题是否可以解决——然后写程序解决它

动态规划

  1. 从暴力递归中来
  2. 将每一个子问题的解记录下来,避免重复计算
  3. 把暴力递归的过程,抽象成了状态表达
  4. 并且存在化简状态表达,使其更加简洁的可能
P和NP

P指的是我明确地知道怎么算,计算的流程很清楚;而NP问题指的是我不知道怎么算,但我知道怎么尝试(暴力递归)

暴力递归相关

我们知道n!的定义,,可以根据定义直接求解:

int getFactorial_1(int n){
  int res=1;
  for(int i = 1 ; i <= n ; n++){
    res*=i;
  }
  return res;
}

但我们可以这样想,如果知道(n-1)!,那通过(n-1)! * n不就得出n!了吗?于是我们就有了如下的尝试:

int getFactorial_2(int n){
  if(n=1)
    return 1;
  return getFactorial_2(n-1) * n;
}

n!的状态依赖(n-1)!,(n-1)!依赖(n-2)!,就这样依赖下去,直到n=1这个突破口,然后回溯,你会发现整个过程就回到了1 * 2 * 3 * …… * (n-1) * n的计算过程。

汉诺塔问题

该问题最基础的一个模型就是,一个竹竿上放了2个圆盘,需要先将最上面的那个移到辅助竹竿上,然后将最底下的圆盘移到目标竹竿,最后把辅助竹竿上的圆盘移回目标竹竿。即必须将小的圆盘放在大的圆盘上面

知乎对汉诺塔问题的详细解释,十分生动

public class Hanoi {

    public static void process(String source,String target,String auxiliary,int n){
        if (n == 1) {
            System.out.println("move 1 disk from " + source + " to " + target);
            return;
        }
          //尝试把前n-1个圆盘暂时放到辅助竹竿->子问题
        process(source, auxiliary, target, n - 1);
          //将底下最大的圆盘移到目标竹竿
        System.out.println("move 1 disk from "+source+" to "+target);
          //再尝试将辅助竹竿上的圆盘移回到目标竹竿->子问题
        process(auxiliary,target,source,n-1);
    }

    public static void main(String[] args) {
        process("Left", "Right", "Help", 3);
    }
}
打印一个字符串的子序列

子串和子序列并不是一个意思
例如:一个字符串 awbcdewgh
他的子串: awbc. awbcd awbcde …很多个子串 但是都是连续在一起
他的子序列: abc . abcd abcde … 很多个子序列 但是子序列中的字符在字符串中不一定是连在一起的, 但是 子序列一定是单调的, (即字符之间ASCII单调递增或单调递减)

在这里插入图片描述

/**
     * 本级任务:将index之后(包括index)位置上的字符和index上的字符交换,将产生的所有结果扔给下一级
     * @param str
     * @param index    
     */
public static void printAllPermutations(char[] chs,int index) {
  //base case
  if(index == chs.length-1) {
    System.out.println(chs);
    return;
  }
  for (int j = index; j < chs.length; j++) {
    swap(chs,inde***rintAllPermutations(chs, index+1);
  }
}

public static void swap(char[] chs,int i,int j) {
  char temp = chs[i];
  chs[i] = chs[j];
  chs[j] = temp;
}

public static void main(String[] args) {
  printAllPermutations("abc".toCharArray(), 0);
}
母牛生牛问题

母牛每年生一只母牛,新出生的母牛成长三年后也能每年生一只母牛,假设不会死。求N年后,母牛的数量。

在这里插入图片描述

那么求第n年母牛的数量,按照此公式顺序计算即可。

暴力递归改为动态规划

动态规划由暴力递归而来,是对暴力递归中的重复计算的一个优化,策略是空间换时间

最小路径和

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

/**
     * 从矩阵matrix的(i,j)位置走到右下角元素,返回最小沿途元素和。每个位置只能向右或向下
     * 
     * @param matrix
     * @param i
     * @param j
     * @return 最小路径和
     */
public static int minPathSum(int matrix[][], int i, int j) {
  // 如果(i,j)就是右下角的元素
  //终止条件
  if (i == matrix.length - 1 && j == matrix[0].length - 1) {
    return matrix[i][j];
  }
  // 如果(i,j)在右边界上,只能向下走
  if (j == matrix[0].length - 1) {
    return matrix[i][j] + minPathSum(matrix, i + 1, j);
  }
  // 如果(i,j)在下边界上,只能向右走
  if (i == matrix.length - 1) {
    return matrix[i][j] + minPathSum(matrix, i, j + 1);
  }
  // 不是上述三种情况,那么(i,j)就有向下和向右两种决策,取决策结果最小的那个
  int left = minPathSum(matrix, i, j + 1);
  int down = minPathSum(matrix, i + 1, j);
  return matrix[i][j] + Math.min(left,down );
}

public static void main(String[] args) {
  int matrix[][] = { 
    { 9, 1, 0, 1 }, 
    { 4, 8, 1, 0 }, 
    { 1, 4, 2, 3 } 
  };
  System.out.println(minPathSum(matrix, 0, 0)); //14
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值