二叉树的递归套路

 

二叉树的递归套路

  1. 假设以X节点为头,假设可以向X左子树和X右子树要任何信息

  2. 在上一步的假设下,讨论以X为头节点的树,得到答案的可能性(最重要)也就是以答案结果跟X有没有关系为划分讨论

  3. 列出所有可能性后,确定到底需要向左子树和右子树要什么信息

  4. 左子树信息和右子树信息求全集,就是任何一颗子树都需要返回的信息info

  5. 递归函数都返回info,每一棵子树都这么要求

  6. 写代码,在代码中考虑如何把左子树和右子树信息整合出整棵树的信息

例一:平衡二叉树

什么是平衡二叉树:

在一棵二叉树中,每一棵子树的左子树和右子树的高度差小于2,分解可得到:

  1. 左子树必须是平衡的

  2. 右子树必须是平衡的

  3. 左子树和右子树的高度差小于2

给定一棵二叉树的头节点head,返回这棵二叉树是不是平衡二叉树

根据套路(1)我们可以写代码

  • 假设我们可以获取到左右子树的信息是否为平衡二叉树、高度是多少

//信息类,代表,能从子树上获取什么信息
class Info{
    private boolean blance;//当前子树是否平衡
    private int height;//当前子树的高度,用来返回给头节点判断是否平衡的
    public Info(boolean blance, int height){
        this.blance = blance;
        this.height = height;
    }
    public boolean isBlance(){
        return blance;
    }
    public int getHeight(){
        return height();
    }
    public void setBlance(boolean blance){
        this.blance = blance;
    }
    public void setHeight(int height){
        this.height = height;
    }
}
class getBlanceTree{
    public Info process(Node X){
        //如果一个空树,肯定是平衡的,并且高度为0,返回
        if(X == null){
            return new Info(true, 0);
        }
        //在左子树上递归,得到左子树的信息:是否平衡,高度是多少
        Info leftInfo = process(X.left);
        //在右子树调用递归,获取右子树的信息
        Info leftRight = process(X.right);
        //接下来的代码就是在当前节点上进行判断
        //当前的节点的高度=左/右子树中最大的的高度+1
        int height = Math.max(leftInfo.getHeight, right.getHeight) + 1;
        //设置当前是否平衡的变量为真
        boolean blance = true;
        //如果左子树、右子树上、当前的子树上,有一个不平衡的,当前x为头节点子树就不平衡
        //判断当前子树是不是平衡:它左右子树的高度差是否小于2
        if(!leftInfo.isBlance() || !rightInfo.isBlance() || Math.abs(left.height - right.height) >= 2){
           blance = false; 
        }
        return new Info(blance, height);
    }
}

例二:最大距离

给定一棵二叉树的头节点head,任何两个节点之间都存在着距离,返回整个二叉树的最大距离

什么是距离

一个节点通过树杈走到另一个节点,中间最短需要经过的节点数,比如如下的树,要从a走到b,经过3个节点,ab距离就是3

最大距离的两种情况

  1. 假如和当前头节点X无关,整棵树的最大距离为左子树的最大距离或者右子树的最大距离,谁大就是谁

  2. 假如和当前头节点X有关,整棵树的最大距离为左子树的高度+右子树的高度+1

根据套路(1)我们可以写代码

  • 假设我们可以获取到左右子树的信息当前节点的最大距离是多少、高度是多少

class Info{
    public int maxDistance;//当前节点的最大距离
    public int height;//高度是多少
    public Info(int maxD, int height){
        this.maxDistance = maxD;
        this.height = height;
    }
}
class getMaxDistance{
    public Info process(Node X){
        if(X == null){
            return new Info(0, 0);
        }
        //在左子树调用递归,获取左子树的信息,左子树上的最大距离,左子树的高度
        Info leftInfo = process(X.left);
        //在右子树调用递归,获取右子树的信息
        Info rightInfo = process(X.right);
        int height = Math.max(leftInfo.height, rightInfo.height) + 1;//当前X节点为头节点的子树的高度
        //整个树的最大距离就是两种情况中最大的那个
        int maxDistance = Math.max(
                          //假如和X无关就是左/右子树的最大距离之间的较大值
                          Math.max(leftInfo.maxDistance, rightInfo.maxDistance), 
                          //假如和X有关,就是左子树高度+右子树高度+1
                          leftInfo.height + rightInfo.height + 1);
        return new Info(maxDistance, height);
    }
}

求满足搜索二叉子树的最大子树 —— 把左子树信息和右子树信息求全集

给定一棵二叉树的头节点head,返回这棵二叉树中最大的二叉搜索子树的头节点

什么是整体搜索二叉树

  1. 整个树上没有重复值

  2. 左树的值比当前的节点小右树的值比当前节点大

  3. 从当前节点为头节点的每一棵子树都如此

分情况解决

  1. 如果最大搜索二叉树与X无关,就是X左 / 右子树上最大二叉树的较大树这一情况永远成立!

  2. 如果与X有关,直接返回X,就是说X就是最大搜索二叉树的头节点,满足这个需要有如下条件

    1. 左边的子树必须是搜索二叉树

    2. 右边的子树必须是搜索二叉树

    3. 左边的最大值小于X,右边的最小值大于X

需要从左右子树上获取什么信息

左树
  1. 是否是最大搜索二叉树

  2. 最大搜索二叉树的Size

  3. 最大值

右树
  1. 是否是最大搜索二叉树

  2. 最大搜索二叉树的Size

  3. 最小值

为了方便代码编写,可以总结为子树的信息
  1. 是否是最大搜索二叉树

  2. 最大搜索二叉树的Size

  3. 最小值

  4. 最大值

class Info{
    public boolean isAllBST;//是否是最大搜索二叉树
    public int maxSubBSTSize;//最大搜索二叉树的节点个数
    public int min;//整个树最小值
    public int max;//整个树最大值
}
class maxBST{
    public void process(Node X){
        if(X == null){
            return null;
        }
        //在当前节点的左右子树上调用递归函数,获取需要的值
        Info leftInfo = process(X.left);
        Info rightInfo = process(X.right);
        
        //当前节点X为头节点的子树的最小值
        int min = X.value;
        //当前节点X为头节点的子树的最大值
        int max = X.value;
        //当左子树不为空,更新当前子树的最大最小值
        if(leftInfo != null){
            max = Math.max(max, leftInfo.max);
            min = Math.min(min, leftInfo.min);
        }
        //当右子树不为空,更新当前子树的最大最小值
        if(rightInfo != null){
            max = Math.max(max, rightInfo.max);
            min = Math.min(min, rightInfo.min);
        }
        
        //设置当前节点的最大搜索二叉树节点数为0
        int maxSubBSTSize = 0;
        //有左子树先设置进去,减少运行时间
        if(leftInfo != null){
            maxSubBSTSize = leftInfo.maxSubBSTSize;
        }
        //再去和右子树进行比较,得到更大的搜索二叉树的节点数
        if(rightInfo != null){
            maxSubBSTSize = Math.max(maxSubBSTSize, rightInfo.maxSubBSTSize);
        }
        
        boolean isAllBST = false;
        //X节点为头节点的二叉树为搜索二叉树必须同时满足三个条件
        if(    //条件1,它的左子树为搜索二叉树,注意:空树也算搜索二叉树
            (leftInfo == null ? true :leftInfo.isAllBST)
            //((leftInfo != null && leftInfo.isAllBST == true) || leftInfo == null)
            && //条件2,它的右子树为搜索二叉树
            (rightInfo == null? true : rightInfo.isAllBST)
            //((rightInfo != null && leftInfo.isAllBST == true) || rightInfo == null)
            && //条件3,左子树最大值小于X.value,右子树最小值大于X.value,注意左右为空树的最大值最小值不用判断但是也要写上
            (leftInfo == null ? true : leftInfo.max < X.value
               &&
            rightInfo == null ? true : rightInfo.min > X.value)
            /*(leftInfo != null && leftInfo.max < X.value || leftInfo == null)
            &&
            (rightInfo != null && rightInfo.min > X.value || rightInfro == null)
            )*/
            {
            //当前搜索二叉树的最大值,首先判断左右儿子是否为空
            maxSubBSTSize = (leftInfo == null ? 0 : leftInfo.maxSubBSTSize) + 
                            (rightInfo == null ? 0 : rightInfo.maxSubBSTSize) + 1;
            isAllBST = true;
        }
        return new Info(isAllBST, maxSubBSTSize, min, max);
    }
}

leetcode:236. 二叉树的最近公共祖先

使用二叉树的递归思想解决,假设当前头节点为X

首先我们确定我们要在左右两子树上要什么信息——这棵以X为头节点的子树上是否有p,q节点(包括X自己)isHave,当前最近公共祖先LCANode

class Info{
    public boolean isHave;
    public TreeNode LCANode;
    public Info(boolean have, TreeNode L){
        isHave = have;
        LCANode = L;
    }
}

然后我们分类讨论,这个问题是否跟X有关,得到的答案——是的

  1. 和X无关

    • 也就是说X点不是p/q节点

    • 我们需要获取X的左右子树上是否有p和q节点,如果有,设置子树的Info.isHave为true

    • 如果p、q两点,分别在X的左右子树上,则X就是最近公共祖先,LCANode = X

    • 如果X的左右子树上产生了LCANode,就弹出对应的信息

  2. 和X有关

    • 也就是说X是p/q节点

    • 我们需要获取X的左右子树上是否有p/q节点,如果有子树info.isHave为true

    • 如果p / q点在X的左/右子树上,X就是最近公共祖先,LCANode = X

    • 如果X的左右子树上产生了LCANode,就弹出对应的信息

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        return process(root, p, q).LCANode;
    }
    public Info process(TreeNode X, TreeNode p, TreeNode q){
        if(X == null){
            return new Info(false, null);
        }
        Info leftInfo = process(X.left, p, q);
        Info rightInfo = process(X.right, p, q);
        Boolean isHave = false;
        TreeNode LCANode = null;
        //如果在左右子树找到了LCANode了就弹出对应的信息,不用再往下继续了
        if(leftInfo.LCANode != null){
            return new Info(leftInfo.isHave, leftInfo.LCANode);
        }else if(rightInfo.LCANode != null){
            return new Info(rightInfo.isHave, rightInfo.LCANode);
        }
        if(X == p || X == q){//如果X自己就是p/q
            //如果X自己就是p/q isHave必定为True
            isHave = true;
            //这种情况下左右子树上必定不能产生LCANode,因为pq其中一个点就是X
            LCANode = leftInfo.isHave || rightInfo.isHave ? X : LCANode;           
        }else{//如果X自己不是p/q
            //X的左右子树上是否有p和q节点,如果有,设置子树的Info.isHave为true
            isHave = leftInfo.isHave || rightInfo.isHave ? true : isHave;
            //如果p、q两点,分别在X的左右子树上,则X就是最近公共祖先,LCANode = X
            LCANode = leftInfo.isHave && rightInfo.isHave ? X : LCANode;
        }
        return new Info(isHave, LCANode);
    }
}

有待优化!

posted on 2022-08-18 22:31  老菜农  阅读(31)  评论(0编辑  收藏  举报

导航