1. 程式人生 > >Leetcode 124 Binary Tree Maximum Path Sum 二叉樹最大路徑和

Leetcode 124 Binary Tree Maximum Path Sum 二叉樹最大路徑和

原題連結

題目描述

Given a binary tree, find the maximum path sum.
給出一棵二叉樹,計算其最大路徑和。

The path may start and end at any node in the tree.
路徑的起止結點必須位於樹內。

For example:
例如:

Given the below binary tree,
給出如下二叉樹,

   1
  / \
 2   3

Return 6.
返回6(sum path 2-1-3)。

解題思路

這個題目還是挺不好想的,我們需要找到問題的關鍵點。那麼對於二叉樹裡的一條子路徑,什麼是關鍵點呢?我們先來做一個假設,假設我們已經找到這個路徑path了,我認為關鍵點就是某一個結點是否處於這個路徑內。但是如何找這個path呢?我們不妨假設,我們正在處理的子樹的根結點root一定位於這個path內,我們計算當path一定經過了root時的最大路徑和max(root)。那麼max(root)該如何計算呢?我們假設我們已經求除了當path一定經過root->left時的最大路徑和max(root->left),也求除了當path一定經過root->right時的最大路徑和max(root->right),這時候,如果max(root->left)>0,那麼對於root而言,將root->left和root連線起來的path的和一定比root自己構成的path的和要大;同理,如果max(root->right)>0,那麼對於root而言,將root->left和root連線起來的path的和一定比root自己構成的path的和要大。然而對於一條路徑來說,root要麼跟root->left連線在一起,要麼跟root->right連線在一起,要麼root既不跟root->left也不跟root->right連線在一起,不然路徑會出現分叉。因此我們需要對root->val+max(root->left)、root->val、root->val+max(root->right)求出其中的最大值來作為max(root)的值。

有了如上的步驟之後,我們到底該如何確定二叉樹的最大路徑和呢?實際上,我們上述過程是求得了以某一個結點為根的子樹的最大路徑和,其中路徑的起點為根,終點為子樹中的某一結點,我們注意到這個過程中,我們每次都只是從根的左右子結點中選擇其中的一個來連線到path中,實際上,一個path是有可能既連線root->left,又連線root->right的,但是這種情況至多會出現在其中一個結點x上,否則就會出現路徑的分叉了。因此,我們在求的max(root->left)、max(root->right)之後,嘗試將root作為我們上面提到的x點,然後來計算由max(root->left)、max(root->right)、root->val可能構成的最大和tmp,然後判定tmp和當前快取好的max(最大路徑和),並更新max值即可。最後,這個max就是我們要求的最大路徑和。

演算法

  1. 如果root為NULL,返回0
  2. 遞迴呼叫1-5,求得maxSum(root->left)和maxSum(root->right)
  3. maxSum(root) = max(root->val, root->val + maxSum(root->left), root->val + maxSum(root->right))
  4. 判定root->val 、 maxSum(root->left)、maxSum(root->right)三者可能構成的最大和(必須包含root->val),是否大於目前快取的最大路徑和tmp,如果是則更新tmp
  5. 返回maxSum(root)的值
  6. 遞迴呼叫結束後,tmp的值就是最大路徑和

程式碼c

/**
 * 計算二叉樹的最大路徑和
 * @param : root 二叉樹根結點
 * @return : 最大路徑和
 */
int maxPathSum(struct TreeNode *root) {
    int maxChildPathSum(struct TreeNode *, int *);
    if (root == NULL) return 0;
    int tmpMax = root->val;

    maxChildPathSum(root, &tmpMax);
    return tmpMax;
}

/**
 * 計算二叉樹子樹的最大路徑和以及整棵二叉樹的最大路徑和
 * 子樹路徑必須以根結點開始,以樹中某一結點結束
 * 二叉樹的最大路徑和的路徑,不必以根結點為開始結點
 * @param : root 二叉樹根結點
 * @param : tmpMax 暫存整棵二叉樹的最路徑和的變數的地址
 * @return : 子樹的最大路徑和
 */
int maxChildPathSum(struct TreeNode *root, int *tmpMax) {
    int max(int, int);

    if (root == NULL) return 0;

    /* 計算左右子樹的最大路徑和 */
    int leftMax = maxChildPathSum(root->left, tmpMax);
    int rightMax = maxChildPathSum(root->right, tmpMax);

    /* 嘗試更新整棵二叉樹的最大路徑和 */    
    int tmp = root->val;
    if (leftMax > 0) tmp += leftMax;
    if (rightMax > 0) tmp += rightMax;
    if (tmp > *tmpMax) *tmpMax = tmp;


    /* 計算並返回子樹的最大路徑和 */
    int maxRoot = max(root->val, max(root->val + leftMax, root->val + rightMax));
    return maxRoot;
}

/* 求兩個數中較大的數字 */
int max(int arg1, int arg2) { return arg1 > arg2 ? arg1 : arg2; }

程式碼cpp

class Solution {
public:
    /**
     * 計算二叉樹的最大路徑和
     * @param : root 二叉樹根結點
     * @return : 最大路徑和
     */
    int maxPathSum(TreeNode *root) {
        if (root == NULL) return 0;
        int tmpMax = root->val;

        maxChildPathSum(root, &tmpMax);
        return tmpMax;
    }

private:
    /**
     * 計算二叉樹子樹的最大路徑和以及整棵二叉樹的最大路徑和
     * 子樹路徑必須以根結點開始,以樹中某一結點結束
     * 二叉樹的最大路徑和的路徑,不必以根結點為開始結點
     * @param : root 二叉樹根結點
     * @param : tmpMax 暫存整棵二叉樹的最路徑和的變數的地址
     * @return : 子樹的最大路徑和
     */  
    int maxChildPathSum(TreeNode *root, int *tmpMax) {
        if (root == NULL) return 0;

        /* 計算左右子樹的最大路徑和 */
        int leftMax = maxChildPathSum(root->left, tmpMax);
        int rightMax = maxChildPathSum(root->right, tmpMax);

        /* 嘗試更新整棵二叉樹的最大路徑和 */    
        int tmp = root->val;
        if (leftMax > 0) tmp += leftMax;
        if (rightMax > 0) tmp += rightMax;
        if (tmp > *tmpMax) *tmpMax = tmp;

        /* 計算並返回子樹的最大路徑和 */
        int maxRoot = max(root->val, max(root->val + leftMax, root->val + rightMax));
        return maxRoot;
    }

    /* 求兩個數中較大的數字 */
    int max(int arg1, int arg2) { return arg1 > arg2 ? arg1 : arg2; }
};

測試資料

輸入:[2,1,3]
輸出: 6

輸入:[5,6,7,-4,null,-1,1,2]
輸出:19

輸入:[5,6,7,-4,null,null,-1,1,2]
輸出:18

輸入:[-5,6,7,-4,null,null,-1,1,2]
輸出:8

輸入:[1,-2,3]
輸出:4

解決情況

Lauguage Status Time
c Accept 20ms
cpp Accept 32ms

2015/7/14