Binary Tree Maximum Path Sum
Difficulty:Hard
Total Accepted:146.6K
Total Submissions:523.7K
Given a non-empty binary tree, find the maximum path sum.
For this problem, a path is defined as any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The path must contain at least one node and does not need to go through the root.
Example 1:
Input: [1,2,3]
1
/ \
2 3
Output: 6
Example 2:
Input: [-10,9,20,null,null,15,7]
-10
/ \
9 20
/ \
15 7
Output: 42
解析:
此题找二叉树所有两个点之间val值最大的那一条路线,咋一看没什么头绪,路线太多,似乎从每个点都可以伸出一条路线出去,可能性太多,毕竟没规定root顶点一定要经过。
仔细分析。发现每条路都有最高层的顶点,从这个顶点往下左右子节点延伸分别寻找左右两边最大值路径再加起来就是以这个点作为顶层点的最长路径长度了。
举例:
8
/ \
5 6
/\ /\
2 4 3 1
以8为顶点,左边的最长路径就是2-5-8,右边路经3-6-8是最长的,合起来就得到以8位最高层的最长路线,结果再加上8本身就得到答案。
我的思路是利用两个先序遍历,一个先序遍历用来遍历二叉树中的每个顶点,第二个先序遍历用来找对应顶点的最长路径。
下面是源码:
class Solution {
public:
int maxPathSum(TreeNode* root) {
int result = root->val;
preorder(root, result);
return result;
}
void preorderFindLength(TreeNode* root,int& currentlength,int& longestLength) {
if (root != NULL) {
currentlength += root->val;
longestLength = (currentlength > longestLength) ? currentlength : longestLength;
preorderFindLength(root->left,currentlength,longestLength);
preorderFindLength(root->right,currentlength,longestLength);
currentlength -= root->val;
}
}
void preorder(TreeNode* root, int& longestLength) {
if (root != NULL) {
int currentLeftLongest = 0, currentRightLongest = 0,longestTemp=0;
int currentTemp = 0;
preorderFindLength( root->left,currentTemp,currentLeftLongest );
currentTemp = 0;
preorderFindLength(root->right, currentTemp, currentRightLongest);
longestTemp = currentLeftLongest + currentRightLongest + root->val;
longestLength = (longestLength > longestTemp) ? longestLength : longestTemp;
preorder(root->left, longestLength);
preorder(root->right, longestLength);
}
}
};
当然文末可以加上下面的来优化系统输入:
static const auto io_sync_off = []()
{
// turn off sync
std::ios::sync_with_stdio(false);
// untie in/out streams
std::cin.tie(nullptr);
return nullptr;
}();
很不幸的是,由于用了两个递归,导致效率很差。
其实可以将两个递归合成一个递归,避免重做无用功。
因为两个都是用先序遍历结构类似,整合起来可以做。
下面是优化后的,用时只有上面的四十分之一。
主要的修改是将两个递归合成一个递归,用返回值作为经过这个点的最长最长的一条子边,从叶节点往上面递归,每到一个节点与另一个方向的子边对比找到更大的子边(当然子边要保证比0大,否则就不纳入),这样一层一层向上递归,便可以连成最长的一条边。
在以下面的为例子:
-8
/ \
5 6
/\ /\
2 4 -3 1
从最下面开始往上递归,第一轮选出2,4和1
往上,2-4-5和6-1
再往上,遇到-8,由于2-4-5长度位11,如果加上-8,6,1长度需要舍弃2且总长度没有更长,所以左边不与右边合并,最长长度边为2-4-5,结束
class Solution {
public:
int maxPathSum(TreeNode* root) {
int result = root->val;
postorder(root, result);
return result;
}
int postorder(TreeNode* root, int& longestLength) {
if (root != NULL) {
int longestTemp, currentLeftLongest, currentRightLongest;
currentLeftLongest = max(0,postorder(root->left, longestLength));
currentRightLongest =max(0, postorder(root->right, longestLength));
longestTemp = currentLeftLongest + currentRightLongest + root->val;
longestLength = max(longestTemp, longestLength);
return (max(currentLeftLongest , currentRightLongest) + root->val);
}
return 0;
}
};