剑指offer T100 三角形中最小路径之和

设计题题目:剑指offer T100 三角形中最小路径之和

题目:在一个由数字组成的三角形中,第1行有1个数字,第2行有2个数字,以此类推,第n行有n个数字。

例如,右图是一个包含4行数字的三角形。如果每步只能前往下一行中相邻的数字,请计算从三角形顶部到底部的路径经过的数字之和的最小值。如右图所示,从三角形顶部到底部的路径数字之和的最小值为11,对应的路径经过的数字用阴影表示。

在这里插入图片描述

暴力法来分析:

第一条路径:2-3-6-4
第二条路径:2-3-6-1
第三条路径:2-3-5-1

第n条路径:2-4-7-3

n = 2$\times 2 2 2\times$2 = 8条

时间复杂度 = O(2h) = O(n)

那有没有稍微复杂度降低点的算法呢?

动态规划法

从顶点到结点某一结点的每一条最小路径中,顶点到上一层结点也必定是最小路径。

如从顶点“2”到结点“1”的最小路径“2-3-5-1”中,2到5也必定是最小路径“2-3-5”而非“2-4-5”,这样才能保证最后得到的答案是最小路径。

在这里插入图片描述

因此到某结点的最小路径值等于本节点的距离+上一层的最小路径之和。

由上我们不难得出该题动态规划的状态递推关系:

minDistance[i][j] = min(minDistance[i][j-1], minDistance[i-1][j-1])+nums[i][j]

动态规划时间复杂度分析

首先纪录第2层的min值
min[1][0] = 3+2 = 5
min[1][1] = 4+2 = 6
第3层:
min[2][0] = 6 + 5 = 11
min[2][1] = 5 + min{5, 6} = 10
min[3][1] = 7 + 6 =13
第4层:

min[3][1] = 1 + min{11, 10} = 11;

最后比较min中最后一层的值,得到最小的路径之和;

由上我们不难看出该算法的复杂度为O(n)

质的飞越啊!!!

代码实现

import java.util.*;

class Solution {
    public ArrayList<Integer> list;
    public int height;

    public int[][] nums; //保存数字
    public int[][] min;  //保存最小路径值
    public int[][] lastRou;    //保存最小路径

    Solution(List list){
        this.list = new ArrayList<>(list);
        this.height = getListHigh();
    }

    //通过list初始化各数组
    public void initArr(){
        nums = new int[height][height]; //保存数字
        min = new int[height][height];  //保存最小路径值
        lastRou = new int[height][height];    //保存最小路径

        int index = 0;
        for (int i = 0; i < height; i++) {
            if (i == 0) {
                //在第一层只需要赋值,无需比较上层
                min[0][0] = list.get(index++);
                nums[0][0] = min[0][0];
                continue;
            }

            for (int j = 0; j < i + 1; j++) {
                nums[i][j] = list.get(index++);

                int leftNum = j == 0? Integer.MAX_VALUE : nums[i][j]+min[i-1][j-1];
                //当j==0直接赋值为右边值加一,则必定会比右边的值大
                int rightNum = j == i? Integer.MAX_VALUE : nums[i][j]+min[i-1][j];

                if (leftNum <= rightNum){
                    min[i][j] = leftNum;
                    lastRou[i][j] = j-1;
                }else {
                    min[i][j] = rightNum;
                    lastRou[i][j] = j;
                }
            }
        }
    }

    //获取最小路径之和
    public int[] getMinLen(){
        //获取最小路径值
        int minLen = Integer.MAX_VALUE;
        int lastLevel = 0;
        for (int i = 0; i < height; i++) {
            if (min[height-1][i] < minLen) {
                minLen = min[height-1][i];
                lastLevel = i;
            }
        }
        int arr[] = {minLen, lastLevel};
        return arr;//返回最小路径的最后一个值
    }

    //获取最小路径
    public int[] getMinRou(int lastLevel){
        //lastlevel为最小路径的最后一个节点
        int[] realRou = new int[height];
        for (int i = height-1; i >= 0; i--) {
            realRou[i] = nums[i][lastLevel];
            int rot = lastRou[i][lastLevel];
            lastLevel = rot;
        }
        return realRou;
    }

    public static void main(String[] args) {

        List<Integer> list = new ArrayList<Integer>();
        Collections.addAll(list, 2,3,4,6,5,7,4,1,8,3,1,2,3,4,5);

        Solution s = new Solution(list);

        s.showList();   //显示列表的树状图
        s.initArr();    //初始化s中的各个数组

        int result[] = s.getMinLen();//result[0]为最小路径值,result[1]为最小路径的最后一层结点
        System.out.println("最小路径值为: " + result[0]);


        int []realRou = s.getMinRou(result[1]);
        System.out.print("最小路径依次为: ");
        for (int i = 0; i < realRou.length; i++) {
            System.out.print(realRou[i]);
        }

    }

    public void showList(){
        System.out.println("该列表的树状图如下: ");
        boolean flag = true;
        int index = 0;

        for(int i=1;i<=this.height;i++){
            for(int j=this.height; i<j; j--)
                System.out.print(" ");
            for(int j=1; j<=i; j++)
                if (flag == true){
                    flag = false;
                    System.out.print(this.list.get(index++));
                }
                else {
                    flag = true;
                    System.out.print(" ");
                }
            for(int j=1; j<i; j++)
                if (flag == true){
                    flag = false;
                    System.out.print(this.list.get(index++));
                }
                else {
                    flag = true;
                    System.out.print(" ");
                }
            flag = true;
            System.out.println();
        }
        System.out.println("该列表的高度为 :" + this.height);
    }

    public int getListHigh(){
        int len = this.list.size();
        int high = 1;
        int account = 0;

        while (account < len){
            account += high++;
        }
        high--;
        return high;
    }

}
程序结果

在这里插入图片描述

本文仅供自己参考学习使用,如有侵权立删

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值