問題
給出一個二叉樹,找到其中的最大路徑和。
路徑可以從樹中任意一個節點開始和結束。
例如:
給出如下二叉樹,
1
/ \
2 3
返回6。
初始思路
為了簡化分析,我們先假設二叉樹中所有節點的值都是正數。通過觀察可以發現,一棵二叉樹的最大路徑,就是其左子樹的最大路徑加上右子樹的最大路徑。看起來可以從根節點出發通過深度優先遞歸來求解:
函數 查找路徑
如果是葉子節點,返回葉子節點的值
如果不是葉子節點
左子樹路徑和 = 查找路徑(左子樹)
右子樹路徑和 = 查找路徑(右子樹)
如果左子樹路徑+右子樹路徑和+當前節點值 > 當前最大路徑,更新最大路徑
返回左子樹路徑+右子樹路徑和+當前節點值
用題目中的簡單例子來驗證,是可以得出答案的。但是使用復雜一點的樹來驗證后就發現其中的問題了,如
1
/ \
2 3
/ \
4 5
使用前面的偽代碼得出的結果是15,但是其實答案應該是11,由3,1,2,5或者2,4,5得到。分析可以發現問題在於計算2,4,5這棵子樹時,它的最長路徑為11,這是正確的。但是當它作為左子樹向父節點返回最長路徑時,因該返回7而不是11。因為從1出發不走重復路徑不可能同時到達4或5的-通常二叉樹節點路徑的定義是每個節點只能訪問一次,通過測試數據也可以驗證題目就是這樣要求的。因此我們需要兩個最大值,一個是當前樹的最大路徑,即前面偽代碼算出來的那個值;另一個是當前樹向父節點提供的最大路徑,這個值應該是根節點的值加上路徑最長的子樹那邊的最大路徑。我們向上層遞歸函數返回這個值。
好了,現在全是正數的情況解決了。讓我們開始把負數引入。負數引入后,將會導致以下幾個變化:
- 葉子節點的值也有可能成為最大路徑。在全是正數的情形下,葉子節點的值肯定不可能會是最大路徑,因為加上父節點的值后必然會變大。有了負數以后,這個情況就不成立了,如:
-1
/
3
這時最大路徑就是3。
- 當前樹最大路徑的計算方法。有了負數以后不能簡單的把左子樹返回的值,右子樹返回的值及當前的值相加了。這里我們把各種情況列舉出來:
- 當前值為正,子樹返回值都為正:全相加
- 當前值為正,子樹返回值有一個為正:當前值+正的那個值,因為負值只會讓結果變小。
- 當前值為正,子樹返回值都是負:只取當前值,負值越加越小。
- 當前值為負,子樹返回的值都為正:全相加,雖然值會變小,但是沒有當前節點左右就不能聯通。
- 當前值為負,子樹返回值有一個為正:當前值+正的那個值。
- 當前值為負,子樹返回值都為負:當前值,負值越加越小。
- 向父節點提供的最大路徑的計算方法。和當前樹最大路徑計算方法基本一樣。就是仍然要左子樹右子樹的值只能取大的那個。
將上面分析轉換成代碼,並加入一些細節如沒有左(右)子樹的判斷。請注意由於節點的取值范圍並沒有限定,所以不能使用某個特殊值作為沒有左(右)子樹的標志。結果如下:

1 class Solution { 2 public: 3 int maxPathSum(TreeNode *root) 4 { 5 if(!root) 6 { 7 return 0; 8 } 9 10 maxSum_ = 0; 11 firstValue_ = true; 12 13 CountPathSum(root); 14 15 return maxSum_; 16 } 17 18 private: 19 int CountPathSum(TreeNode* root) 20 { 21 if(root->left == 0 && root->right == 0) 22 { 23 if(firstValue_ || root->val > maxSum_) 24 { 25 maxSum_ = root->val; 26 firstValue_ = false; 27 } 28 return root->val; 29 } 30 else 31 { 32 int left = 0; 33 int right = 0; 34 if(root->left) 35 { 36 left = CountPathSum(root->left); 37 } 38 39 if(root->right) 40 { 41 right = CountPathSum(root->right); 42 } 43 44 int currentBest = 0; 45 int sumInPah = 0; 46 47 if(left > 0 && right > 0) 48 { 49 currentBest = left + right; 50 51 sumInPah = left > right ? left : right; 52 } 53 else if(left > 0) 54 { 55 currentBest = left; 56 sumInPah = left; 57 } 58 else if(right > 0) 59 { 60 currentBest = right; 61 sumInPah = right; 62 } 63 else 64 { 65 if(!root->left) 66 { 67 currentBest = right; 68 } 69 else if(!root->right) 70 { 71 currentBest = left; 72 } 73 else 74 { 75 currentBest = left > right ? left : right; 76 77 } 78 sumInPah = currentBest; 79 } 80 81 //前面已做只取正值的處理,如果還小於零說明兩個都是負數 82 if(sumInPah < 0) 83 { 84 sumInPah = root->val; 85 } 86 else 87 { 88 sumInPah += root->val; 89 } 90 91 if(currentBest < 0) 92 { 93 currentBest = root->val; 94 } 95 else 96 { 97 currentBest += root->val; 98 } 99 100 if(currentBest > maxSum_) 101 { 102 maxSum_ = currentBest; 103 } 104 105 return sumInPah; 106 } 107 } 108 109 int maxSum_; 110 bool firstValue_; 111 };
提交后Judge Small和Judge Large都順利通過。