数据结构与算法分析笔记与总结(java实现)--二叉树5:平衡二叉树判断练习题

视频(3)

子树的概念

平衡二叉树(AVL树)

平衡二叉树又叫作AVL树,所谓平衡二叉树是指对于这棵树中的任意根结点,他的左子树和右子树的高度是平衡的,即高度差是0或者1,何为子树的高度?所谓树的高度是指从根结点开始到叶子结点的最长的路径上的结点的数目(与书上有所不同但是以此为准),因此如果一棵二叉树,上面的任何根结点的左右子树的高度差小于1,那么这棵树就是平衡二叉树。直观的来看,如果某个结点开始的左边的最长路径与右边的最长路径的长度差大于1,那么这个结点就是不平衡的,即以该结点为根的树是不平衡的;反之,如果任何一个结点的左右最长路径都小于等于1,那么这棵树就是平衡的二叉树。

 

Tree5:平衡二叉树判断练习题

题目:有一棵二叉树,请设计一个算法判断这棵二叉树是否为平衡二叉树。给定二叉树的根结点root,请返回一个bool值,代表这棵树是否为平衡二叉树。

思路:根据定义,要判断一棵二叉树是否是平衡二叉树需要判断树上的每一个结点的左右子树的高度差是否小于等于1,显然需要遍历左右的根结点。其实对于二叉树而言,遍历时使用的遍历方式无非是先序遍历、中序遍历、后序遍历、按层遍历,选取合适的遍历方式即可。其中,先序遍历实际上是从上到下遍历结点的,由于是先根结点再左右子树,因此大致上是从上到下进行的;中序遍历是先左子树再中间根结点再右子树,因此大致上是从下开始的;后序遍历是先左结点再右结点再根结点,因此大致上是从下向上的,按层遍历特征太明显,从上到下,从左到右。本题中,要判断是否是平衡二叉树,需要对每个结点进行判断,对于每个结点又要得到它的左右子树的高度,如果从上到下遍历会对下部的子树进行多次的重复判断因此应该从下往上遍历子树判断子树是否平衡,即先判断左子树是否平衡,并求出其最大的高度,如果不平衡则整棵树不平衡;同理再求右子树是否平衡以及求出最大的高度,如果不平衡则整棵树不平衡;如果左右子树都平衡,那么比较2左右子树是否平衡即可,显然这是一个递归的过程。由于是先遍历左子树,再遍历右子树再遍历根结点,因此显然应该对后续遍历进行改造。


二叉树难的地方在于很多时候需要使用递归,而且是需要携带返回值的递归,此时高度抽象,但是不要灰心,需要多理解,多练习,找找感觉。

很巧妙,很简单:

其实还是一个递归的过程,构造一个递归函数,函数的功能是给定一个一棵子树的根结点root,返回这棵树的高度:递推关系是:对于任何一个跟结点,它的高度是其左右子树高度+1,即比较得到2个子树的高度,选取较大的高度+1即为当前结点的高度,于是求某个结点的高度转化为求它的子树的高度,这显然是一个递推的过程,递推的边界条件是当一直向下递推时,如果某个结点的子树为null,那么这棵子树的高度为0,同时表示到达叶子结点,递归结束return即可。由于需要判断是否是平衡二叉树,于是在求出左右子树的高度同时稍微改编加一些逻辑即可:

写一个递归函数,输入一个根结点,判断这个根结点所在的树是否是平衡二叉树,如果是则返回这棵树的高度,如果不是就返回-1;

①递归的递推条件:判断一棵二叉树是否平衡并求出高度,可以转化为分别判断左右二叉树是否平衡并求出其高度,如果有某一棵子树不是平衡的就直接return -1,如果2子树都不返回-1,那么判断2子树返回的高度差是否<=1,如果不是则返回-1,如果是就返回2棵子树中高度较大的值作为当前子树的高度。

②基准情形:当root为null时,说明是求null结点的高度,显然是0,直接返回0即可。

基准情形也可以理解为边界条件,因为此时的结果是明显的,不需要递归调用,可以直接返回一个明确的值,或者不返回值而是直接return,例如之前遍历时当stack==null时直接return也可以认为是基准情形。

关键是根据逻辑要求构造出一个递归函数。

 

importjava.util.*;

//判断一棵二叉树是否是平衡二叉树,使用递归,以子树的高度作为返回值

publicclass CheckBalance {

    public boolean check(TreeNode root) {

        //调用递归方法来判断是否是平衡的

       //如果返回-1表不平衡,如果返回一个具体的值,说明树平衡,返回的是树的高度值

        booleanresult=this.getHeight(root)==-1?false:true;

        return result;

    }

    //这是一个递归的方法,用于返回一棵二叉树的高度,如果平衡返回高度,如果不平衡返回-1

    private int getHeight(TreeNode root){

       //基准情形

        if(root==null) return 0;

        //先求左子树的高度

        intleftHeight=this.getHeight(root.left);

       //判断左子树是否平衡,调用递归方法后总是认为这个方法已经全部执行完毕

        if(leftHeight==-1) return -1;

       //再求右子树的高度

        intrightHeight=this.getHeight(root.right);

       //判断右子树是否平衡

        if(rightHeight==-1) return -1;

       //判断高度差是否过大

        if(Math.abs(leftHeight-rightHeight)>1) return -1;

       //执行到此处说明二叉树平衡,返回此树的高度(子树较大值+1

        returnMath.max(leftHeight,rightHeight)+1;

    }

}

 

搜索二叉树

搜索二叉树又叫作二叉树查找树或者二叉排序树,


所谓搜索二叉树是指对于任何一个结点,它的左子树的所有结点都比这个根结点要小,它的右子树的所有结点都比这个根结点要大。注意是根结点与左右子树上所有的结点进行比较而不是仅仅与左右孩子结点进行比较,因此根据这个定义,那么当按照中序遍历来遍历一棵搜索二叉树时,必然是单调递增的,即是有序的序列,反之,如果一棵二叉树按照中序遍历得到的序列时有序的,那么这棵二叉树一定是搜索二叉树,因此对于搜索二叉树通常总是进行中序遍历的操作。

红黑树、平衡搜索二叉树(AVL树)等其实都是搜索二叉树的不同实现,它们都努力使得搜索二叉树的搜索效率更高,调整代价更小。

题目:判断一棵二叉树是否是搜索二叉树

思路:根据定义检查在进行中序遍历时结点值是否递增即可,如果一旦出现减小,必然不是搜索二叉树。

可以使用递归也可以不使用递归,当使用递归时注意,由于每次遍历到的结点需要与上一个结点进行比较,因此要保留上一个结点的值,因此在递归外面要设置一个成员变量temp(不能是局部变量)用来记住上一次遍历到的结点值,如果本次结点>=temp表示符合要求,再将temp替换为当前的值即可。并且,为了使得当遇到<temp的结点时停止,因此要设置一个falg变量作为标记符,每次判断当前值与temp时如果不符合要去就将falg设置为false,然后结束方法。

packagecom.caicainiao.nowcoder;

//主函数

publicclass SearchBinaryTree {

         public static void main(String[] args){

                   TreeNode node1=newTreeNode(1);

                   TreeNode node2=newTreeNode(2);

                   TreeNode node3=newTreeNode(3);

                   TreeNode node4=new TreeNode(4);

                   TreeNode node5=newTreeNode(5);

                   TreeNode node6=newTreeNode(6);

                   TreeNode node7=newTreeNode(7);

                   node4.left=node2;

                   node4.right=node6;

                   node2.left=node1;

                   node2.right=node3;

                   node6.left=node5;

                   node6.right=node7;

                   new SearchBinaryTree().checkIsSearch(node4);

         }

        

         //辅助变量:成员变量,所有递归栈共享

         int temp;

         boolean flag=true;

        

         //判断是否是搜索二叉树,中序遍历

         public void checkIsSearch(TreeNodetreeNode ){

                   temp=Integer.MIN_VALUE;

                   this.inOrderRecru(treeNode);

                   if(flag){

                            System.out.println("是搜索二叉树");

                   }else{

                            System.out.println("不是搜索二叉树");

                   }

         }

        

         //中序遍历,同时判断是否递增

          private void inOrderRecru(TreeNode treeNode){

                    //这是一个递归方法,要有结束的边界条件,当没有子节点时返回

                if(treeNode==null) return;

                //①先遍历子树的左子树

                this.inOrderRecru(treeNode.left);

                //②与前一个结点值比较

                if(treeNode.val<temp){

                   flag=false;

                   return;

                }

                temp=treeNode.val;

                //③遍历子树的右子树

                this.inOrderRecru(treeNode.right);

            }

}

满二叉树与完全二叉树

所谓的满二叉树是指一棵二叉树中除了最后一层的结点没有任何子节点之外,剩下每一层上的结点都有2个子节点,即直观地看,满二叉树没有任何确实的结点。对于满二叉树,它的结点数目与层数存在直接的对应关系,如果层数为L,那么满二叉树的结点数目为N=2^L-1,反之L=log2(N+1)

 

所谓完全二叉树是指除了最后一层之外,其他每一层的结点数目都是满的,如果最后一层也满了就是满二叉树,如果最后一层不满那么结点全部集中在左侧。满二叉树是一棵特殊的完全二叉树,对于完全二叉树这种特殊的结构,它可以使用数组来表示各个结点,此时每个结点与它的子节点的位置下标之间存在直接的对应关系,即从数组中i=1开始存放元素,于是对于某个结点i,它的左孩子下标是2*i,它的右孩子结点时2*i+1,它的父节点下标是i/2。堆(优先队列)就是一种完全二叉树结构。


(1)非递归定义 树(tree)是由n(n≥0)个结点组成的有限集合。n=0的树称为空树;n>0的树T ① 有且仅有一个结点n0,它没有前驱结点,只有后继结点。n0称作树的根(root)结点。 ② 除结点外n0 , 其余的每一个结点都有且仅有一个直接前驱结点;有零个或多个直接后继结点。 (2)递归定义 一颗大树分成几个大的分枝,每个大分枝再分成几个小分枝,小分枝再分成更小的分枝,… ,每个分枝也都是一颗树,由此我们可以给出树的递归定义。 树(tree)是由n(n≥0)个结点组成的有限集合。n=0的树称为空树;n>0的树T ① 有且仅有一个结点n0,它没有前驱结点,只有后继结点。n0称作树的根(root)结点。 ② 除根结点之外的其他结点分为m(m≥0)个互不相交的集合T0,T1,…,Tm-1,其中每个集合Ti(0≤i<m)本身又是一棵树,称为根的子树(subtree)。 2、掌握树的各种术语 (1) 父母、孩子兄弟结点 (2) 度 (3) 结点层次、树的高度 (4) 边、路径 (5) 无序树、有序树 (6) 森林 3、二叉树的定义 二叉树(binary tree)是由n(n≥0)个结点组成的有限集合,此集合或者为空,或者由一个根结点加上两棵分别称为左、右子树的,互不相交的二叉树组成。 二叉树可以为空集,因此根可以有空的左子树或者右子树,亦或者左、右子树皆为空。 4、掌握二叉树的五个性质 5二叉树的二叉链表存储。

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论

打赏作者

小菜鸟也想飞

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

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值