lc1373 求二叉搜索子树的最大键值和
对于一颗二叉树,其必然存在一个子树为二叉搜索树,因为即便没有非叶节点形成二叉搜索树,该树的叶节点也算是二叉搜索树。题目求这些二叉搜索子树所有节点和最大的那个节点和是多少?
二叉树由于其本身先天具备子问题的结构和原问题结构一致这样的特性,所以一般来说用dfs或bfs都是可以解决的。一个问题的解,可以转化成其子问题的解和当前节点之间的关系来求解。
因此对于二叉树的问题要想用递归的思维解决必须明白下列几个问题:
1.递归函数的含义或者返回值是什么?
2.站在当前角度解决当前节点需要知道哪些信息?这些信息决定了我们是要前中后哪种遍历方式,以及递归函数返回什么?
对于本题而言,
若递归函数返回的是当前节点的最大二叉搜索字数节点和,那么对于当前节点,我们只能从左右子树中选择最大的递归结果返回,但这完全没有考虑到是否是二叉搜索树。
若递归函数只是遍历不返回值,对于每个节点我们都去检查是否是二叉搜索树,计算其节点和,用一个全局变量去记录最大的子树和。那么判断是否是二叉搜索树需要一个递归函数,计算节点和需要一个递归函数,这样虽然能做出来那复杂度太高。
我们发现连同遍历过程一共需要三个函数,这三个函数都是要对树进行遍历的,那么能不能一次遍历把这几个需要的信息全都得到呢?一次遍历既能得到节点和也能得到是否是二叉搜索树。如果可以的话,那么对于当前节点我们就可以简化问题,对于当前节点,若其左右子树都是二叉搜索树那么,判断当前节点是否也符合定义,若是更新节点和,更新最大值。若左右子树不是二叉搜索树,则没有必要判断了,直接return。
分别解析一下判断是否是二叉搜索树和求节点和的遍历过程。
1.判断是否是二叉搜索树?
对于当前节点若其左右子树都是二叉搜索树,且当前节点值在左子树最大值和右子树最小值之间,则当前节点向下的子树是二叉搜索树。也就是说我们需要事先知道左右子树的情况。这明显只有后序遍历才能实现该效果。因此递归函数返回当前树的最大值和最小值以及是否是二叉搜索树即可。这样对于当前节点我们就可以直接判断了。边界条件:当节点为空,说明一定是二叉搜索树,最大值初始化成INT_MIN,最小值初始化成INT_MAX。因为只有叶节点才会出现边界情况,那么叶节点必然是二叉搜索树,那么值应该在左子树的最大值和右子树的最小值之间。如果最大值初始化成INT_MAX,那必然不满足,叶节点就不会被当成二叉搜索树了。
2.计算节点和
不管哪种遍历顺序都是可以的,所以可以并在判断二叉搜索树的遍历过程中。
因此该题我们采用后序遍历的方式,递归函数返回四个值,是否是二叉搜索树,树中最大值和最小值是多少,树的节点和是多少。
先递归左右子树,根据左右子树返回的信息来更新当前节点应该返回的四个值是多少。
代码:
class Solution {
public:
int res = 0;
int maxSumBST(TreeNode* root) {
dfs(root);
return res;
}
vector<int> dfs(TreeNode* root){
if(!root)
return {1,INT_MAX,INT_MIN,0};
vector<int> left = dfs(root->left);
vector<int> right = dfs(root->right);
vector<int> cur = {0,min(left[1],root->val),max(root->val,right[2]),root->val + left[3] + right[3]};
if(left[0] && right[0] && root->val > left[2] && root->val < right[1]){
cur[0] = 1;
res = max(cur[3],res);
}
return cur;
}
};