本文正在参与掘金团队号上线活动,点击 查看大厂春招职位
一、题目描述:
二、思路分析:
遇到树的题目,直接递归返回就完事了,不过今天要介绍的这种类型,最终的结果不一定是经过根节点的,所以需要在递归遍历的时候使用中间变量比较一下。
递归
-
我们可以从最底层的叶子节点开始往上考虑,考虑每个节点能够提供给父节点的收益是多少。
-
对于每个叶子节点,因为其左右孩子都是NULL,相当于提供的收益都是0,所以叶子节点能提供给父节点的收益就是1。我们在递归函数里返回: 1 + max(rightGain); 为什么返回的左右子树中的较大收益,因为这题目要求的是路径,路径是不能分叉的。但是你可能会说示例2中的4,4,4子树是最大值啊。这咋办?
-
示例2中的 4,4,4子树就是我们也额外讨论的情况。因为这种情况是无法在递归函数中返回的,所以我们使用一个全局变量来跟这种情况可能产生的最大路径做比较,这种情况的最大同值路径值就是,当前root加上左右子树产生的收益: 1 + leftGain
-
需要注意的是,只有当当前的root节点的值和子节点的值相同的时候,我们才会保留其子节点的收益。
-
时间复杂度:O(N),这里 N 二叉树中节点的个数。
-
空间复杂度:O(H),H是二叉树的高度。
三、AC 代码:
/**
* Definition for a binary tree node.
* class TreeNode {
* public $val = null;
* public $left = null;
* public $right = null;
* function __construct($val = 0, $left = null, $right = null) {
* $this->val = $val;
* $this->left = $left;
* $this->right = $right;
* }
* }
*/
class Solution {
/**
* @param TreeNode $root
* @return Integer
*/
protected $max = PHP_INT_MIN;
function longestUnivaluePath($root) {
if ($root == null) {
return 0;
}
$this->helpMe($root);
return $this->max - 1;
}
//递归函数的返回是当前节点能提供给父节点的最大收益值
protected function helpMe($root) {
if ($root == null) {
return 0;
}
$leftGain = $rightGain = 0;
//依次判断左右子树 同值 收益
$left = $this->helpMe($root->left);
$right = $this->helpMe($root->right);
//如果当前有左节点并且左节点的值和当前节点的值一样,则保留左节点的收益
if ($root->left && $root->val == $root->left->val) {
$leftGain = $left;
}
//如果当前有右节点并且右节点的值和当前节点的值一样,则保留右节点的收益
if ($root->right && $root->val == $root->right->val) {
$rightGain = $right;
}
//当前的最大收益可能是 当前根1 + 左收益 + 右收益。
//在遍历到每个节点的时候都有可能产生全局最大的收益,所以在此比较
//细想一下,最开始传入的root + 左子树 + 右子树 的情况其实也是包含在这个case里的。
$this->max = max($this->max, 1 + $rightGain + $leftGain);
//递归返回的是左右子树的最大收益, + 1 是 + 了root节点
//因为求得是路径,只能从左右子树选择一个大的返回给父亲
return 1 + max($leftGain, $rightGain);
}
}
四、总结:
遇到二叉树的问题直接考虑递归。不过今天这种类型的题目递归函数的返回并不一定是最终要求的结果,因为路径这个可能不经过root节点。所以需要在遍历的过程中比较一下可能产生的最大值情况。类似的题目还有好几道,下次我们再见。