题目分析

题目链接:124. Binary Tree Maximum Path Sum

不难分析出,最大的路径是“^型”的,从一个最高点向下延伸的两条路径组成一个“^型”路径。注意这个“最高点”不一定是树的根。
并且,这两条路径必定分别经过左右子节点,并且是向下路径中权值最大的。
由于每个节点都有可能是“最高点”,因此对于每个节点(可以用DFS/BFS遍历每个节点),我们都要计算出以该节点为“最高点”的最大“^型”路径;。为了计算出该值,我们要先得到经过左子节点的最长向下路径经过右子节点的最长向下路径(这两个值可以用DFS得到),两者相加,再加上该节点本身的权值,就得到了以该节点为“最高点”的最大“^型”路径;

这时如果不注意可能会出现严重的性能问题:两层DFS嵌套。第一层DFS用于遍历每个节点,并假设这个节点为“^型”路径的最高点;对于每个节点,又会进行第二层DFS:用于计算两个最长的向下路径。
两层DFS嵌套会大大影响计算时间,我们应该合并两次DFS。注意到DFS只不过是一种特殊的递归调用,对于父节点的每个子节点,都进行一次递归调用,每个子节点都遍历完毕(子节点的调用堆栈经历装载、弹出)以后,返回到父节点的调用堆栈(如果父节点不需要进行后续的计算,则父节点的调用堆栈弹出)。我们如果在第一次遍历中就计算出经过子节点的最长的向下路径,并返回给父节点的调用堆栈,父节点就能直接计算出经过左/右子节点的最长的向下路径,从而避免第二层的递归调用。

代码实现

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution
{
  private:
    // 存放已经找到的最大路径权值
    int result;

  public:
    int maxPathSum(TreeNode *root)
    {
        this->result = INT_MIN;
        calculateMaxPathDown(root);
        return this->result;
    }

    // 计算从subRoot向下的最大路径权值
    // 如果经过subRoot的路径都为负值,则返回0(终止于subRoot的路径权值)
    int calculateMaxPathDown(TreeNode *subRoot)
    {
        if (subRoot == NULL)
            return 0;
        int leftMaxPathDown = calculateMaxPathDown(subRoot->left);
        int rightMaxPathDown = calculateMaxPathDown(subRoot->right);

        // 这个语句与本函数的目的无关,但对于得到最终结果至关重要
        // 计算以subRoot为最高点的最大 “^型” 路径,将它与已经找到的最大路径比较
        this->result = max(leftMaxPathDown + rightMaxPathDown + subRoot->val, this->result);

        // 返回从subRoot向下的最大路径权值
        return max(0, max(leftMaxPathDown, rightMaxPathDown) + subRoot->val);
    }
};

由于对每个节点仅仅进行一次递归调用,因此时间复杂度为O(n)

由此可见,在选择递归之前,我们应该仔细思考每次递归调用应该计算什么、返回什么。


csRyan
1.1k 声望198 粉丝

So you're passionate? How passionate? What actions does your passion lead you to do? If the heart doesn't find a perfect rhyme with the head, then your passion means nothing.