[LeetCode 120] - 三角形(Triangle)
問題
給出一個三角形,找出從頂部至底部的最小路徑和。每一步你只能移動到下一行的鄰接數字。
例如,給出如下三角形:
[
?? ? [2],
? ? [3,4],
?? [6,5,7],
? [4,1,8,3]
]
從頂部至底部的最小路徑和為11(即2+3+5+1=11)。
注意:
加分項-如果你能只使用O(n)的額外空間,n為三角形中的總行數。
?
初始思路
最直接的思路就是把路徑都走一遍。即從頂點出發,分別往左中右移動(如果可能的話);然后對走到的位置繼續進行同樣移動,直到走到最后一行。這樣就可以得到一個遞歸的方案,而遞歸的結束條件就是前面所說的走到最后一行。偽代碼如下:
[最短路徑長度] 查找路徑(當前節點坐標,當前路徑值)
如果是最后一行,返回當前路徑值+當前節點值
否則
? 如果可以往左下走,左路徑 =?當前路徑值 +?查找路徑(左下節點坐標,當前路徑值)
? 如果可以往下走,下路徑 =?當前路徑值 +?查找路徑(下節點坐標,當前路徑值)
??如果可以往右下走,右路徑 =?當前路徑值 +?查找路徑(右下節點坐標,當前路徑值)
? 找出左路徑,下路徑和右路徑中的最小值,返回該最小值
結合范例數據仔細分析一下上面的偽代碼, 可以發現其中有不少重復的步驟。如2->3->5和2->4->5后面的處理是完全相同的。回想一下我們在?[LeetCode 132] - 回文分割II(Palindrome Partitioning II)?中的做法,可以使用一個map保存已計算過的路徑來應對這種重復。這里我們使用std::map<std::pair<int, int>, int>,將某點的坐標作為map的key,從key出發的最小路徑作為值。
按以上思路完成代碼提交后發現有些測試用例不能通過,如:
[
? ? ? [-1]
? ? ?[3,2]
? ?[-3,1,-1]
]
按以上算法得出的值為-2,而期望的值為-1。-2為-1 -> 2-> -3這條路徑得出的值,而-1為路徑-1 -> 3 -> -3。看來題目中的鄰接(英文原文adjacent)規定只能往下或者右走。修改也很簡單,將代碼中處理向左下走的那部分邏輯去掉即可。最終通過了Judge Small和Judge Large的代碼如下:
1 class Solution { 2 public: 3 int minimumTotal(std::vector<std::vector<int> > &triangle) 4 { 5 pathInfo.clear(); 6 7 if(triangle.empty()) 8 { 9 return 0; 10 } 11 12 return FindMinPath(triangle, 0, 0, 0); 13 } 14 15 private: 16 int FindMinPath(std::vector<std::vector<int>>& input, int X, int Y, int currentPathValue) 17 { 18 if(X == input.size() - 1) 19 { 20 return currentPathValue + input[X][Y]; 21 } 22 23 24 auto iter = pathInfo.find(Coordinate(X, Y)); 25 26 if(iter != pathInfo.end()) 27 { 28 return currentPathValue + iter->second; 29 } 30 31 32 //int left = currentPathValue; 33 int down = currentPathValue; 34 int right = currentPathValue; 35 int min = 0; 36 bool minUpdated = false; 37 38 /* 39 if(Y - 1 >= 0) 40 { 41 left += FindMinPath(input, X + 1, Y - 1, input[X][Y]); 42 min = left; 43 minUpdated = true; 44 } 45 */ 46 47 if(Y < input[X + 1].size()) 48 { 49 down += FindMinPath(input, X + 1, Y, input[X][Y]); 50 51 if(!minUpdated || min > down) 52 { 53 min = down; 54 minUpdated = true; 55 } 56 57 if(Y + 1 < input[X + 1].size()) 58 { 59 right += FindMinPath(input, X + 1, Y + 1, input[X][Y]); 60 if(!minUpdated || min > right) 61 { 62 min = right; 63 } 64 } 65 } 66 67 pathInfo[Coordinate(X, Y)] = min - currentPathValue; 68 69 return min; 70 } 71 72 73 std::map<std::pair<int, int>, int> pathInfo; 74 75 typedef std::pair<int, int> Coordinate; 76 };minimumTotal
?
獲得加分的方案
在上面的方案中,我們使用了以每個點坐標為key的map來保存已計算過路徑,空間復雜度達到了n^2的級別,即不計map的額外消耗需要1 + 2 + 3 +..... + n = n(n-1) / 2的空間來儲存這些信息。
讓我們改變一下思路,不考慮某點出發的最短路徑,而考慮到達某點的最短路徑。給出一個點,怎么得到到該點的最短路徑?可以發現有三種情況:
- 該點為最左邊的點,即縱坐標為0。由于我們前面已經知道題目不允許往左下走,所以這種情況沒得選擇,最短路徑就是上面的點的最短路徑加當前點的值。
- 該點為最右邊的點,即縱坐標為n-1(n為該行的長度)。由于是三角形,上一行中沒有縱坐標為n-1的點。這種情況最短路徑只能是左上的點的最短路徑加當前點的值。
- 其他情況。有兩種選擇,左上的點或者上方的點,需要取其中的小者來加當前點的值。
用上面方法得出第n行的所有點的最短路徑后,我們發現第n-1行即上面一行的信息已經不再需要被存儲了,因為第n+1行即下一行可以完全通過第n行的信息來算得自己的最短路徑值。那么我們需要的最大存儲空間就為最后一行的點的個數。不難看出,該數字和行數是相等的。這就符合了加分項中空間復雜度為O(n)的要求。
根據以上算法,我們將第一行中唯一一個值直接存到以縱坐標為下標的一個數組pathInfo中。然后從第二行開始對每行中的每列進行遍歷,不斷更新直到最后一行最后一列即可得到一個存有最后一行中每個點的最短路徑的數組。對數組pathInfo進行一次遍歷找出最小值即為題目所求。在處理過程中,還需要注意一個小細節:遍歷每行時,需要從最右邊的列開始。因為如果從左邊開始,我們更新pathInfo[0]時就把上一層的信息覆蓋了,而新的pathInfo[1]還需要用到上一層的信息(它需要從上一層的0和1中選一個最小值)。
最終代碼如下:
1 class Solution 2 { 3 public: 4 int minimumTotal(std::vector<std::vector<int> > &triangle) 5 { 6 std::vector<int> pathInfo(triangle.size()); 7 8 pathInfo[0] = triangle[0][0]; 9 10 for(int i = 1; i < triangle.size(); ++i) 11 { 12 for(int j = i; j >= 0; --j) 13 { 14 if(j == 0) 15 { 16 pathInfo[j] = pathInfo[j] + triangle[i][j]; 17 } 18 else if(j == triangle[i].size() - 1) 19 { 20 pathInfo[j] = pathInfo[j - 1] + triangle[i][j]; 21 } 22 else 23 { 24 pathInfo[j] = pathInfo[j] < pathInfo[j - 1] ? pathInfo[j] : pathInfo[j - 1]; 25 pathInfo[j] += triangle[i][j]; 26 } 27 } 28 } 29 30 int min = *pathInfo.begin(); 31 for(auto iter = pathInfo.begin() + 1; iter != pathInfo.end(); ++iter) 32 { 33 if(min > *iter) 34 { 35 min = *iter; 36 } 37 } 38 39 return min; 40 } 41 };minimumTotal_Bonus
使用了新的算法后,不但減少了空間復雜度,遞歸也不再需要了,過Judge Large的時間由130ms左右降到了40ms左右。
?
轉載于:https://www.cnblogs.com/shawnhue/p/leetcode_120.html
總結
以上是生活随笔為你收集整理的[LeetCode 120] - 三角形(Triangle)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 有谁知道这部电影《网红杀手》中,下面图片
- 下一篇: OpenGL概念辨析: 窗口,视口,裁剪