红黑树和AVL树的实现与比较-----算法导论

一、问题描述

实现3种树中的两种:红黑树,AVL树,Treap树

二、算法原理

(1)红黑树

红黑树是一种二叉查找树,但在每个结点上增加一个存储位表示结点的颜色,可以是red或black。红黑树满足以下五个性质:

1) 每个结点或是红色或是黑色

2) 根结点是黑色

3) 每个叶结点是黑的

4)如果一个结点是红的,则它的两个儿子均是黑色

5) 每个结点到其子孙结点的所有路径上包含相同数目的黑色结点

本实验主要实现红黑树的初始化,插入和删除操作。当对红黑树进行插入和

删除操作时,可能会破坏红黑树的五个性质。为了保证红黑树的性质,需要进行修改颜色和指针结构等。指针结构的修改是通过旋转操作完成。因此这里首先描述红黑树的旋转操作。

A.旋转操作

旋转操作主要分为左旋和右旋操作。当在某个节点x上进行左旋操作时,假设x的右孩子y不是nil[T],左旋以x到y之间的链为轴进行,使得y成为该子树新的根,x成为y的左孩子,而y的左孩子则成为x的右孩子。右旋类似。

B.插入操作

红黑树的插入操作主要有两个步骤。

a) 首先以二叉查找树的方式插入一个红色结点;

b) 然后对新的二叉树进行修复,即进行颜色调整或旋转操作,以此满足红

黑树的五个性质。

在对红黑树进行插入修复操作时,循环进入条件是当前结点的父结点为红色

即性质4破坏。在修复函数中主要考虑以下三种情况:

情况1:插入结点z的父结点为红色,而z的叔叔结点y是红色

此时z的父结点的父结点一定存在,而且必为黑色结点,否则插入前即不是红黑树。这种情况的主要思路就是修改z的父结点和叔叔结点y的颜色为黑色,z的祖父结点的颜色修改为红色,目的就是红色上移,然后当前结点指针指向祖父结点,循环进行修复操作

情况2:z的父结点为红色,而z的叔叔结点y是黑色,而且z是右孩子

此时将当前节点的z的父结点设为新的当前节点,并以新当前节点为支点左旋。将情况2转化为情况3。

情况3:z的父结点为红色,而z的叔叔结点y是黑色,而且z是左孩子

此时将z的父节点修改为黑色,祖父节点变为红色,并以祖父节点为支点进行右旋操作。此时一行中不再有两个连续的红色结点,红黑树所有性质调整完成,因此所有的处理到此完毕。

C.删除操作

类似插入,红黑树的删除操作主要有两个步骤。

c)     首先以二叉查找树的方式删除一个指定结点;

d)     然后对新的二叉树进行修复,即进行颜色调整或旋转操作,以此满足红

黑树的五个性质。

在对红黑树进行删除修复操作时,循环进入条件是当前结点x为非根结点并

且是双重黑结点。在修复函数中主要考虑以下四种情况:

情况1:x的兄弟结点w是红色的

此时需把父结点修改成红色,把兄弟结点w修改成黑色。然后,针对父结点进行左旋操作,此时红黑树性质5不变。将情况1转化为其他情况。

情况2:x的兄弟w是黑色的,而且w的两个孩子都是黑色的

此时需从当前结点x和兄弟节点w去掉一重黑色,从而x只有一重黑色而w是红色。同时为了补偿去掉的一重黑色,需在x的父结点上增加一重黑色,并把x的父结点作为新的当前节点,重新进入循环。

情况3:x的兄弟w是黑色的,w的左孩子是红色的,右孩子是黑色的

此时需把兄弟结点w修改成红色,兄弟w的左孩子修改成黑色,然后以兄弟结点w作为支点进行右旋操作,之后重新进入算法。将情况5转化为情况6。

情况4:x的兄弟w是黑色的,而且w的右孩子是红色的

此时需将兄弟结点w修改成x的父结点的颜色,然后将x的父结点修改成黑色,兄弟结点w的右孩子修改成黑色,然后以x的父结点为支点进行左旋操作。此时算法结束,红黑树所有性质调整完成。

D.计算根结点到叶结点所有路径对应的黑高度

对红黑树进行插入或删除结点时,为了能够保持红黑树的性质会进行对应的修复操作。为了能更直观的测试经过该修复操作后红黑树的性质是否正确的得到满足,在本次实验的结果中将依次输出根结点到叶结点的所有路径并输出该路径上所有黑色结点的个数。因此需要实现一个输出路径并计算对应黑高度的方法。

该方法的主要原理就是利用递归思想,在函数内部维护一个记录路径结点的数组path[]。每访问一个结点时,首先判断该结点是否为哨兵结点。若为哨兵,则表示该路径已扫描结束。此时需把路径输出,并输出对应黑高度。若该结点不是哨兵结点,此时需把该结点关键字值记录到path[]数组中。同时如果该结点为黑色,则将表示黑高度的变量值加1.做完这些操作之后,则一次递归调用该结点的左右子树。实现该操作的主要代码如下:

E.打印树的结构

在对红黑树进行插入或删除操作之后,为了能够更加直观的观察当前红黑树的特点,本实验将在结果中输出当前红黑树的结构。实现该操作的主要原理就是利用树的结构特点,优先打印出右子树,然后打印根结点值和该结点的颜色,最后打印出左子树,在函数中通过空格来控制最后的显示效果。主要代码如下:

(2)AVL树

AVL树是一种高度平衡的二叉查找树。它或者是一颗空树,或者是具有下列性质的二叉树:它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1。在AVL树中的每个结点都有一个平衡因子bf,它表示这个结点的左、右子树的高度差,即左子树的高度减去右子树的高度。AVL树上所有结点的平衡因子只能是-1、0、1。

实现AVL树的关键在于维持树的平衡性。每当进行插入或删除操作时都有可能破坏了树的平衡性。因此首先检查是否破坏了树的平衡性,如果因插入结点而破坏了二叉查找树的平衡,则找出离插入点最近的不平衡结点,然后将该不平衡结点为根的子树进行旋转操作。旋转操作主要分为:LL型、RR型、LR型、RL型等。

A.旋转操作

a)     LL型

由于在A的左子树根结点B的左子树上插入结点,使A的平衡因子由1增至2而失去平衡。故需进行一次向右顺时针旋转操作。 即将A的左孩子B向右上旋转代替A作为根结点,A向右下旋转成为B的右子树的根结点。而原来B的右子树则变成A的左子树。

b)     RR型

由于在A的右子树根结点B的右子树上插入结点,使A的平衡因子由-1增至-2而失去平衡。故需进行一次向左顺时针旋转操作。

c)     LR型

由于在A的左子树根结点B的右子树上插入结点,使A的平衡因子由1增至2而失去平衡。故需进行两次旋转操作,先左旋再右旋。

d)     RL型

由于在A的右子树根结点B的左子树上插入结点,使A的平衡因子由-1增至-2而失去平衡。故需进行两次旋转操作,先左旋再右旋。

B.插入操作

在AVL树上插入一个新的数据元素e的递归算法可描述如下: 

(1)若BBST为空树,则插入一个数据元素为e的新结点作为BBST的根结点,树的深度增1;

(2)若e的关键字和BBST的根结点的关键字相等,则不进行;

(3)若e的关键字小于BBST的根结点的关键字,而且在BBST的左子树中不存在和e有相同关键字的结点,则将e插入在BBST的左子树上,并且当插入之后的左子树深度增加(+1)时,分别就下列不同情况处理之:

a、BBST的根结点的平衡因子为-1(右子树的深度大于左子树的深度,则将根结点的平衡因子更改为0,BBST的深度不变;

b、BBST的根结点的平衡因子为0(左、右子树的深度相等):则将根结点的平衡因子更改为1,BBST的深度增1;

c、BBST的根结点的平衡因子为1(左子树的深度大于右子树的深度):若BBST的左子树根结点的平衡因子为1:则需进行单向右旋平衡处理,并且在右旋处理之后,将根结点和其右子树根结点的平衡因子更改为0,树的深度不变;

(4)若e的关键字大于BBST的根结点的关键字,而且在BBST的右子树中不存在和e有相同关键字的结点,则将e插入在BBST的右子树上,并且当插入之后的右子树深度增加(+1)时,分别就不同情况处理之。

C.删除操作

假设被删结点x的父结点为y, 如果删除的是y的左子树, 则y的平衡因子bf减1, 否则y的平衡因子bf加1. 根据y的平衡因子的值分为以下三种情况:

情况一:

如果结点y的新的平衡因子bf等于0,则表示删除前y的平衡因子等于1 或者-1, 此时高度减1, 需改变父节点和其他祖父节点的平衡因子

情况二:

如果结点y的新的平衡因子bf等于1或者-1,则表示删除前y的平衡因子等于0, 左右子树高度一样, 此时高度不变, 需改变父节点和其他某些祖父节点的平衡因子.

情况三:

如果结点y新的平衡因子bf等于-2或2, 则表示删除前y的平衡因子1等于或-1, 左右子树高度不相同, 此时树在结点y是不平衡的, 需要做平衡化处理

三、实验数据

(1)输入:

红黑树实验和AVL树实验采用的数据均是随机生成的20个关键字值。然后依次将这些关键字利用插入算法插入到相应树中。在进行删除操作时,删除的数据是随机选取的3个关键字值,然后依次删除对应结点。

(2)输出:

对于红黑树,在进行插入和删除操作后,将输出对应树的结构,并标明结点的颜色(red或black),然后计算根结点到叶结点所有路径的黑高度并依次输出显示。

四、源代码

下载链接:http://download.csdn.net/detail/zhh1992/8359301

时间: 2024-10-13 14:41:44

红黑树和AVL树的实现与比较-----算法导论的相关文章

Linux内核之于红黑树and AVL树

为什么Linux早先使用AVL树而后来倾向于红黑树?       实际上这是由红黑树的实用主义特质导致的结果,本短文依然是形而上的观点.红黑树可以直接由2-3树导出,我们可以不再提红黑树,而只提2-3树,因为 2-3树的操作太简单.另外,任何红黑树的操作和特性都可以映射到2-3树中.因此红黑树和AVL树的比较就成了2-3树和AVL树的比较. 它们俩的区别在哪?2-3树的平衡是完美平衡的,但是树杈数量却可以是3个,而AVL树差一点点就完美平衡的标准二叉树,它只允许子树的高度差最多为1. 可见这么看

B树、B+树、红黑树、AVL树

定义及概念 B树 二叉树的深度较大,在查找时会造成I/O读写频繁,查询效率低下,所以引入了多叉树的结构,也就是B树.阶为M的B树具有以下性质: 1.根节点在不为叶子节点的情况下儿子数为 2 ~ M2.除根结点以外的非叶子结点的儿子数为 M/2(向上取整) ~ M3.拥有 K 个孩子的非叶子节点包含 k-1 个keys(关键字),且递增排列4.所有叶子结点在同一层,即深度相同 (叶节点可以看成是一种外部节点,不包含任何关键字信息) 在B-树中,每个结点中关键字从小到大排列,并且当该结点的孩子是非叶

【算法导论学习-26】 二叉树专题4:红黑树、AVL树、B-Tree

1.   红黑树(Red-Black Trees) 参考<算法导论>P308页,红黑树是一种对树的高度要求最灵活的准平衡二叉搜索树.五大属性: 1: Every node is either RED or BLACK. 2: The root is black. 3: Every leaf(NIL) is black.  (The NIL is the sentinel.) 4: If a node is RED, then both its children areblack. 5: For

红黑树和AVL树的比较

1. 红黑树并不追求"完全平衡"--它只要求部分地达到平衡要求,降低了对旋转的要求,从而提高了性能. 红黑树能够以O(log2 n) 的时间复杂度进行搜索.插入.删除操作.此外,由于它的设计,任何不平衡都会在三次旋转之内解决.当然,还有一些更好的,但实现起来更复杂的数据结构,能够做到一步旋转之内达到平衡,但红黑树能够给我们一个比较"便宜"的解决方案.红黑树的算法时间复杂度和AVL相同,但统计性能比AVL树更高. 当然,红黑树并不适应所有应用树的领域.如果数据基本上是

红黑树与AVL树

概述:本文从排序二叉树作为引子,讲解了红黑树,最后把红黑树和AVL树做了一个比较全面的对比. 1 排序二叉树 排序二叉树是一种特殊结构的二叉树,可以非常方便地对树中所有节点进行排序和检索. 排序二叉树要么是一棵空二叉树,要么是具有下列性质的二叉树: ? 若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值: ? 若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值: ? 它的左.右子树也分别为排序二叉树. 下图显示了一棵排序二叉树: 对排序二叉树,若按中序遍历就可以得到由小到大的

红黑树和AVL树的区别(转)

add by zhj: AVL树和红黑树都是平衡二叉树,虽然AVL树是最早发明的平衡二叉树,但直接把平衡二叉树等价于AVL树,我认为非常不合适. 但很多地方都在这么用.两者的比较如下 平衡二叉树类型 平衡度 调整频率 适用场景 AVL树 高 高 查询多,增/删少 红黑树 低 低 增/删频繁 原文:https://blog.csdn.net/u010899985/article/details/80981053 一,AVL树 (1)简介 一般用平衡因子判断是否平衡并通过旋转来实现平衡,左右子树树高

B树、B+树、红黑树、AVL树比较

B树是为了提高磁盘或外部存储设备查找效率而产生的一种多路平衡查找树. B+树为B树的变形结构,用于大多数数据库或文件系统的存储而设计. B树相对于红黑树的区别 在大规模数据存储的时候,红黑树往往出现由于树的深度过大而造成磁盘IO读写过于频繁,进而导致效率低下的情况.为什么会出现这样的情况,我们知道要获取磁盘上数据,必须先通过磁盘移动臂移动到数据所在的柱面,然后找到指定盘面,接着旋转盘面找到数据所在的磁道,最后对数据进行读写.磁盘IO代价主要花费在查找所需的柱面上,树的深度过大会造成磁盘IO频繁读

红黑树、B(+)树、跳表、AVL等数据结构,应用场景及分析,以及一些英文缩写

在网上学习了一些材料. 这一篇:https://www.zhihu.com/question/30527705 AVL树:最早的平衡二叉树之一.应用相对其他数据结构比较少.windows对进程地址空间的管理用到了AVL树 红黑树:平衡二叉树,广泛用在C++的STL中.map和set都是用红黑树实现的.我们熟悉的STL的map容器底层是RBtree,当然指的不是unordered_map,后者是hash. B/B+树用在磁盘文件组织 数据索引和数据库索引 Trie树 字典树,用在统计和排序大量字符

AVL树、红黑树以及B树介绍

简介 首先,说一下在数据结构中为什么要引入树这种结构,在我们上篇文章中介绍的数组与链表中,可以发现,数组适合查询这种静态操作(O(1)),不合适删除与插入这种动态操作(O(n)),而链表则是适合删除与插入,而查询效率则就比较慢了,本文要分享学习的树就是为了平衡这种静态操作与动态操作的差距. 一.二叉查找树 简介 满足下面条件就是二叉查找树 任意节点左子树不为空,则左子树的值均小于根节点的值. 任意节点右子树不为空,则右子树的值均大于于根节点的值. 任意节点的左右子树也分别是二叉查找树. 没有键值