一道水题又蘑菇了一下午...这水平可咋办...
原题地址:http://leetcode.com/onlinejudge#question_124
题目要求:
题意分析:
给定一棵二叉树,求最大路径和。路径可以从任意节点开始,到任意节点结束,不必从父节点到子节点。
即找到一棵子树,满足以下要求:
1、该子树中,除了根节点可以有任意多(0、1、2)个孩子,其他节点最多有一个孩子。
2、该子树的路径和是 所有满足要求1的子树的路径和的最大值。
如题中例子给出的,最大值路径就是2---1---3。
方法一、
实例:
(5)
/ \
/ \
(-5) (-9)
/ \ /
/ \ /
(6) (3) (2)
\ /
(5) (4)
\
(1)
以上面这棵二叉树为例,假设要计算以(5)为根节点的子树(路径必须经过节点(5))的最大路径和
令Left=以左子树根节点(-5)为起点的最大值路径的值,Right=以右子树根节点(-9)为起点的最大路径的值
注意这里的最大路径与题目中不同,这里只能以左右子树的根节点为起点,且每个节点至多有1个孩子节点,若非如此,就会破坏子树条件1,我们称这个过程为getMax(root)。
当Left或Right为负时,将其值赋为0。
该子树的最大路径的值是Left+Right+root。
计算过程:
Left=getMax(Leftchild(5))
=getMax(-5)
=max{(6+5-5),(1+4+3-5)}
=6;
Right=getMax(Rightchild(5))
=getMax(-9)
= -7;
Sum=isminus(Left)+isminus(Right)+5
=6+0+5
=11;
则,以(5)为根节点的情况下,最大路径值为11,路径为(5)---(6)---(-5)----(5)。
我们递归的按以上方法遍历所有的节点,可以发现,当以(-5)为根节点的时候:
getMax(6)=11;
getMax(3)=8;
Sum=11+8+(-5);
=14;
此时的子树拥有最大路径和值,即14,路径为(5)---(6)---(-5)---(3)---(4)---(1)
以下代码中,getMax和recurMax均递归实现,recurMax(root)用来递归遍历所有节点,并计算以root为根节点的子树的最大路径和值,并将所得的最大值返回,即题目所求最大路径值。
由于每次递归都要调用getMax,出现了很多重复的计算,在大数据中没能通过。
实现过程如下:
class Solution { public: int maxSum; int maxPathSum(TreeNode *root) { maxSum=~((~((unsigned int)0))>>1); recurMax(root); return maxSum; } void recurMax(TreeNode *root) { int leftMax=getMax(root->left); int rightMax=getMax(root->right); int cur_sum=(leftMax>0?leftMax:0)+(rightMax>0?rightMax:0)+root->val;//注意要加括号,因为+的优先级比:的要高。 if(cur_sum>maxSum)maxSum=cur_sum; if(root->left!=NULL) recurMax(root->left); if(root->right!=NULL) recurMax(root->right); } int getMax(TreeNode *root) { if(root==NULL) return 0; int left=root->val+getMax(root->left); int right=root->val+getMax(root->right); return left>right?left:right; } };
方法二、
方法二对方法一进行了优化,只需要对根节点计算一次getMax。并将getMax的值保存在树中。
还是以方法一中的实例为例:
原树:
(5)
/ \
/ \
(-5) (-9)
/ \ /
/ \ /
(6) (3) (2)
\ /
(5) (4)
\
(1)
首先对根节点调用一次getMax(root),其作用如方法一所述,同时将左右子树的递归返回值中较大且非负的值,与节点值相加,保存在root节点中。
调用结束后的树如下所示:
(11)
/ \
/ \
(6) (-7)
/ \ /
/ \ /
(11) (8) (2)
\ /
(5) (5)
\
(1)
此时,各个节点的值是原节点值加上左右子树的最大路径和中,较大且非负者。
再次递归遍历所有节点,计算当前的节点值加上左右子树最大路径和中,较小且非负者(因为较大的已经加过),所得就是当前节点为根节点的子树的最大路径和。
本次遍历过程中所得的最大值即为题目要求的最大路径和。
public: int maxSum; int maxPathSum(TreeNode *root) { maxSum=~((~((unsigned int)0))>>1); getMax(root); traversel(root); //recurMax(root); return maxSum; } void traversel(TreeNode *root) { if(root!=NULL) { int left=0,right=0; if(root->left!=NULL) left=root->left->val; if(root->right!=NULL) right=root->right->val; int smaller=left<right?left:right; if(smaller<0) smaller=0; if((root->val+smaller)>maxSum) maxSum=root->val+smaller; traversel(root->left); traversel(root->right); } } int getMax(TreeNode *root) { if(root==NULL) return 0; int left=getMax(root->left); int right=getMax(root->right); int bigger=left>right?left:right; if(bigger>0) root->val+=bigger; return root->val; } };