【数据结构】二叉查找树和平衡二叉树,以及二者的区别

2024-02-18 02:44

本文主要是介绍【数据结构】二叉查找树和平衡二叉树,以及二者的区别,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1、二叉查找树

1.1、定义

 1.2、查找二叉树的优点

 1.2、查找二叉树的弊端

2、平衡二叉树

2.1、定义

2.2、 实现树结构平衡的方法(旋转机制)

2.2.1、左旋

2.2.2、右旋

3、总结

1、二叉查找树

       二叉查找树又名二叉排序树,亦称二叉搜索树。是每个结点最多有两个子树的树结构,通常子树被称作“左子树”和“右子树”。

1.1、定义

二叉查找树的定义:

  1. 若左子树不空,则左子树上所有节点的值均小于它的根节点的值;
  2. 若右子树不空,则右子树上所有节点的值均大于它的根节点的值;
  3. 左、右子树也分别为二叉排序树;
  4. 没有键值相等的节点。

 1.2、查找二叉树的优点

普通二叉树和二叉查找树示例图如下所示:

       上图中左边为普通二叉树,它的弊端是对数据的插入没有要求,在进行数据的查找时就非常的麻烦,因为它里面的数据没有什么规律让我们进行查找。普通的二叉树如果想要查找数据的话就只能进行遍历,查找的效率非常的低。

        所以就有了上图中右边的二叉查找树,因为二叉查找树插入数据的时的规律是:小的存左边;大的存右边;一样的不存,并且它在查找数据的时候也是按照这个规律来进行查找的

 1.2、查找二叉树的弊端

现有数字{ 3,6,7,9,10,11,12 } ,基于这些数字生成二叉查找树如下:

这时候看起来二叉树的结构是很均匀的排列的,但是当依次插入13,14数据后,如下图:

       这时候发现,由于二叉树的根节点是确定不变的,所以当调整数据的插入或删除顺序,会造成二叉树朝着单项链表的方向发展(张歪了,变成歪脖子树了),大大降低了数据的查询效率。一棵树要提高查询效率,左右的高度要差不多才可以。那问题是在添加节点的时候,如何保证把它变成左右高度差不多长的这种树呢?这时候就出现了下面的平衡二叉树。

2、平衡二叉树

平衡二叉树是基于二叉查找树优化而来的。

2.1、定义

       非叶子结点最多只能有两个子结点,且左边子结点点小于当前结点值,右边子结点大于当前结点树,并且为保证查询性能增增删结点时要保证左右两边结点层级相差不大于1,具体实现有AVL、Treap、红黑树等。Java中TreeMap就是基于红黑树实现的。

       也就是说平衡二叉树是在二叉查找树的基础上又多了一个规则:任意节点左右子树高度差不超过1。

判断是否为平衡二叉树示例: 

示例1:

 

      上图中的二叉树是一个查找二叉树,但不是平衡二叉树。虽然根节点7左子树高度为3右子树高度为4,左右子树高度差不超过1。但平衡二叉树是需要任意节点都满足这个规则,比如节点10就不满足这个规则,节点10的左子树高度为0,右子树高度为3,左右子树高度差明显超过1了,所以它不是平衡二叉树。

示例2: 

 

       上图中的二叉树是一个平衡二叉树,因为它满足查找二叉树的同时还满足了规则:任意节点左右子树高度差不超过1。

2.2、 实现树结构平衡的方法(旋转机制)

        旋转机制右两种,分别是左旋和右旋。旋转机制的触发时机为:当添加一个节点之后,该树不再是一颗平衡二叉树时

2.2.1、左旋

左旋的步骤

先确定支点:从添加的节点开始,不断的往父节点找不平衡的节点

找到不平衡的节点后:

        (1)若不平衡的节点没有左子树,以不平衡的点作为支点,把支点左旋降级,变成左子节点,晋升原来的右子节点

        (2)若不平衡的节点有左子树,以不平衡的点作为支点,将根节点的右侧往左拉,原先的右子节点变成新的父节点,并把多余的左子节点出让,给已经降级的根节点当右子节点

左旋示例

示例1:

上图中左边的平衡二查树添加了节点12后,变成了右边的结构,这时候平衡被破坏了,所以要进行旋转。

先确定支点:从添加的节点12开始,不断的往父节点找不平衡的节点。我们发现节点10的左子树高度为0,右子树高度为2,所以找到不平衡的节点10。

找到不平衡的节点:不平衡的节点10没有左子树,以不平衡的点作为支点,把支点左旋降级,变成左子节点,晋升原来的右子节点

如下图所示:

通过左旋后就保持平衡了,还是一个平衡二叉树 

示例2:

上图中左边的平衡二查树添加了节点12后,变成了右边的结构,这时候平衡被破坏了,所以要进行旋转。

先确定支点:从添加的节点12开始,不断的往父节点找不平衡的节点。我们发现节点7的左子树高度为1,右子树高度为3,所以找到不平衡的节点7。

找到不平衡的节点:不平衡的节点7有左子树,以不平衡的点作为支点,将根节点的右侧往左拉,原先的右子节点变成新的父节点,并把多余的左子节点出让,给已经降级的根节点当右子节点

如下图所示:

 通过左旋后就保持平衡了,还是一个平衡二叉树 

2.2.2、右旋

右旋的步骤

先确定支点:从添加的节点开始,不断的往父节点找不平衡的节点

找到不平衡的节点后:

        (1)若不平衡的节点没有右子树,以不平衡的点作为支点,把支点右旋降级,变成右子节点,晋升原来的左子节点

        (2)若不平衡的节点有左子树,以不平衡的点作为支点,将根节点的左侧往右拉,原先的左子节点变成新的父节点,并把多余的右子节点出让,给已经降级的根节点当左子节点

右旋示例

示例1: 

上图中左边的平衡二查树添加了节点1后,变成了右边的结构,这时候平衡被破坏了,所以要进行旋转。

先确定支点:从添加的节点1开始,不断的往父节点找不平衡的节点。我们发现节点4的左子树高度为2,右子树高度为0,所以找到不平衡的节点4。

找到不平衡的节点:不平衡的节点4有左子树,以不平衡的点作为支点,将根节点的右侧往左拉,原先的右子节点变成新的父节点,并把多余的左子节点出让,给已经降级的根节点当右子节点

如下图所示:

  通过右旋后就保持平衡了,还是一个平衡二叉树 

示例2: 

上图中左边的平衡二查树添加了节点1后,变成了右边的结构,这时候平衡被破坏了,所以要进行旋转。

先确定支点:从添加的节点1开始,不断的往父节点找不平衡的节点。我们发现节点7的左子树高度为3,右子树高度为1,所以找到不平衡的节点7。

找到不平衡的节点:不平衡的节点7有左子树,以不平衡的点作为支点,将根节点的右侧往左拉,原先的右子节点变成新的父节点,并把多余的左子节点出让,给已经降级的根节点当右子节点

如下图所示:

  通过右旋后就保持平衡了,还是一个平衡二叉树 

3、总结

相同点:都是基于分治思想采用二分法的策略提高数据查找速度的二叉树结构。

不同点:

  1. 二叉查找树的根节点是不可变的,左右两边结点层级差没有限制;
  2. 平衡二叉树左右两边结点层级相差不大于1,通过旋转实现根节点可变,达到自平衡。

相对于二叉查找树,平衡二叉树的查询效率高,但由于增加和删除节点时,为了保证自平衡会做连续的旋转操作,平衡二叉树的额外开销比较大,耗时相对较长。

推荐:

【数据结构】前缀树的模拟实现-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/m0_65277261/article/details/136086068?spm=1001.2014.3001.5501

【计算机组成原理】存储器知识-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/m0_65277261/article/details/134770339?spm=1001.2014.3001.5501

java数据结构(哈希表—HashMap)含LeetCode例题讲解_leetcode hashmap.values()-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/m0_65277261/article/details/134712832?spm=1001.2014.3001.5501

这篇关于【数据结构】二叉查找树和平衡二叉树,以及二者的区别的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/719801

相关文章

JAVA覆盖和重写的区别及说明

《JAVA覆盖和重写的区别及说明》非静态方法的覆盖即重写,具有多态性;静态方法无法被覆盖,但可被重写(仅通过类名调用),二者区别在于绑定时机与引用类型关联性... 目录Java覆盖和重写的区别经常听到两种话认真读完上面两份代码JAVA覆盖和重写的区别经常听到两种话1.覆盖=重写。2.静态方法可andro

C++中全局变量和局部变量的区别

《C++中全局变量和局部变量的区别》本文主要介绍了C++中全局变量和局部变量的区别,全局变量和局部变量在作用域和生命周期上有显著的区别,下面就来介绍一下,感兴趣的可以了解一下... 目录一、全局变量定义生命周期存储位置代码示例输出二、局部变量定义生命周期存储位置代码示例输出三、全局变量和局部变量的区别作用域

MyBatis中$与#的区别解析

《MyBatis中$与#的区别解析》文章浏览阅读314次,点赞4次,收藏6次。MyBatis使用#{}作为参数占位符时,会创建预处理语句(PreparedStatement),并将参数值作为预处理语句... 目录一、介绍二、sql注入风险实例一、介绍#(井号):MyBATis使用#{}作为参数占位符时,会

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

Javaee多线程之进程和线程之间的区别和联系(最新整理)

《Javaee多线程之进程和线程之间的区别和联系(最新整理)》进程是资源分配单位,线程是调度执行单位,共享资源更高效,创建线程五种方式:继承Thread、Runnable接口、匿名类、lambda,r... 目录进程和线程进程线程进程和线程的区别创建线程的五种写法继承Thread,重写run实现Runnab

C++中NULL与nullptr的区别小结

《C++中NULL与nullptr的区别小结》本文介绍了C++编程中NULL与nullptr的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编... 目录C++98空值——NULLC++11空值——nullptr区别对比示例 C++98空值——NUL

Conda与Python venv虚拟环境的区别与使用方法详解

《Conda与Pythonvenv虚拟环境的区别与使用方法详解》随着Python社区的成长,虚拟环境的概念和技术也在不断发展,:本文主要介绍Conda与Pythonvenv虚拟环境的区别与使用... 目录前言一、Conda 与 python venv 的核心区别1. Conda 的特点2. Python v

Go语言中make和new的区别及说明

《Go语言中make和new的区别及说明》:本文主要介绍Go语言中make和new的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1 概述2 new 函数2.1 功能2.2 语法2.3 初始化案例3 make 函数3.1 功能3.2 语法3.3 初始化

MySQL中查找重复值的实现

《MySQL中查找重复值的实现》查找重复值是一项常见需求,比如在数据清理、数据分析、数据质量检查等场景下,我们常常需要找出表中某列或多列的重复值,具有一定的参考价值,感兴趣的可以了解一下... 目录技术背景实现步骤方法一:使用GROUP BY和HAVING子句方法二:仅返回重复值方法三:返回完整记录方法四:

深度解析Spring Boot拦截器Interceptor与过滤器Filter的区别与实战指南

《深度解析SpringBoot拦截器Interceptor与过滤器Filter的区别与实战指南》本文深度解析SpringBoot中拦截器与过滤器的区别,涵盖执行顺序、依赖关系、异常处理等核心差异,并... 目录Spring Boot拦截器(Interceptor)与过滤器(Filter)深度解析:区别、实现