题目
【Hard】给你一棵以 root 为根的 二叉树 ,请你返回 任意 二叉搜索子树的最大键值和。
二叉搜索树的定义如下:
任意节点的左子树中的键值都 小于 此节点的键值。
任意节点的右子树中的键值都 大于 此节点的键值。
任意节点的左子树和右子树都是二叉搜索树。
示例1
输入:root = [1,4,3,2,4,2,5,null,null,null,null,null,null,4,6]
输出:20
解释:键值为 3 的子树是和最大的二叉搜索树。
示例2
输入:root = [4,3,null,1,2]
输出:2
解释:键值为 2 的单节点子树是和最大的二叉搜索树。
思路
首先想到二叉树的两种解题框架:
- 通过子问题的解推导出答案(递归)
- 通过遍历一遍树找出答案
很明显,子树是BST时当前树不一定是BST,所以第一种情况不适用,本题采用遍历方法实现。
既然要遍历,就要考虑以什么样的遍历顺序遍历,本题中要找最大键值和,对于每个节点来说,需要做的事有:
- 判断以自己为根的树是不是BST
- 如果是,求解以自己为根的树的键值和
- 与目前键值和比较,更新结果为较大的
其中,1.可以拆解为两步:
- 判断左右子树是不是BST
- 如果左右子树都是,判断加入当前root后还是不是(即判断是否左小右大)
上述思路很明显要先求解子树的相关值才能确定当前节点的相关值,所以确定以后序遍历实现,其他遍历顺序也可以实现,但一定没有后序遍历有效,复杂度更低。
经过上述分析,总结本题思路:
-
后序遍历一遍二叉树
-
遍历时对于每个节点,判断:
(1) 左右子树是不是BST
(2)加入当前节点还是不是BST -
上述两个条件都满足的情况下:求以当前节点为根的树的节点和
-
当前节点的键值和与目前最大键值和maxSum比较,更新maxSum为较大值。
这样,经过一次遍历以后,maxSum就是最终的最大键值和。
代码实现(C++)
class Solution {
public:
int maxSum = 0; //最大和
int maxSumBST(TreeNode* root) {
traverse(root);
return maxSum;
}
// 返回的vector分别保存: 0. 是不是BST; 1.当前树的最小值; 2.当前树的最大值; 3.当前树节点和
vector<int> traverse(TreeNode* root) {
if (root == nullptr) return vector<int> {1, INT_MAX, INT_MIN, 0};
vector<int> left = traverse(root->left);
vector<int> right = traverse(root->right);
vector<int> res(4);
if (left[0] == 1 && right[0] == 1 && root->val > left[2] && root->val < right[1]) {
res[0] = 1;
res[1] = min(left[1], root->val);
res[2] = max(right[2], root->val);
res[3] = left[3] + right[3] + root->val;
maxSum = max(res[3], maxSum);
} else {
res[0] = 0;
}
return res;
}
};
总结
此题的关键在于后序遍历顺序的确定,一般需要用到左右子树的计算结果时,要用后序遍历。
如果用其他遍历顺序,一定需要在递归中调用其他递归函数,要尽量避免这种情况,有时候换一种合理的遍历顺序就能够很好地优化代码。