LeetCode 120. Triangle 动态规划

来源:互联网 发布:网络系统管理课程 编辑:程序博客网 时间:2024/06/05 23:39

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.

题意

给定一个三角形,找到一个最小路径和从顶部到底部,每一次只能移动到下一行相邻的元素。
空间复杂度能是O(n)

思路1

自底向上。动态规划。
求解从上到下最小路径和,那么由底部将每一层到达当前位置的最小路径计算出来,计算方法底层相邻元素取小的并和当前位置元素相加,层层计算,那么最顶层就是最小路径和。

  • k层中第i个位置最小路径和

minPath[k][i] = min(minPath[k+1][i]+minPath[k+1][i+1])+triangle[k][i]
这里写图片描述

代码1

class Solution {public:   int minimumTotal(vector<vector<int>>& triangle) {       vector<vector<int>> minLen(triangle);       //用于保存每一层的最小路径和    //从倒数第二层开始判断        for (int layer = triangle.size() - 2; layer >= 0; layer--)        {            for (int i = 0; i < triangle[layer].size(); i++)            {                minLen[layer][i] = min(minLen[layer+1][i], minLen[layer+1][i + 1]) + triangle[layer][i];            }        }        return minLen[0][0];   }};
  • 代码优化,将空间复杂度降低。
  • 保存当前行各个位置的最小路径和,到下一次计算可以被边计算边覆盖,节省空间。
  • minPath[i] = min(minPath[i]+minPath[i+1])+triangle[k][i]
class Solution {public:   int minimumTotal(vector<vector<int>>& triangle) {        int res = 0;        vector<int> minLen(triangle.back());  //行内最多元素为三角形最底层        for(int layer=triangle.size()-2; layer >= 0; layer--)        {            for(int i=0;i<triangle[layer].size();i++)            {                minLen[i] = min(minLen[i],minLen[i+1])+triangle[layer][i];            }        }        return minLen[0];   }};

这里写图片描述

思路2

自顶向下,并未使用递归结构。
特除处理是处理每一行中第一个元素:没有左子树为无限大,最后一个元素:没有右子树为无限大。其模拟的结果就是,到达两侧元素的路径最小和就是上一行的第一个元素(最后一个元素)+当前元素。
非两侧的元素,就需要比较相邻的两个元素选择小的一个+当前元素。
就像一颗倒立的二叉树
空间复杂度使用的是三角形数组本身。记忆累加,各个位置存储的都是到达当前位置的最小路径和。
最终将三角形数组的最后一行排序,输出最小值,即为从顶到底的最小路径和。
这里写图片描述

代码2

  • triangle[i][j] = triangle[i][j] + min(triangle[i-1][j-1],triangle[i-1][j]);
class Solution {public:   int minimumTotal(vector<vector<int>>& triangle) {        for(int i=1;i<triangle.size();i++)        {            for(int j=0;j<triangle[i].size();j++)            {                int left = 9999999;                int right = 9999999;                if(j>0)  //第一个元素没有左子树,                {                    left = triangle[i-1][j-1];                }                if(j<triangle[i].size()-1)   //最后一个元素没有右子树                {                    right = triangle[i-1][j];                }                //非两侧元素                triangle[i][j] = triangle[i][j] + min(left,right);            }        }        sort(triangle.back().begin(), triangle.back().end());        return triangle.back()[0];    }};

这里写图片描述

思路3

自顶向下的另外一种写发,较思路2较复杂。
其实是思路2是思路3的一种优化,将两侧元素和其余元素统一处理,不需要额外空间。

代码3

class Solution {public:   int minimumTotal(vector<vector<int>>& triangle) {        vector<int> minLen(triangle.back().size(), triangle[0][0]);        //一开始的初始值要确定好了,是必须的等于第一个元素,默认将第一行的值作为初始值        //for循环,一开始从第二行开始,内层for循环必须使用逆序,因为在进行minLen操作,顺序会将之前的值覆盖,只有逆序将上次的结果使用之后        for (int i = 1; i < triangle.size();i++) //行        {            /*            for (int j = 0; j < triangle[i].size(); j++)   //行中有多少元素            {                if (j == 0)                    //直接赋值是行不通的,因为上一次的加结果并未参与到下一行加的计算中,需要+=                     //minLen[j] += triangle[i-1][0]+triangle[i][j];                    minLen[j] += triangle[i][j];   //将上一次的累加结果+当前元素的值,即为当前路径的值                else if (j == triangle[i].size() - 1)                    minLen[j] += triangle[i][j];        //处理三角形两侧的元素                else                    minLen[j] = min(minLen[j-1], minLen[j]) + triangle[i][j];            }            */            for (int j=triangle[i].size()-1;j>=0;j--)  //逆序,否则数据还未被使用,就会被覆盖            {                if (j == 0)                    minLen[j] += triangle[i][j];             //第一个元素可以加直接 + minLen[j] 是因为历史值就存储在minLen[0]中,                else if (j == triangle[i].size() - 1)                    minLen[j] = triangle[i][j] + minLen[j - 1];   //最后的元素必须是+minLen[j-1],这是历史存储的结果,如果是+minLen[j]那么就是一个初始值为第一行第一个元素                else                    minLen[j] = triangle[i][j] + min(minLen[j - 1], minLen[j]);    //更新这一行相同坐标的值,使用了上一行的minLen[j-1],[j]值            }        }        sort(minLen.begin(), minLen.end());        return minLen[0];    }};
原创粉丝点击