分享一个关于Avl树的迭代器算法
1 研究过程
前段时间在研究avl树的迭代实现,在节点不使用parent指针的情况下,如何使用堆栈来实现双向地迭代。我参考了网络上的大部分迭代器实现,要么是使用了parent指针(就像c++的map容器中的迭代算法),要么就是前中后序遍历,没找到一种真正意义上可以双向迭代的算法,于是乎在我的不屑努力下,基于灵感想到了一个只使用很低层数的堆栈就可以完成双向迭代的算法。
我把它命名为“基于双向堆栈的avl树双向迭代算法”。
这个算法分为三个主要部分:初始化、前向迭代(next)、后向迭代(previous)。下面我将以文字的形式详细说明这三个算法。
2 算法步骤
2.1 初始化算法
1.定义需要使用的变量:一个长度为40的用于存放树节点的指针数组stack,模拟双向堆栈,存放前进或后退方向的节点。2个长度变量,分别是stack_next_len以及stack_prev_len,分别初始化为0和39(stack的长度-1)。以下将stack_next_len统称为前向索引,将stack_prev_len统称为后向索引。前者用于存放前进方向的节点指针,后者用于存放后退方向的节点指针。当前的高度cur_height,用于保存当前所处节点的高度,并用于更新在后续迭代过程中节点的高度。状态量status,表示当前迭代的边界状态。
2.根据用户所选的迭代起始位置(最小值节点或最大值节点),初始化stack。
2.1) 如果从最小值节点开始迭代,那么从树根节点开始,下潜到最左侧节点,并依次将下潜过程中遍历的所有节点从前向索引往后(从0到39的方向)依次存入stack。
2.2) 如果从最大值节点开始迭代,那么从树根节点开始,下潜到最右侧节点,并依次将下潜过程中遍历的所有节点从后向索引往前(从39到0的方向)依次存入stack。
3.根据下潜的方向,修改status,如果是左侧下潜,那么状态为1(表示当前的位置是处于比树的最左侧节点还要小的一个假象相邻节点),如果是右侧下潜,那么状态为2(表示当前的位置是处于比树的最右节点还要大的一个假象相邻节点)。若树为空,则将status设置为3。
2.2 前向迭代的算法
在完成了初始化之后,根据用户传入的迭代方向,调整双向堆栈的内容和迭代器的状态。
以下是执行next(前向)动作的迭代算法:
如果迭代器的status = 0,说明当前的迭代状态正常,没有处于边界条件。接下来判断迭代器所指向节点是否存在右子节点。
1.1) 如果存在,那么将当前节点存入双向堆栈的后向索引处,索引递减1(表现为向双向堆栈的尾部堆入一个节点)。随后将节点设置为其右子节点R,并执行一个循环。该循环的作用表现为:从R节点左向下潜到其最左侧节点LM,并依次更新下潜过程中的节点高度,将它们存入到双向堆栈的前向索引处,然后递增1(表现为向双向堆栈的首部堆入一个节点)。完成后返回LM所指数据。
1.2) 如果不存在,且前向索引的值不为0。那么取出前向索引所处的节点N。设当前节点的高度减去N节点的高度再减去1的值为m,然后将后向索引的值加上m(表现双向堆栈的尾部出栈m个节点)。然后更新迭代器高度为N的高度。完成后返回N所指数据。
1.3) 如果上述都不满足,那么说明节点达到了比二叉树的最右节点还要大的位置(设为limR)。设置status=2,并返回空指针。
如果当前status = 1,那么说明当前的位置处于二叉树最左节点还要小的位置(设为limL),此时进行next操作,那么所指向的节点必定是二叉树的最左节点L。设置迭代器status = 0,表示状态正常,然后返回L所指数据。
如果当前status = 2,那么说明在到达limR之后还在尝试next操作,这种操作是无效的。只需返回空指针。
如果status = 3,直接返回空指针
2.3 后向迭代的算法
后向迭代的算法与前向迭代大致相同,只需要将对应的方向和索引进行交换即可。
以下是执行previous(后向)动作的迭代算法:
如果迭代器的status = 0,说明当前的迭代状态正常,没有处于边界条件。接下来判断迭代器所指向节点是否存在左子节点。
1.1) 如果存在,那么将当前节点存入双向堆栈的前向索引处,索引递减1(表现为向双向堆栈的首部堆入一个节点)。随后将节点设置为其左子节点L,并执行一个循环。该循环的作用表现为:从L节点右向下潜到其最右侧节点RM,并依次更新下潜过程中的节点高度,将它们存入到双向堆栈的后向索引处,然后递减1(表现为向双向堆栈的尾部堆入一个节点)。完成后返回RM所指数据。
1.2) 如果不存在,且后向索引的值不为39(栈的最大高度-1)。那么取出后向索引所处的节点N。设当前节点的高度减去N节点的高度再减去1的值为m,然后将前向索引的值减去m(表现双向堆栈的首部出栈m个节点)。然后更新迭代器高度为N的高度。完成后返回N所指数据。
1.3) 如果上述都不满足,那么说明节点到达了limL(见上文)。设置status=1,并返回空指针。
如果当前status = 2,那么说明当前的位置在limR(见上文),此时进行previous操作,那么所指向的节点必定是二叉树的最右节点R。设置迭代器status = 0,表示状态正常,然后返回R所指数据。
如果当前status = 1,那么说明在到达limL之后还在尝试previous操作,这种操作是无效的。只需返回空指针。
如果status = 3,直接返回空指针
3 算法原理分析
基于堆栈的双向迭代算法,实现难点在于如何保证在双向任意次数迭代的情况下而不会产生节点的丢失以及失序的问题,解决这个问题的核心在于,如何根据前后迭代状态合理地更新两个堆栈的高度。
根据二叉树迭代过程中的下潜方向,我们分别用两个堆栈保存左向下潜和右向下潜迭代过程的所有节点,并且在无法通过下潜获取符合迭代方向的节点时,我们取出自身堆栈顶部的节点。这个节点代表需要回退的位置。如图1所示,假设当前节点是C,那么根据二叉树性质可知,B节点一定位于C节点之后迭代,且A节点一定位于C节点之前迭代。若此时执行previous操作,而节点B位于节点A和节点C之间,那么B节点应该失效。如果当前所处的节点是D,在执行next操作后,应该回退到节点R,并且在R与D之间的节点A和B都应该失效。而根据这一个过程,可以得知,若发生回退,则一定只可能且只有同一个下潜方向的节点需要被消除。而不同下潜方向的节点被保存在了不同堆栈,所以只需更新与自身相对的另一个堆栈的高度即可。且这两个堆栈的高度总和不会超过树高-1的长度,因此可以采用双向堆栈进一步节省空间。

图1 AVL树实例
4 算法扩展
基于上述的算法流程,可以延伸出其他算法的迭代器版本(不考虑多线程)。
首先是查找算法,在需要获取某一个节点并对其附近的节点进行迭代时,只需要把查找路径上的节点,按照下潜的方向依次存入双向堆栈即可。
其次是插入算法,在插入某一个节点之后,只需自顶向下,再次使用迭代器版本的查找算法即可重新构建查找路径。时间复杂度为:
\]
最后是删除算法,如果删除的节点是迭代器所指的节点,那么在执行删除时,只需要将节点高度从小到大,进行合并,然后使用自底向上的删除算法,对树结构进行重构,并且将于删除的节点值最接近的节点更新到迭代器所指节点,然后在回退至根节点的过程中,如果发生旋转操作,只需要根据旋转的类型,调整双向堆栈的结构即可。时间复杂度为:
\]
5 多线程可行性
以下将对多个线程使用同一个avl树对象进行迭代的情况做讨论。
经过理论分析可以得到,在多线程环境下,使用parent指针与使用双向堆栈的迭代算法在进行静态迭代时都不会失效,而使用parent指针的迭代算法,在迭代过程中如果需要进行插入操作,那么只需要加上互斥语句即可。而使用parent指针的迭代算法在删除操作的情况下,有非常低的概率会其他线程的迭代器失效,这种情况是所删除的节点正好被其他迭代器访问。
使用双向堆栈迭代的迭代算法,在进行删除操作以及插入操作时,都会使其他线程的迭代器失效,原因是树结构的改变不会实时更新到其他线程的堆栈上,并且各个迭代器的堆栈状态是不一样的,这样的同步代价就很高。解决方法是,在任意一个线程使用插入或删除操作之后,通知其余正在使用迭代器的进程,从avl树的根节点开始,将迭代器的当前所指数据指针作为查找依据,进行一个模糊查找,查找到最接近的那一个节点,从而完成双向堆栈的重构。使用这种方法时,在删除操作上同样存在与使用parent指针时一样的问题。如果使用的是指针类型来做查找依据,那么在查找时需确保备份了自身数据指针所指的数据,避免出现悬挂指针。这两种操作在有效的情况下,同步的时间复杂度如下:
\]
其中是第i个线程的节点数量。
还可以使用读写锁的方式进一步简化时间复杂度,简化后的时间复杂度如下
\]
其中O(1)表示获取读写锁的时间,后部表示节点数量最大的线程查找一次某个节点的时间。
6 其他探讨
也许会存在其他能够进一步缩短迭代器重新调整时间的实现,也欢迎各位能把自己的看法分享出来,希望能够互相探讨。
源码的链接:https://gitee.com/luqi_866813670/MyLib
分享一个关于Avl树的迭代器算法的更多相关文章
- AVL树的平衡算法(JAVA实现)
1.概念: AVL树本质上还是一个二叉搜索树,不过比二叉搜索树多了一个平衡条件:每个节点的左右子树的高度差不大于1. 二叉树的应用是为了弥补链表的查询效率问题,但是极端情况下,二叉搜索树会无限接近 ...
- AVL树的JAVA实现及AVL树的旋转算法
1,AVL树又称平衡二叉树,它首先是一颗二叉查找树,但在二叉查找树中,某个结点的左右子树高度之差的绝对值可能会超过1,称之为不平衡.而在平衡二叉树中,任何结点的左右子树高度之差的绝对值会小于等于 1. ...
- 分享一个我改进过的ocr算法
https://github.com/zhangbo2008/chineseOCR-jingjianban 欢迎大家前来拍砖
- 数据结构和算法(Golang实现)(28)查找算法-AVL树
AVL树 二叉查找树的树高度影响了查找的效率,需要尽量减小树的高度,AVL树正是这样的树. 一.AVL树介绍 AVL树是一棵严格自平衡的二叉查找树,1962年,发明者Adelson-Velsky和La ...
- 数据结构图文解析之:AVL树详解及C++模板实现
0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...
- 纸上谈兵:AVL树
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 二叉搜索树的深度与搜索效率 我们在树, 二叉树, 二叉搜索树中提到,一个有n个节点 ...
- 纸上谈兵: AVL树[转]
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 二叉搜索树的深度与搜索效率 我们在树, 二叉树, 二叉搜索树中提到,一个有n个节点 ...
- 使用C编程语言实现AVL树
本文将介绍AVL树及其插入.删除操作,最后使用C编程语言实现基于平衡因子(balance factor)的AVL树. 什么是AVL树? AVL树(AVL tree)是前苏联计算机科学家Adelson- ...
- PAT 1066 Root of AVL Tree[AVL树][难]
1066 Root of AVL Tree (25)(25 分) An AVL tree is a self-balancing binary search tree. In an AVL tree, ...
- AVL树,红黑树,B-B+树,Trie树原理和应用
前言:本文章来源于我在知乎上回答的一个问题 AVL树,红黑树,B树,B+树,Trie树都分别应用在哪些现实场景中? 看完后您可能会了解到这些数据结构大致的原理及为什么用在这些场景,文章并不涉及具体操作 ...
随机推荐
- IntelliJ IDEA Community 无法启动 Spring Boot 项目的解决方案
菜单中依次选择 Run >Edit Configuration 在弹出窗口中,点击左上角的 +,选择 Maven 在 Name 中自定义一个名称,一般与项目名称相同 在 Run 下方的文本框中输 ...
- VulnHub-Jangow-01-1.0.1打靶记录
知识点 NMAP参数 -sV 获取系统信息 -sT TCP扫描可能会留下日志记录 -sC 使用默认脚本(在-A模式下不需要) -p1-xxx 扫描端口号 -p- ==>等价于 -p1-65535 ...
- python将日志生成到文件和控制台
# 日志收集设置import logging, osfrom logging.handlers import TimedRotatingFileHandlerimport datetimecurren ...
- ES6中对象新增了哪些扩展?
一.属性的简写 ES6中,当对象键名与对应值名相等的时候,可以进行简写 const baz = {foo:foo}// 等同于const baz = {foo} 方法也能够进行简写 const o = ...
- Kafka 线上性能调优
Kafka 线上性能调优是一项综合工程,不仅仅是 Kafka 本身,还应该从硬件(存储.网络.CPU)以及操作系统方面来整体考量,首先我们要有一套生产部署方案,基于这套方案再进行调优,这样就有了可靠的 ...
- 容器启动流程(containerd 和 runc)
启动流程 containerd 作为一个 api 服务,提供了一系列的接口供外部调用,比如创建容器.删除容器.创建镜像.删除镜像等等.使用 docker 和 ctr 等工具,都是通过调用 contai ...
- 牛客网-SQL专项训练10
①SQL语句中与Having子句同时使用的语句是:group by 解析: SQL语法中,having需要与group by联用,起到过滤group by后数据的作用. ②下列说法错误的是?C 解析: ...
- 干货|一文读懂阿里云数据库Autoscaling是如何工作的
简介: 阿里云数据库实现了其特有的Autosaling能力,该能力由数据库内核.管控及DAS(数据库自治服务)团队共同构建,内核及管控团队提供了数据库Autoscaling的基础能力,DAS则负责性能 ...
- AI和大数据结合,智能运维平台助力流利说提升核心竞争力
简介: 简介:本文整理自数智创新行--智能运维专场(上海站),流利说最佳实践演讲:<基于SLS千万级在线教育平台统一监控运营实践> 作者:孙文杰 流利说运维总监元乙 阿里云智能技术专家 优 ...
- 阿里云消息队列 RocketMQ 5.0 全新升级:消息、事件、流融合处理平台
简介: RocketMQ5.0 的发布标志着阿里云消息从消息领域正式迈向了"消息.事件.流"场景大融合的新局面.未来阿里云消息产品的演进也将继续围绕消息.事件.流核心场景而开展. ...