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

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

相关文章

Spring Boot Interceptor的原理、配置、顺序控制及与Filter的关键区别对比分析

《SpringBootInterceptor的原理、配置、顺序控制及与Filter的关键区别对比分析》本文主要介绍了SpringBoot中的拦截器(Interceptor)及其与过滤器(Filt... 目录前言一、核心功能二、拦截器的实现2.1 定义自定义拦截器2.2 注册拦截器三、多拦截器的执行顺序四、过

C# Semaphore与SemaphoreSlim区别小结

《C#Semaphore与SemaphoreSlim区别小结》本文主要介绍了C#Semaphore与SemaphoreSlim区别小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的... 目录一、核心区别概览二、详细对比说明1.跨进程支持2.异步支持(关键区别!)3.性能差异4.API 差

Java中自旋锁与CAS机制的深层关系与区别

《Java中自旋锁与CAS机制的深层关系与区别》CAS算法即比较并替换,是一种实现并发编程时常用到的算法,Java并发包中的很多类都使用了CAS算法,:本文主要介绍Java中自旋锁与CAS机制深层... 目录1. 引言2. 比较并交换 (Compare-and-Swap, CAS) 核心原理2.1 CAS

MySQL中VARCHAR和TEXT的区别小结

《MySQL中VARCHAR和TEXT的区别小结》MySQL中VARCHAR和TEXT用于存储字符串,VARCHAR可变长度存储在行内,适合短文本;TEXT存储在溢出页,适合大文本,下面就来具体的了解... 目录一、VARCHAR 和 TEXT 基本介绍1. VARCHAR2. TEXT二、VARCHAR

linux查找java项目日志查找报错信息方式

《linux查找java项目日志查找报错信息方式》日志查找定位步骤:进入项目,用tail-f实时跟踪日志,tail-n1000查看末尾1000行,grep搜索关键词或时间,vim内精准查找并高亮定位,... 目录日志查找定位在当前文件里找到报错消息总结日志查找定位1.cd 进入项目2.正常日志 和错误日

python中getsizeof和asizeof的区别小结

《python中getsizeof和asizeof的区别小结》本文详细的介绍了getsizeof和asizeof的区别,这两个函数都用于获取对象的内存占用大小,它们来自不同的库,下面就来详细的介绍一下... 目录sys.getsizeof (python 内置)pympler.asizeof.asizeof

Vue和React受控组件的区别小结

《Vue和React受控组件的区别小结》本文主要介绍了Vue和React受控组件的区别小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录背景React 的实现vue3 的实现写法一:直接修改事件参数写法二:通过ref引用 DOMVu

Go之errors.New和fmt.Errorf 的区别小结

《Go之errors.New和fmt.Errorf的区别小结》本文主要介绍了Go之errors.New和fmt.Errorf的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考... 目录error的基本用法1. 获取错误信息2. 在条件判断中使用基本区别1.函数签名2.使用场景详细对

Redis中哨兵机制和集群的区别及说明

《Redis中哨兵机制和集群的区别及说明》Redis哨兵通过主从复制实现高可用,适用于中小规模数据;集群采用分布式分片,支持动态扩展,适合大规模数据,哨兵管理简单但扩展性弱,集群性能更强但架构复杂,根... 目录一、架构设计与节点角色1. 哨兵机制(Sentinel)2. 集群(Cluster)二、数据分片

C#高效实现Word文档内容查找与替换的6种方法

《C#高效实现Word文档内容查找与替换的6种方法》在日常文档处理工作中,尤其是面对大型Word文档时,手动查找、替换文本往往既耗时又容易出错,本文整理了C#查找与替换Word内容的6种方法,大家可以... 目录环境准备方法一:查找文本并替换为新文本方法二:使用正则表达式查找并替换文本方法三:将文本替换为图