LeetCode 120. Triangle

问题描述

这里写图片描述

问题分析

  • 在三角形中选择一条自顶向下的路径,使得路径和最小。
  • 最值问题动态规划的经典题目,只要设计出递归关系,然后根据递归关系便能写出动态规划的形式。
    • 首先要明确递归函数的意义: getMinSum(List<List<Integer>> triangle, int i, int j) : 表示从triangle.get(i).get(j) 开始(包括),向下的最小路径和
    • 递归关系为:return triangle.get(i).get(j) + Math.min(getMinSum(triangle, i + 1, j), getMinSum(triangle, i + 1, j + 1))
    • base case 便是 当i == triangle.size() 便返回0
  • 以上是自顶向下的思路,若自底向上看,那么便是动态规划的思路
    • dp[i][j]表示从triangle.get(i).get(j)开始的最小路径和。
    • 依赖关系dp[i][j] = list.get(j) + Math.min(dp[i + 1][j], dp[i + 1][j + 1]);
    • 初始条件i == size时,那一行全是0,然后从下到上,从左到右依次填好这个二维表。
  • 上述动态规划的思路用的空间复杂度为 O(N^2) ,但题目要要求是 O(N)的空间复杂度,这就有牵扯出动态规划的空间优化问题,根据依赖关系可知,该行的元素只依赖于下一行的元素,严格来说,dp[i][j] 只依赖于 dp[i + 1][j]dp[i + 1][j + 1] ,所以如果计算完第 i行,那么 第 i+1 行便永远不会再用到,更严格来说,只要计算完 dp[i][j],dp[i + 1][j] 便永远不会用到,所以我们只用一行数组滚动更新即可,从下到上滚动,从左到右更新(可以防止覆盖)。这样,便只用了 O(N)的时间复杂度完成了该问题。

经验教训

  • 如何设计递归函数,明确递归函数的意义才是根本
  • 对于动态规划,要明确dp[i] 或者 dp[i][j] 的意思
  • 动态规划的空间优化问题。

代码实现

  • 递归法(会TLE)
public int minimumTotal(List<List<Integer>> triangle) {
        if(triangle == null || triangle.size() == 0 || triangle.get(0) == null || triangle.get(0).size() == 0) {
            return 0;
        }
        return getMinSum(triangle, 0, 0);
    }

    //getMinSum(List<List<Integer>> triangle, int i, int j) : 表示从triangle.get(i).get(j) 开始(包括),向下的最小路径和
    public int getMinSum(List<List<Integer>> triangle, int i, int j) {
        if (i == triangle.size()) {
            return 0;
        }
        return triangle.get(i).get(j) + Math.min(getMinSum(triangle, i + 1, j), getMinSum(triangle, i + 1, j + 1));
    }
  • 动态规划
public int minimumTotal(List<List<Integer>> triangle) {
        if(triangle == null || triangle.size() == 0 || triangle.get(0) == null || triangle.get(0).size() == 0) {
            return 0;
        }
        int row = triangle.size() + 1;
        int col = row;
        //dp[i][j]表示从triangle.get(i).get(j)开始的最小路径和。
        int[][] dp = new int[row][col];
        for (int j = 0; j < col; ++j) {
            dp[row - 1][j] = 0;
        }
        for (int i = row - 2; i >= 0; --i) {
            List<Integer> list = triangle.get(i);
            for (int j = 0; j < list.size(); ++j) {
                dp[i][j] = list.get(j) + Math.min(dp[i + 1][j], dp[i + 1][j + 1]);
            }
        }
        return dp[0][0];
    }
  • 动态规划空间优化
public int minimumTotal(List<List<Integer>> triangle) {
        if(triangle == null || triangle.size() == 0 || triangle.get(0) == null || triangle.get(0).size() == 0) {
            return 0;
        }
        int size = triangle.size();
        int[] dp = new int[size + 1];
        for (int i = size - 1; i >= 0; --i) {
            List<Integer> list = triangle.get(i);
            for (int j = 0; j < list.size(); ++j) {
                dp[j] = list.get(j) + Math.min(dp[j], dp[j + 1]);
            }
        }
        return dp[0];
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值