120. Triangle

本文探讨了寻找三角形从顶到底的最小路径和的问题,提供了三种不同的算法实现方案,包括自顶向下的动态规划、就地修改输入数组以及自底向上的动态规划方法。

Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.
For example, given the following triangle

 [
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]

The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11).
Note:
Bonus point if you are able to do this using only O(n) extra space, where n is the total number of rows in the triangle.

这是一道典型的dp问题的题目,我的第一种解法很直接,思路也很简单就是自顶向下的遍历一整个三角数组,然后每一个节点都记录到这个节点的最短距离,这样时间复杂度和空间复杂度都是O(n * n)
以下是我的程序:

class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
        int n = 0;
        int size = 0;
        int pos = 0;
        int sum = 0;
        int small = 0;
        std::vector<std::vector<int>> vec;
        n = triangle.size();
        if (n <= 0)
            return sum;
        std::vector<int> v;
        v.push_back(triangle[0][0]);
        vec.push_back(v);
        for (int i = 1; i < n; i++) {
            size = triangle[i].size();
            std::vector<int> vec_small;
            sum = vec[i - 1][0] + triangle[i][0];
            vec_small.push_back(sum);
            for (int j = 1; j < size - 1; j++) {
                small = vec[i - 1][j - 1] <
                    vec[i - 1][j] ? 
                    vec[i - 1][j - 1] : vec[i - 1][j];
                sum = triangle[i][j] + small;
                vec_small.push_back(sum);
            }
            sum = triangle[i][size - 1] + 
                    vec[i - 1][size - 2];
            vec_small.push_back(sum);
            vec.push_back(vec_small);
        }
        n = vec.size();
        size = vec[n - 1].size();
        small = vec[n - 1][0];
        for (int i = 1; i < size; i++)
            if (small > vec[n - 1][i])
                small = vec[n - 1][i];       
        return small;
    }
};

但是我觉得这样的方式并不是很好,后来又改进了下,没有申请空间,而是直接在原来的三角数组上进行更改,虽然空间复杂度是O(1)但是这种方法的话不能保存数组的值,也不是很好,先看看程序:

class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
        //这种方法直接利用的就是原来的数组,每一次记录的都是
        //到当前节点的最小的步数之和。这样可以节省空间,但是不能保存下原来的数据
        //我使用的是top-down的方法,但是在讨论里面别人使用的是bottom-up的方法;
        //我觉得bottom-up的方法很好,节约空间,实现起来也不会很难
        int n = 0;
        int size = 0;
        int pos = 0;
        int sum = 0;
        int small = 0;
        n = triangle.size();
        if (n <= 0)
            return sum;
        for (int i = 1; i < n; i++) {
            size = triangle[i].size();
            sum = triangle[i - 1][0] + triangle[i][0];
            triangle[i][0] = sum;
            for (int j = 1; j < size - 1; j++) {
                small = triangle[i - 1][j - 1] <
                 triangle[i - 1][j] ? 
                    triangle[i - 1][j - 1] : 
                    triangle[i - 1][j];
                sum = triangle[i][j] + small;
                triangle[i][j] = sum;
            }
            sum = triangle[i][size - 1] + 
            triangle[i - 1][size - 2];
            triangle[i][size - 1] = sum;
        }
        n = triangle.size();
        size = triangle[n - 1].size();
        small = triangle[n - 1][0];
        for (int i = 1; i < size; i++)
            if (small > triangle[n - 1][i])
                small = triangle[n - 1][i];       
        return small;
    }
};

最后看了讨论里面别人的算法,发现还可以从底部遍历回去,这样可以做到O(n * n)的时间复杂度和O(n)的空间复杂度,以下是程序:

class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
        int n = triangle.size();
        if (n == 0)
            return 0;
        int num = 0;
        int size = triangle[n - 1].size();
        int* arr = new int[size];
        int small = 0;
        for (int i = 0; i < size; i++)
            arr[i] = triangle[n - 1][i];
        for (int i = n - 2; i >= 0; i--) {
            size = triangle[i].size();
            for (int j = 0; j < size; j++) {
                small = arr[j] < arr[j + 1] ? 
                    arr[j] : arr[j + 1];
                arr[j] = small + triangle[i][j];
            }
        }
        num = arr[0];
        delete []arr;
        return num;
    }
};

好啦,基本就是这样啦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值