求二叉树中,包含的最大二叉搜索子树的节点数量是多少

求二叉树中,包含的最大二叉搜索子树的节点数量是多少?

提示:本节仍然是重点说二叉树的DP递归套路,非常重要而且容易理解

二叉树的动态规划树形DP递归套路系列文章有这些,可以帮助你快速掌握树形DP的题目解题思想,就一个套路:
(1)判断二叉树是否为平衡二叉树?树形DP,树形动态规划的递归套路
(2)求二叉树中,任意两个节点之间的距离最大值是多少


题目

求二叉树中,包含的最大二叉搜索子树的节点数量是多少?

所谓二叉搜索树是指:
严格的遵守以下规则的二叉树:
(1)二叉树中没有重复值出现;
(2)任意节点x的左树中最大值严格小于x
(3)任意节点x的右树中最小值严格大于x
比如:
在这里插入图片描述


一、审题

示例:
可能一个二叉树head开头的树不见得是二叉搜索树,如果head开头的树整个是二叉搜索子树,那head就是最大的二叉搜索子树了
下面这个head就不是,它下面有一个子树是二叉搜索树,那就称这个子树为最大的二叉搜索子树,咱们需要统计这个子树有多少个节点数量?
下面这个树中,由于y开头的树,右树3<4,满足不了严格的搜索二叉树的条件,所以y不是搜索子树
显然h就不可能是,而x是最大的二叉搜索子树,节点数量为3
在这里插入图片描述
本题要用的二叉树为:

public static class Node {
        public int value;
        public Node left;
        public Node right;

        public Node(int v) {
            value = v;
        }
    }

    //构造一颗树,今后方便使用
    public static Node generateBinaryTree() {
        //树长啥样呢
        //          1
        //        2   3
        //       4 5 6 7
        //      8
        //     9
        Node head = new Node(1);
        Node n2 = new Node(2);
        Node n3 = new Node(3);
        head.left = n2;
        head.right = n3;
        Node n4 = new Node(4);
        Node n5 = new Node(5);
        n2.left = n4;
        n2.right = n5;
        Node n6 = new Node(6);
        Node n7 = new Node(7);
        n3.left = n6;
        n3.right = n7;
        Node n8 = new Node(8);
        n4.left = n8;
        Node n9 = new Node(9);
        n8.left = n9;
        return head;
    }

    //构造一颗树,今后方便使用
    public static Node generateBinaryTree2() {
        //树长啥样呢
        //          4
        //        2   5
        //       1 3
        Node head = new Node(4);
        Node n2 = new Node(2);
        Node n3 = new Node(5);
        head.left = n2;
        head.right = n3;
        Node n4 = new Node(1);
        Node n5 = new Node(3);
        n2.left = n4;
        n2.right = n5;
        return head;
    }

二、树形动态规划的递归套路:树形DP

95%的二叉树动态规划问题,都可以用以下套路解决:
一、定义个信息类:Info
收集x左树和右树都有的信息(左右树都有哦,而不是针对某单独的左树或者右树),比如是否平衡?树的高度,总之就是有的信息,不管你是String还是Boolean还是Integer类型的信息。经常是要讨论与x有关,还是 与x无关。

二、树形DP递归套路: 来到x节点
1)base case:考虑叶节点应该返回的信息Info
2)先收集左树和右树的信息Info
3)综合2)整理x自己的信息,并返回;

三、从题目给的二叉树head开始求(2),得到了一个head的信息Info,找里面咱们要用的那个信息:比如是否平衡?返回它。

来,咱们举例说明:实践知真理!


三、解题思路:

一、定义个信息类:Info
收集x左树和右树都有的信息(左右树都有哦,而不是针对某单独的左树或者右树),比如是否平衡?树的高度,总之就是有的信息,不管你是String还是Boolean还是Integer类型的信息。经常是要讨论与x有关,还是 与x无关。
(1)本题,自然要返回一个信息:树是否是二叉搜索树?isBST【is Balanced Search Tree缩写】
(2)另外,我们要判断x开头的树,是否严格搜索?那就需要知道x的左树,它树上的最大值max,x的右树,它树上的最小值min
这样我们才能判断maxL<x<minR是否成立?
光是这个成立还不行,咱还得确保,x左树它确实是搜索二叉树,x右树也是搜索二叉树,且同时满足maxL<x<minR条件,咱们才能断定x开头的树也是搜索二叉树
(3)如果确定了x开头的树是搜索二叉树,还需要统计x的节点数量,怎么统计呢?左树节点数+右树节点数+1(x自己算一个),这样作为x开头的数是二叉搜索树的节点数量。maxBSTSize——最大二叉搜索子树的节点数量。
注意哦,这个节点数量,我们示例中看过了,不一定非要是x开头的树哦,可能是x下面的某个子树。

无论如何,咱需要收集上面分析的(1)(2)(3)的信息,一共四个待确认值:

public static class Info {
        public int max;//树上的最大值
        public int min;//树上的最小值
        public boolean isBST;//这个树,是否诶搜索二叉树
        public int maxBSTSize;//最大二叉搜索树的节点数量--并不一定就是x开头的树

        public Info(int maxv, int minv, boolean flag, int maxSize) {
            max = maxv;
            min = minv;
            isBST = flag;
            maxBSTSize = maxSize;
        }
    }

二、树形DP递归套路: 来到x节点,要收集节点x的信息:public static Info process(Node x)
1)base case:考虑叶节点应该返回的信息Info
本题中的这个基础情况,不太好整理,遇到了叶节点的null
不能确定这null的最小值和最大值,因为null没法跟Integer对比,所以这样子,咱就直接返回null
让下面3)重新初始化那四个信息,然后自己整合。if (head == null) return null;//叶节点暂时没法处理

2)先收集左树和右树的信息Info
这好说:

        //两边获取信息
        Info leftInfo = process(head.left);
        Info rightInfo = process(head.right);

3)综合2)整理x自己的信息,并返回;
这里是本题的重点!
我们要整合出Info中的四个信息:
——min和max
咱们先初始化,min和max都是x自己,万一左树或者右树有更大,或者有更小的,只要左右不是null就更新

//整合出:max,min
        int min = head.value;//先初始化一下,待会不一定会被覆盖的
        int max = head.value;//得看与左子和右子比较的情况
        if (leftInfo != null) {
            min = Math.min(min, leftInfo.min);
            max = Math.max(max, leftInfo.max);//最大最小都要比较的
        }
        if (rightInfo != null) {
            max = Math.max(max, rightInfo.max);
            min = Math.min(min, rightInfo.min);
        }
        //如果是叶节点,基本就是head自己的值

——整理isBST信息和maxBSTSize
当然了,h开头的最大二叉搜搜子树,可能与h无关:
即不是h开头的这个树,可能是h的左树,或者右树,
看看在谁那,比如下图,在h的右树x上,先更新给maxBSTSize再说:
在这里插入图片描述

//整合出maxBSTSize和isBST信息
        int maxBSTSize = 0;
        //这里如果是叶节点遇到了null,还得单独操作--有时候一棵树可能右子就为null
        //null就是0个点
        //这里要谨慎,如果x开头的树,压根没法形成BST的话,x的左树,或者右树,有最大二叉搜索树吗?
        //有的话,先更新给我
        if (leftInfo != null) maxBSTSize = Math.max(maxBSTSize, leftInfo.maxBSTSize);
        if (rightInfo != null) maxBSTSize = Math.max(maxBSTSize, rightInfo.maxBSTSize);

另外,可能与h本身有关哦!
不妨设最大二叉搜索子树就是h开头的树,
在这里插入图片描述
得判断h开头的树是否为搜索二叉树?
判断条件比较牛:
判断之前,咱都得确认左树信息和右树信息是否存在?
如果左树或者右树信息是null,自然咱们认为它就是搜索二叉树
如果左树或者右树信息不是null,则我们就看信息内放的isBST是不是true,保证左右同时都是搜索二叉树,与此同时
保证左树最大值<h<右树最小值【如果存在的话才比较】。

在这种情况下,x开头的树确实是最大二叉搜索树,那么更新maxBSTSize【如果左右树不是null的话,是null的话+0就行】
maxBSTSize为:左边的最大节点数+右边的最大节点数+1(h自己算一个);【这节点数包含整个二叉树呗】

boolean isBST = false;//初始化
        //满足最大二叉搜索树的条件
        if ((leftInfo == null ? true : leftInfo.isBST) &&
                (rightInfo == null ? true : rightInfo.isBST) &&
                (leftInfo == null ? true : leftInfo.max < head.value) &&
                (rightInfo == null ? true : rightInfo.min > head.value)) {
            //第一行条件,如果遇到叶节点则直接进来,因为叶节点null也是最大二叉搜索树
            //如果信息不为空,就要看左树isBST了,它时,同时满足
            //3和4的关系才行
            isBST = true;
            maxBSTSize = (leftInfo == null ? 0 : leftInfo.maxBSTSize) +
                    (rightInfo == null ? 0 : rightInfo.maxBSTSize) + 1;
            //这里如果第一次遇到叶节点,则最大节点只会加1,在这加1一定不是null叶节点
            //如果在这过程中,某一个子树没有点了,那不需要加,有才能加
        }

仔细搞懂这个整合信息的步骤
细细品味,咱们二叉树动态规划树形DP的递归套路,是不是很经典
左右收集信息,然后整理出x节点的信息,返回:
下面手撕代码:收集节点x的信息

//复习
    public static Info f(Node x){
        //base case,null没法和Integer对比,返回null
        if (x == null) return null;

        //x的左右树收集信息
        Info left = f(x.left);
        Info right = f(x.right);

        //整个四个信息
        //——min和max
        int min = x.value;
        int max = x.value;//如果左右都没有,那就我自己了呗
        if (left != null) {//左右树不null,需要更新
            min = Math.min(min, left.min);
            max = Math.max(max, left.max);
        }
        if (right != null) {//左右树不null,需要更新
            min = Math.min(min, right.min);
            max = Math.max(max, right.max);
        }

        //——整理isBST信息和maxBSTSize
        //如果最大二叉搜索树与x无关,那先得确定一下左右树有没有?
        int maxBSTSize = 0;//暂时默认为0
        boolean isBST = false;//暂时就不是x
        if (left != null) maxBSTSize = left.maxBSTSize;
        if (right != null) maxBSTSize = Math.max(maxBSTSize, right.maxBSTSize);//左右谁有,更新谁

        //默认最开始x不是最大二叉搜索树
        //判断一下,可能x是吗,满足三个条件,全部齐全
        boolean isLeftBST = left == null ? true : left.isBST;//null就是了
        boolean isRightBST = right == null ? true : right.isBST;
        boolean valueOK = (left == null ? true : left.max < x.value) &&
                (right == null ? true : x.value < right.min);//就是maxL<x<minR
        //仨都得满足,才能保证x就是二叉搜索树
        if (isLeftBST && isRightBST && valueOK){
            isBST = true;
            //同时更新maxBSTSize
            maxBSTSize = (left == null ? 0 : left.maxBSTSize) +
                    (right == null ? 0 : right.maxBSTSize) +
                    1;//算是x自己,左右节点数的和
        }

        return new Info(max, min, isBST, maxBSTSize);//顺序别搞反了
    }

三、从题目给的二叉树head开始求(2),得到了一个head的信息Info,找里面咱们要用的那个信息:比如是否平衡?返回它。
调用很简单,收集head的信息,反面里面的maxBSTSize

//调用
    public static int reviewGetMaxBSTNum(Node head){
        if (head == null) return 0;

        Info info = f(head);
        return info.maxBSTSize;
    }

    //树长啥样呢
    //          1
    //        2   3
    //       4 5 6 7
    //      8
    //     9
    //必然是1,结果最差就一个节点呗。
    public static void test(){

        Node cur = generateBinaryTree();
        int max = getMaxBSTNum(cur);
        int max2 = reviewGetMaxBSTNum(cur);
        System.out.println(max);
        System.out.println(max2);
    }


    //树长啥样呢
    //          4
    //        2   5
    //       1 3
    public static void test2(){
        Node cur = generateBinaryTree2();
        int max = getMaxBSTNum(cur);
        int max2 = reviewGetMaxBSTNum(cur);
        System.out.println(max);
        System.out.println(max2);
    }

    public static void main(String[] args) {
        test();
        test2();
    }

验证结果,如果x整个树都不是平衡搜索二叉树,就看其左右树了,
最次就是某个叶节点,它单独就是搜索二叉树,size=1(比如第1棵树)

如果确实x是整个搜索二叉树,那就返回x整个树上的节点数量(比如第二棵树)

1
1
5
5

再来看一个,不以头节点head开头的树为最大搜索二叉树,
而是在左树上有一个最大搜索二叉树
下面这个树,右边那个1违反规定了,重复出现,而且,它比4小,所以head不是BST
这个树的最大搜索二叉树就是2开头的子树,节点数量为3

    //构造一颗树,今后方便使用
    public static Node generateBinaryTree3() {
        //树长啥样呢
        //          4
        //        2   1
        //       1 3
        //这个最大搜索二叉树就是2开头的子树,节点为3
        Node head = new Node(4);
        Node n2 = new Node(2);
        Node n3 = new Node(1);//这里就违反了
        head.left = n2;
        head.right = n3;
        Node n4 = new Node(1);
        Node n5 = new Node(3);
        n2.left = n4;
        n2.right = n5;
        return head;
    }
    public static void test3(){
        Node cur = generateBinaryTree3();
        int max = getMaxBSTNum(cur);
        int max2 = reviewGetMaxBSTNum(cur);
        System.out.println(max);
        System.out.println(max2);
    }


    public static void main(String[] args) {
        test();
        test2();
        test3();
    }
3
3

果然,这个树形DP的递归套路很强大吧!


总结

提示:重要经验:

1)有时候,可能遇到叶节点左右为null时,我们要收集的最大值最小值不好确定,那就直接返回null信息,让整合信息时来确定x节点的信息。
2)树形DP的递归套路非常强大,这是可以解决绝大部分的二叉树动态规划问题的解题方法,一定要熟练使用。
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰露可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值