leetcode:333. 最大 BST 子树的节点数

题目来源

题目描述

在这里插入图片描述

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode() : val(0), left(nullptr), right(nullptr) {}
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
    TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};


class Solution{
public:
    int maxSubSearchBinaryTreeSize(TreeNode *root){

    }
};

题目解析

题目大概意思是:

  • 给定一个二叉树,整体可能是,也可能不是二叉搜索树,但是它一定有子树是二叉搜索树,要找到节点数最多的子搜索二叉树的节点数

所以,本题需要解决两个问题:

  • 找出二叉树中所有为BST的子树
  • 返回最大的那个BST子树的节点数

这个最大的子搜索二叉树,有两种可能性:

  • 第一种:包含二叉树头节点
    • 如果来自node左子树上的最大搜索二叉子树是以node.left为头的;来自右子树上的最大搜索二叉子树是以node.right为头的;
    • node左子树上的最大搜索二叉子树的最大值小于node.value;node右子树上的最大搜索二叉子树的最小值大于node.value
    • 那么,以节点node1为头的整棵树都是搜索二叉树
  • 第二种:不包含二叉树头节点
    • 如果不满足第一种情况,说明以节点node为头的树不能连成搜索二叉树
    • 此时,以node为头的树上的最大搜索二叉树是来自node的左子树上的最大搜索二叉子树和来自node的右子树上的最大搜索二叉子树之间,节点数较多的那个

此时,求解的具体过程如下:

  • 整棵树都是二叉树的后序遍历
  • 遍历到节点curr时,需要知道的左右子树如下信息:最大搜索二叉子树的头结点、最大BST子树的节点数、最大值、最小值
  • 根据上面收集到的信息,递归求解
class Solution{
    struct Info{
        TreeNode *node;
        int max;
        int min;
        int size;

        explicit Info(TreeNode *node, int max, int min, int size)
        : node(node), max(max), min(min), size(size){
        }
    };
    
    std::shared_ptr<Info> process(TreeNode *root){
        if(root == nullptr){
            return std::make_shared<Info>(nullptr, INT32_MIN, INT32_MAX,  0);
        }
        
        auto left = process(root->left);
        auto right = process(root->right);
        int max = std::max(root->val, std::max(left->max, right->max));
        int min = std::min(root->val, std::max(left->min, right->min));
        
        if(left->node == root->left && right->node == root->right && left->max < root->val && root->val < right->min){
            int size = left->size + right->size + 1;
            TreeNode *node = root;
            return std::make_shared<Info>(node, max, min, size);
        }else{
            int size = std::max(left->size, right->size);
            TreeNode *node = left->size > right->size ? root->left : root->right;
            return std::make_shared<Info>(node, max, min, size);
        }
    }
public:
    int maxSubSearchBinaryTreeSize(TreeNode *root){
        return process(root)->size;
    }
};

这个最大的子搜索二叉树

  • 不包含头结点时:就需要知道左子树的最大搜索二叉树的节点数,右子树的最大搜索二叉树的节点数
  • 包含头结点时:需要判断左树是不是搜索二叉树,右树是不是搜索二叉树,左树的最大值是否小于头节点,右树的最小值是否大于头节点,同时还需要左树和右树的节点数。

也就是说,root每次都需要问一问左子树和右子树:

  • 最大满足二叉搜索树条件的子树有多少节点数
  • 你有多少节点数,最大值、最小值是多少
  • 你是不是二叉搜索树

又因为,当最大搜索二叉树的节点数和节点数相等就意味着整个子树是搜索二叉树,所以可以化简为最大搜索二叉树的节点数、max、min、节点数

所以,定义一个辅助类:

 struct Info{
        int maxBSTSubtreeSize;
        int allSize;
        int max;
        int min;

        explicit Info(int m, int a, int ma, int mi) {
            maxBSTSubtreeSize = m;
            allSize = a;
            max = ma;
            min = mi;
        }
    };

递归求解当前子树的info:

	std::shared_ptr<Info> process(TreeNode *root){
        if(root == nullptr){
            return nullptr;
        }

        // 获取左右子树信息
        auto leftInfo = process(root->left);
        auto rightInfo = process(root->right);
        // 拼凑自己的信息
        int max = root->val, min = root->val, allSize = 1;
        if(leftInfo != nullptr){
            max = std::max(max, leftInfo->max);
            min = std::min(min, leftInfo->min);
            allSize += leftInfo->allSize;
        }
        if(rightInfo != nullptr){
            max = std::max(max, rightInfo->max);
            min = std::min(min, rightInfo->min);
            allSize += rightInfo->allSize;
        }

        // [左|右]树 最大搜索二叉树大小
        int p1 = -1, p2 = -1;
        if(leftInfo != nullptr){
            p1 = leftInfo->maxBSTSubtreeSize;
        }
        if(rightInfo != nullptr){
            p2 = rightInfo->maxBSTSubtreeSize;
        }

        // 是否包含头节点
        int p3 = -1;
        bool leftSearch = leftInfo == nullptr || leftInfo->maxBSTSubtreeSize == leftInfo->allSize;   // 左树是否是搜索二叉树
        bool rightSearch = rightInfo == nullptr || rightInfo->maxBSTSubtreeSize == rightInfo->allSize;  // 右树是否是搜索二叉树
        if(leftSearch && rightSearch){
            bool  lessMaxLessX = leftInfo == nullptr || leftInfo->max < root->val;     // 左树最大值是否比当前节点值小(空也认为比当前节点小)
            bool  rightMinMoreX = rightInfo == nullptr || rightInfo->min > root->val;  // 右树最小值是否比当前节点值大(空也认为比当前节点大)
            // 都满足,才能修改p3的值
            if (lessMaxLessX && rightMinMoreX) {
                int leftSize = leftInfo == nullptr ? 0 : leftInfo->allSize;
                int rightSize = rightInfo == nullptr ? 0 : rightInfo->allSize;
                p3 = leftSize + rightSize + 1;
            }
        }

        // 最后修改,当前子树最大搜索二叉子树的大小
        int maxSubSize = std::max(p1,  std::max(p2, p3));
        return std::make_shared<Info>(maxSubSize, max, min, allSize);
    }

调用上面方法获取结果:

    int maxSubSearchBinaryTreeSize(TreeNode *root){
        if(root == nullptr){
            return 0;
        }
        return process(root)->maxBSTSubtreeSize;
    }

小结

二叉树递归套路总结:

  • 1)假设以X节点为头,假设可以向X左树和X右树要任何信息
  • 2)在上一步的假设下,讨论以X为头节点的树,得到答案的可能性(最重要)
  • 3)列出所有可能性后,确定到底需要向左树和右树要什么样的信息
  • 4)把左树信息和右树信息求全集,就是任何一棵子树都需要返回的信息S
  • 5)递归函数都返回S,每一棵子树都这么要求
  • 6)写代码,在代码中考虑如何把左树的信息和右树信息整合出整棵树的信息
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>