Java刷题笔记14:三角形最小路径和

通过层序遍历和动态规划方法解决给定三角形中自顶向下的最小路径和问题,动态规划中利用滚动数组优化,降低空间复杂度。核心在于更新每个节点的最小路径和,考虑边界条件并逆序遍历以保持滚动数组的有效性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。

相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。

示例

在这里插入图片描述
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。

题目链接

层序遍历

用一个容器保存到达一层所有的可能结果,对于如示例中的5,可由3,4到达,则保存其中一个较小的值:
[2],
[5,6],
[11,10,13],
[15,11,18,16]
计算结果如上图所示。一层一层计算,然后在最后结果中取最小值即可。

 public static int minimumTotal(List<List<Integer>> triangle) {
      //层序遍历,计算最小路径
      //int maxLength = triangle.get(triangle.size() - 1).size();//底层长度
      Deque<Integer> deque = new LinkedList<>();
      deque.addLast(triangle.get(0).get(0));
      for (int i = 1; i < triangle.size(); i++) {
          List<Integer> tmpList = triangle.get(i);
          int queSize = deque.size();
          for (int j = 0; j < queSize; j++) {
              int tmp = deque.pollFirst();
              deque.addLast(tmp + tmpList.get(j));
              deque.addLast(tmp + tmpList.get(j + 1));
          }
          merge(deque);
      }
      int min = deque.pollLast();
      while (!deque.isEmpty()) {
          min = Math.min(min, deque.pollLast());
      }
      return min;
  }

  private static void merge(Deque<Integer> deque) {
      //中间元素两两合并,取较小者
      if (deque.size() <= 3)
          return;
      int first = deque.pollFirst();
      int last = deque.pollLast();
      int size = deque.size();
      for (int i = 0; i < size / 2; i++) {
          deque.addLast(Math.min(deque.pollFirst(), deque.pollFirst()));
      }
      deque.addFirst(first);
      deque.addLast(last);
  }

动态规划

在写以上代码的时候发现,到达坐标(i,j)的最小路径之和 与到达坐标(i-1,j-1)以及坐标(i-1,j)有关,取其中一个较小值然后加上当前坐标的值就得到了到达当前坐标的最小路径。对与边界j=0则只和(i-1,j)有关,对于j=i(意味着这个坐标处与第i层的最右边)则只与(i-1,j-1)有关。
所以我们可以采用动态规划来解决此问题。dp[i][j]保存到达坐标(i,j)的最小路径和。动态转移方程也在之前的分析过程给出了。if(j==0)dp[i][j]=dp[i-1][j]+triangle[i][j].
if(j==i)dp[i][j]=dp[i-1][i-1]+triangle[i][i];
对于其它情况则有
dp[i][j]=min(dp[i-1][j-1],dp[i-1][j])+triangel[i][j]

public static int minimumTotalDP(List<List<Integer>> triangle) {
        //dp[i][j]表示顶点到达坐标(i,j)的最小路径长度
        //结果在dp[n-1][]这一维数组取最小者
        int n = triangle.size();
        int m = triangle.get(n - 1).size();
        int[][] dp = new int[n][m];
        //边界条件,j=0,i=j;
        dp[0][0] = triangle.get(0).get(0);
        for (int i = 1; i < n; i++) {
            dp[i][0] = dp[i - 1][0] + triangle.get(i).get(0);
            for (int j = 1; j < i; j++) {
                dp[i][j] = Math.min(dp[i - 1][j - 1], dp[i - 1][j]) + triangle.get(i).get(j);
            }
            dp[i][i] = dp[i - 1][i - 1] + triangle.get(i).get(i);
        }
        int ans = dp[n - 1][0];
        for (int i = 1; i < m; i++) {
            ans = Math.min(ans, dp[n - 1][i]);
        }
        return ans;
    }

动态规划+滚动数组

从层序遍历和之前动态规划的代码都可以看出,到达当前层的最小路径和只与到达前一层所需要的最小路径和有关。所以我们只需要用一个一维数组保存到达上一层的最小路径和即可。

public static int minimumTotalDP2(List<List<Integer>> triangle) {
        //逆序枚举滚动数组,dp思路不变
        int n=triangle.size();
        int [] dp=new int[n];
        dp[0]=triangle.get(0).get(0);
        for(int i=1;i<n;i++){
            dp[i]=dp[i-1]+triangle.get(i).get(i);
            for(int j=i-1;j>0;j--){
                dp[j]=Math.min(dp[j-1],dp[j])+triangle.get(i).get(j);
            }
            dp[0]=dp[0]+triangle.get(i).get(0);
        }
        int min=dp[0];
        for(int i=1;i<n;i++)
            min=Math.min(min,dp[i]);
        return min;
}

上面代码与之前动态规划比较起来,主要的变化是用一个一维数组储存路径和。而为了实现这个,我们需要在遍历每层的元素时采用逆序遍历的方式。因为在只有在逆序遍历时,dp[j-1]保存的才是上一层的结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值