红黑树的简介论文
排序二叉树和红黑树

排序⼆叉树和红⿊树排序⼆叉树:排序⼆叉树是⼀种特殊结构的⼆叉树,通过它可以⾮常⽅便的对树中所有节点进⾏排序和检索。
排序⼆叉树要么是⼀棵空⼆叉树,要么是具有以下性质的⼆叉树:若它的左⼦树不空,则左⼦树上所有节点的值均⼩于它的根节点的值若它的右⼦树不空,则右⼦树上所有节点的值均⼤于它的根节点的值它的左、右⼦树也分别为排序⼆叉树对于排序⼆叉树,⽤中序遍历就可以得到由⼩到⼤的有序序列。
创建排序⼆叉树的步骤,就是不断地向排序⼆叉树添加节点的过程,具体如下:1、拿根节点为当前节点开始搜索2、拿新节点的值和当前节点的值⽐较3、如果新节点的值更⼤,则以当前节点的右⼦节点作为新的当前节点;如果新节点的值更⼩,则以当前节点的左⼦节点作为新的当前节点4、重复2、3步骤,知道搜索到合适的叶⼦节点5、将新节点添加为第4步找到的叶⼦节点的⼦节点,如果新节点值更⼤,则添加为右⼦节点,否则,加为左⼦节点当程序从排序⼆叉树中删除⼀个节点之后,为了让它依然保持为排序⼆叉树,就必须对该排序⼆叉树进⾏维护,维护可分为如下⼏种情况:1、被删除的节点时叶⼦节点,只需将它从其⽗节点中删除2、被删除节点p只有左⼦树,将p的左⼦树pL添加成p的⽗节点的左⼦树即可,被删除节点p只有右⼦树,将p的右⼦树pR添加成p的⽗节点的右⼦树即可3、若被删除节点p的左、右⼦树均为空,有⼀下两种做法:看图描述,⽤⽂字真绕。
敲代码不打注释敲得眼花。
package com.test_one;import java.util.ArrayDeque;import java.util.ArrayList;import java.util.List;import java.util.Queue;/*** Created by Administrator on 2017/3/13.*/public class SortedBinTree<T extends Comparable> {static class Node{Object data;Node parent;Node left;Node right;public Node(Object data , Node parent , Node left , Node right){this.data = data;this.parent = parent;this.left = left;this.right = right;}public String toString(){return "[data=" + data + "]";}public boolean equals(Object obj){if(this == obj){return true;}if(obj.getClass() == Node.class){Node target = (Node) obj;return data.equals(target.data) && left == target.left && right == target.right && parent == target.parent; }return false;}}private Node root;public SortedBinTree(){root = null;}public SortedBinTree(T o){root = new Node(o , null , null , null);}public void add(T ele){if(root == null){root = new Node(ele , null , null , null);}else{Node current = root;Node parent = null;int cmp = 0;do{parent = current;cmp = pareTo(current.data);if(cmp > 0){current = current.right;}else{current = current.left;}}while(current != null);Node newNode = new Node(ele , parent , null , null);if(cmp > 0){parent.right = newNode;}else{parent.left = newNode;}}}public void remove(T ele){Node target = getNode(ele);if(target == null){return;}if(target.left == null && target.right == null){if(target == root){root = null;}else{if(target == target.parent.left){target.parent.left = null;}else{target.parent.right = null;}target.parent = null;}}else if(target.left == null && target.right != null){ if(target == root){root = target.right;}else{if(target == target.parent.left){target.parent.left = target.right;}else{target.parent.right = target.right;}target.right.parent = target.parent;}}else if(target.left != null && target.right == null){ if(target == root){root = target.left;}else{if(target == target.parent.left){target.parent.left = target.left;}else{target.parent.right = target.left;}target.left.parent = target.parent;}}else{Node leftMaxNode = target.left;while(leftMaxNode.right != null){leftMaxNode = leftMaxNode.right;}leftMaxNode.parent.right = null;leftMaxNode.parent = target.parent;if(target == target.parent.left){target.parent.left = leftMaxNode;}else{target.parent.right = leftMaxNode;}leftMaxNode.left = target.left;leftMaxNode.right = target.right;target.parent = target.left = target.right = null; }}public Node getNode(T ele){Node p = root;while(p != null){Object obj = new Object();int cmp = pareTo(p.data);if(cmp < 0){p = p.left;}else if(cmp > 0){p = p.right;}else{return p;}}return null;}public List<Node> breadthFirst(){Queue<Node> queue = new ArrayDeque<Node>(); List<Node> list = new ArrayList<Node>();if(root != null){queue.offer(root);}while(!queue.isEmpty()){list.add(queue.peek());Node p = queue.poll();if(p.left != null){queue.offer(p.left);}if(p.right != null){queue.offer(p.right);}}return list;}}package com.test_one;/*** Created by Administrator on 2017/3/13.*/public class SortedBinTreeTest {public static void main(String[] args){SortedBinTree<Integer> tree = new SortedBinTree<Integer>();tree.add(50);tree.add(20);tree.add(10);tree.add(3);tree.add(8);tree.add(15);tree.add(30);System.out.println(tree.breadthFirst());tree.remove(20);System.out.println(tree.breadthFirst());}}View Code红⿊树排序⼆叉树虽然可以快速检索,但是在最坏的情况下,如果插⼊的节点本⾝就是有序的,最后得到的排序⼆叉树将变成链表,其检索效率就会很差。
【红黑树】的详细实现(C++)

>若pu是黑色结点,再插入红色结点,则特性没有破坏,结束重新平衡的过程。
>若pu是红色结点,则出现连续两个红色结点的情形,这时还要考查pu的兄弟结点。
情况1:如果pu的兄弟结点gr是红色结点,此时结点pu的父结点gu是黑色结点,它有两个红色子女结点。交换结点gu和它的子女结点的颜色。
62
{
63
root = s;
64
root->parent = pr;
65
}
66
else //如果父节点不是根节点
67
{
68
if (value < pr->key)
69
{
70
pr->left = s;
71
}
72
else
73
{
74
pr->right = s;
75
}
76
s->parent = pr; //设置新结点s的父节点
136
{
137
y->left->parent = z;
138
}
139
y->parent = z->parent;
140
if (root == z) //z就是根节点
性,结束重新平衡过程。
>当结点u是pu的右子女的情形与u是pu的左子女的情形是镜像的,只要左右指针互换即可。
红黑树的删除
红黑树的删除算法与二又搜索树的删除算法类似,不同之处在于,在红黑树中执行一次二叉搜索树的删除运算,可能会破坏红黑树的特 性,需要重新平衡。 在红黑树中真正删除的结点应是叶结点或只有一个子女的结点。若设被删除为p,其唯一的子女为s。结点p被删除后,结点s取代了它的 位置。 如果被删结点p是红色的,删去它不存在问题。因为树中各结点的黑高度都没有改变,也不会出现连续两个红色结点,红黑树的特性仍然保 持,不需执行重新平衡过程。 如果被删结点p是黑色的,一旦删去它,红黑树将不满足特性的要求,因为在这条路径上黑色结点少了一个,从根到外部结点的黑高度将会 降低。因此必须通过旋转变换和改变结点的颜色,消除双重黑色结点,恢复红黑树的特性。
如果红黑树的所有结点都是黑色,那么他一定是一棵满二叉树

如果红黑树的所有结点都是黑色,那么他一定是一棵
满二叉树
红黑树的原理是通过进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而实现关联数组,存储有序的数据。
它是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,其典型的用途就是实现关联数组。
扩展资料:
一、简单介绍:
红黑树是一种特定类型的二叉树,它是在计算机科学中用来组织数据比如数字的块的一种结构。
若一棵二叉查找树是红黑树,则它的任一子树必为红黑树。
而由于每一颗红黑树都是一颗二叉排序树,因此,在对红黑树进行查找时,可以采用运用于普通二叉排序树上的查找算法,在查找过程中不需要颜色信息。
二、行为特征:
红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。
在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
性质1、节点是红色或黑色。
性质2、根节点是黑色。
性质3、所有叶子都是黑色。
(叶子是NUIL节点)
性质4、每个红色节点的两个子节点都是黑色。
(从每个叶子到根的所有路径上不能有两个连续的'红色节点)
性质5、从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
三、红黑树和AVL树:
红黑树和AVL树的区别在于它使用颜色来标识结点的高度,它所追求的是局部平衡而不是AVL树中的非常严格的平衡。
学过数据结构的人应该都已经领教过AVL树的复杂,但AVL树的复杂比起红黑树来说简直是小巫见大巫,红黑树才是真正的变态级数据结构。
linux内核之红黑树

Linux内核之红黑树作者:harvey wang邮箱:harvey.perfect@新浪博客地址:/harveyperfect,有关于减肥和学习英语相关的博文,欢迎交流最近在学习linux内核的主要模块,遇到了红黑树数据结构,在经过了很长时间的一头雾水后,终于明白一点了。
把自己的体会写下来和大家分享,同时欢迎指正。
1、初识红黑树从网上搜索了许多红黑树的介绍,这些文章中主要介绍了红黑树的性质,然后就是红黑树的旋转如下示意图。
左旋、右旋,旋转过程中爸爸变成了儿子,兄弟变成了孙子;红的变成黑的,黑的变成红的。
经过一系列的旋转,就把我旋转的晕头转向了,脑子里搅成了一团浆糊。
相信,没有学过二叉树的同学肯定会遇到和我一样窘况。
2、曙光乍现一天,顶着一头圈圈的我(是头晕的圈圈,可不是天使头上美丽的光环,呵呵)看到了一棵用数字表示的红黑树,如下尝试着用旋转规则进行旋转,惊奇的发现,按照无论怎么旋转,结果都是使得从任何一个节点开始,左下方的数比本节点数值小,右下方的数比本节点大。
我立刻想到了这种数据结构的好处,查找一个数非常方便(有点像二分法,只是不是中分)。
另外,从资料上看到,红黑树放松了平衡二叉树的某些要求,插入和删除操作中的平衡处理只发生在局部,据有资料介绍,最多经过3次左右旋转就可以达到平衡,插入和删除的效率非常高。
因为红黑树也是二叉查找树,因此红黑树上的查找操作与普通二叉查找树上的查找操作相同,以O(log2 n)的时间复杂度进行搜索。
红黑树真是太神奇了,发明人真是天才!!!在此向发明人致敬!3、linux内核模块中对红黑树的应用如果只是为了理解linux内核进程调度和内存管理中的红黑树应用的话,只要记住下面即可A、红黑树是一个二叉搜索树(从任何一个节点开始,左下方的数比本节点数值小,右下方的数比本节点大)。
B、插入过程中按照key值大小插入到树中适当的位置。
C、搜索、插入和删除效率很高。
至于如何调整平衡,该旋转哪个节点、左旋还是右旋、该如何改变颜色,那是研究算法人员的事(就是这些让我头晕晕的,呵呵),我们只要知道搜索、插入和删除等函数的名称和用法就可以了,呵呵在linux进程调度中,准确地说是非实时进程的CFS调度中用到了红黑树,每个进程都有一个虚拟运行时间,进程运行一段时间后其虚拟运行时间会增加,优先级高的进程的虚拟时间增加的慢,如优先级高的进程运行10ms后,虚拟运行时间增加1vms(vms 为虚拟毫秒,我自己给他定义的,为了区别真是的时间),优先级低的进程运行10ms后,虚拟运行时间增加1000vms。
例题红黑树的插入

红黑树是一种自平衡的二叉搜索树,用于解决普通二叉搜索树在插入和删除操作时可能出现的不平衡问题。
下面是红黑树插入的基本步骤:
1.插入节点:首先,将新节点作为叶子节点插入到红黑树中,按照普通二叉搜索树的
插入规则进行插入。
2.重新着色和旋转:插入节点后,为了保持红黑树的特性,需要进行重新着色和旋转
操作。
a. 如果插入节点的父节点是黑色,那么不需要进行任何操作,红黑树的性质没有被破坏。
b. 如果插入节点的父节点是红色,需要进行调整以满足红黑树的性质。
i. 如果插入节点的叔叔节点也是红色,那么将插入节点的父节点和叔叔节点都改为黑色,将插入节点的祖父节点改为红色,并以祖父节点为当前节点进行进一步调整。
ii. 如果插入节点的叔叔节点是黑色或者缺少叔叔节点,需要进行旋转操作来恢复平衡。
- 如果插入节点是其父节点的右子节点,且父节点是祖父节点的左子节点,那么进行左旋操作。
- 如果插入节点是其父节点的左子节点,且父节点是祖父节点的右子节点,那么进行右旋操作。
- 如果插入节点是其父节点的左子节点,且父节点是祖父节点的左子节点,那么进行右旋操作,并交换父节点和祖父节点的颜色。
- 如果插入节点是其父节点的右子节点,且父节点是祖父节点的右子节点,那么进行左旋操作,并交换父节点和祖父节点的颜色。
3.根节点处理:最后,将根节点设置为黑色,以满足红黑树的性质。
这些步骤确保了插入后的红黑树仍然保持平衡和满足红黑树的性质。
需要注意的是,以上步骤只是插入操作的基本流程,具体的实现可能会有一些细微的差别。
红黑树在不同场景的应用及其优势

红⿊树在不同场景的应⽤及其优势红⿊树(平衡的排序⼆叉树),满⾜以下性质:1)每个结点要么是红的,要么是⿊的。
2)根结点是⿊的。
3)每个叶结点,即空结点(NIL)是⿊的。
4)如果⼀个结点是红的,那么它的俩个⼉⼦都是⿊的。
5)对每个结点,从该结点到其⼦孙结点的所有路径上包含相同数⽬的⿊结点根据性质5,我们得出:从根到叶⼦的最长的可能路径不多于最短的可能路径的两倍长红⿊树的关键性质: 内部保证有序,旋转开销⼩,整体相对平衡红⿊树的应⽤:1、java8 hashmap中链表转红⿊树。
优势:时间复杂度从O(n)-->O(logn) ,且⾃旋开销较其他树较低(不⽤整体平衡)。
2、epoll在内核中的实现,⽤红⿊树管理事件块(⽂件描述符)。
优势:1、因为内核态需要维护⼀个长久存放fd的数据结构,⽽fd变动⼗分频繁,且需要⽀持快速查询,且所以红⿊树很适合。
2、红⿊树可以判断是否是重复的fd-----其实我感觉⽤hash表也可以3、Java的TreeMap实现相对与hashMap优势,内部key保持有序,且⽀持⾃定义排序⽐较器。
适⽤场景,对数据需要排序统计4、linux进程调度Completely Fair Scheduler,⽤红⿊树管理进程控制块CFS 背后的主要想法是维护为任务提供处理器时间⽅⾯的平衡(公平性)。
这意味着应给进程分配相当数量的处理器任务存储在以时间为顺序的红⿊树中(由sched_entity对象表⽰),对处理器需求最多的任务(最低虚拟执⾏时vruntime)存储在树的左側,处理器需求最少的任务(最⾼虚拟执⾏时)存储在树的右側。
为了公平。
调度器然后选取红⿊树最左端的节点调度为下⼀个以便保持公平性。
任务通过将其执⾏时间加⼊到虚拟执⾏时,说明其占⽤ CPU 的时间,然后假设可执⾏。
再插回到树中。
这样,树左側的任务就被给予时间执⾏了,树的内容从右側迁移到左側以保持公平。
因此,每⼀个可执⾏的任务都会追赶其它任务以维持整个可执⾏任务集合的执⾏平衡。
构树解说词
构树解说词
尊敬的观众朋友们,今天我将向大家介绍构树。
构树是一种用于数据存储和检索的非平衡树结构,也被称为红黑树或AVL 树。
首先,让我们来了解一下平衡树的概念。
平衡树是指高度不平衡的树结构,在进行插入和删除操作时容易导致树的高度增加,影响树的性能。
而构树通过旋转、平移等方式,在插入和删除操作时动态调整树结构,使得树的高度保持在一个较小的范围内,从而得到更好的性能。
与平衡树不同的是,构树利用颜色标记的方式来区分节点的状态。
通常将节点分为红色节点和黑色节点,其中黑色节点代表平衡节点,红色节点代表需要调整的节点。
通过对红色节点的旋转、平移等操作,可以使树结构变得更加平衡。
在构树中,还有一个重要的操作——双旋转。
双旋转包括左旋和右旋这两种操作。
左旋是指将节点向左旋转一定角度,右旋则是将节点向右旋转一定角度。
这些操作可以使得树的结构更加平衡,提高树的检索效率。
总之,构树是一种非常优秀的数据存储和检索结构,通过动态调整树结构,使得树的高度保持在合理的范围内,从而得到更好的性能。
如果您在数据存储和检索方面遇到了困难,我相信这种数据结构一定会帮助您实现目标。
感谢您的聆听!。
红黑树面试最简洁的回答方式
红黑树面试最简洁的回答方式
红黑树是一种用来维护具有唯一性和相对次序关系的数据集合的树形结构。
它是一种特殊的二叉搜索树,其特征是每个结点都有一个标签(红色或黑色),这些标签对提高红黑树的性能起到关键作用。
一棵红黑树由一个特定顺序的结点(比如根结点)及其相连的子树组成,这些结点通过指向比较器来进行排序。
红黑树的优势在于它的搜索,插入和删除的时间复杂度都为O(logN),而且构造整棵树的时间复杂度也为O(logN),这种性能是其他数据结构所无法比拟的。
另外,它还具有占用空间合理,自我调整,和向任何节点插入和更新数据的能力,使其成为JavaScript和Python中非常流行的数据结构之一。
红黑树不仅在学术界受到推崇,而且在计算机实践中也能发挥极大作用,其应用范围也很广泛,它不仅可以应用于数据库存储、内部数据结构,也可以应用于查找,排序,网络路由,内存管理等。
从这些例子中可以看出,红黑树对计算机应用程序来说是至关重要的,所以它在面试中是一个非常重要的话题。
总之,红黑树是一种出色的数据结构,既有优秀的性能又有极佳的扩展性,它的应用可以说是无处不在。
因此,学习和掌握它的知识回报也是颇丰的,且在面试中可以极大地凸显自身的潜力。
java treeset原理
java treeset原理Java中的TreeSet是一个基于红黑树实现的有序集合,具有自动排序和去重功能。
它是Set接口的实现类之一,继承自AbstractSet 类,实现了NavigableSet接口。
本文将深入介绍Java TreeSet的原理,包括红黑树、TreeSet的实现、遍历方式和常用操作等内容。
1. 红黑树红黑树是一种自平衡二叉查找树,它的每个节点都有一个颜色属性,可以是红色或黑色。
这种树具有以下特性:1. 每个节点要么是黑色,要么是红色。
2. 根节点是黑色。
3. 每个叶子节点(NIL节点,空节点)是黑色。
4. 如果一个节点是红色,那么它的子节点必须是黑色。
5. 从任意一个节点到其每个叶子节点的所有路径都包含相同数目的黑色节点。
这些特性确保了红黑树的平衡性和搜索效率。
插入、删除节点时,需要对树进行旋转和改变颜色等操作,以维持平衡。
2. TreeSet的实现Java TreeSet是基于红黑树实现的有序集合,它的元素按照自然顺序或者指定的比较器顺序进行排序。
TreeSet中的元素必须是可比较的,即实现了Comparable接口或者传入了Comparator比较器。
TreeSet的底层数据结构是红黑树,它的节点类是TreeMap中的Entry类,包含三个属性:key、value和color。
TreeSet的实现主要涉及以下几个方法:1. add(E e):向TreeSet中添加元素e。
首先通过比较器或者自然顺序找到要插入的位置,然后将元素包装成Entry对象,插入红黑树中,最后进行平衡操作。
2. remove(Object o):从TreeSet中删除元素o。
首先通过比较器或者自然顺序找到要删除的节点,然后进行删除操作,最后进行平衡操作。
3. clear():清空TreeSet中的所有元素。
4. iterator():返回一个迭代器,按照自然顺序或者指定的比较器顺序遍历元素。
5. size():返回TreeSet中元素的个数。
基于红黑树的操作与检验
找 和删 除( 1 o g n中 的 n是树 中元 素 的数 目) 。它 的统 计 性能 优于 平衡二 又树 ( AVI 一树 ) , 所 以红黑 树 应 用
很广 , 如字典 的实 现 、 关 联数组 上 , 且 内存 使用率 较 高 。
的每个 节点 都有 颜 色 属性 , 颜 色为 红 色或 黑 色 。除 了
红黑 树在 最坏情 况 下都是 高效 的 。
1 优 点 和 用 途
虽然 h a s h表 查 找 非 常迅 速 , 但 随 着 数 据 种 类 变
右旋 : 可 由左 旋类 推之 。
树旋转 后 , 保 持不 变 的唯有 原树 的搜 索性 质 , 而不
能保 持原树 的红黑性 质 , 红 黑树 的数据 插入 、 删 除后 可
流程 图如 图 2和 图 3 : 2 . 2 . 1 . 1 情况 1 ( 如图 2 ) : z的叔 叔 Y为红 色 。 ( a ) 情
坏, 不是 性质 2 就 是性 质 4 。若 违 反性 质 2 , 则发 生 原 因是 Z 为根 且为 红色 。若违 反性 质 4 , 则原 因为 z 和P
况, 即z 是右孩子, 因p E p E z ] ] ( c ) 为黑 色 , 所 以将 r - p ] [ z ] 、 Y都涂 成黑 色 , 这 就解 决 了 Z 、 p E Z ] 皆为 红 色 的 问 题, 将p i p [ z ] ] 涂 为红 色 , 则 维持 了性质 5 . ( b ) 情 况 同
多, h a s h表长 会 变 得 更 长且 冲 突也 会 增 多 , 那 么 如何 能 实现数 据量 无论 多大 , 查 找 依 然 高性 能 ?一 般 情 况 下, 树 是很好 的一 种 数据 结 构 , 用于插入、 查 找 和删 除 等操作 都很 高效 , 但 在 很 坏 的情 况 下 , 操 作很 费时 间 , 性 能 难 以保 证 。 向树 中插 入时 , 按一定 规则 调整 , 使其
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1. 红黑树的简介 红黑树是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。它是在1972年由Rudolf Bayer发明的,他称之为"对称二叉B树",它现代的名字是在 Leo J. Guibas 和 Robert Sedgewick 于1978年写的一篇论文中获得的。它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。 红黑树(Red-Black Tree)是二叉搜索树(Binary Search Tree)的一种改进。我们知道二叉搜索树在最坏的情况下可能会变成一个链表(当所有节点按从小到大的顺序依次插入后)。而红黑树在每一次插入或删除节点 之后都会花O(log N)的时间来对树的结构作修改,以保持树的平衡。也就是说,红黑树的查找方法与二叉搜索树完全一样;插入和删除节点的的方法前半部分节与二叉搜索树完全一 样,而后半部分添加了一些修改树的结构的操作。
2. 红黑树的性质
红黑树的每个节点上的属性除了有一个key、3个指针:parent、lchild、rchild以外,还多了一个属性:color。它只能是两种颜色:红或黑。而红黑树除了具有二叉搜索树的所有性质之外,还具有以下4点性质: 根节点是黑色的。 空节点是黑色的(红黑树中,根节点的parent以及所有叶节点lchild、rchild都不指向NULL,而是指向一个定义好的空节点)。 红色节点的父、左子、右子节点都是黑色。 在任何一棵子树中,每一条从根节点向下走到空节点的路径上包含的黑色节点数量都相同。
3. 红黑树的使用
如下图就是一棵红黑树:
3-0-1 红黑树 根据红黑树的性质,就可以保证整棵树的平衡,也就等于保证了搜索的时间为O(log N)。 但是在插入、删除节点后,就有可能破坏了红黑树的性质。所以我们要做一些操作来把整棵树修补好: 首先,是节点的Left-Rotate和Right-Rotate操作。所谓Left-Rotate(x)就是把节点x 向左下方 向移动一格,然后让x原来的右子节点代替它的位置。而Right-Rotate当然就是把Left-Rotate左、右互反一下。如下图: 3-0-2 节点LeftLeft-Rotate和Right-Rotate操作 注意:Left-Rotate(x)后,x的右子树变成了原来y的左子树,Right-Rotate反之。这样一次变换后,仍然满足二叉搜索树的性质。在红黑树的插入、删除中,要用到很多Left-Rotate和Right-Rotate操作。 3.1 插入 插入首先是按部就班二叉搜索树的插入步骤,把新节点z插入到某一个叶节点的位置上。 接下来把z的颜色设成红色。根据红黑树的性质,从根节点向下到空节点的每一条路径上的黑色节点数要相同。如果新插入的是黑色节点,那么它所在 的路径上就多出了一个黑色的节点了。所以新插入的节点一定要设成红色。但是这样可能又有一个矛盾:如果z的父节点也是红色,怎么办,前面说过红色节点的子 节点必须是黑色。因此我们要执行下面一个迭代的过程,称为Insert-Fixup,来修补这棵红黑树。 在Insert-Fixup中,每一次迭代的开始,指针z一定都指向一个红色的节点。如果z->parent是黑色,那我们就大功 告成了;如果z->parent是红色,显然这就违返了红黑的树性质,那么我们要想办法把z或者z->parent变成黑色,但这要建立在不 破坏红黑树的其他性质的基础上。 这里再引入两个指针:grandfather,指向z->parent->parent,也就是z的爷爷(显然由于 z->parent为红色,grandfather一定是黑色);uncle,指向grandfather除了z->parent之外的另一 个子节点,也就是z的父亲的兄弟,所以叫uncle。 (我们这里都假设z->parent是grandfather的左子节点,而uncle是grandfather的右子节点。如果遇到的实际情况不是这样,那也只要把所有操作中的左、右互反就可以了。) 在每一次迭代中,我们可能遇到以下三种情况。 Case 1. uncle也是红色。这时只要把z->parent和uncle都设成黑色,并把grandfather设成红色。这样仍然确保了每一条路径上的黑色节点数不变。然后把z指向grandfather,并开始新一轮的迭代。如下图:
3-1-1 插入时uncle是红色的情况 Case 2. uncle是黑色,并且z是z->parent的右子节点。这时我们只要把z指向z->parent,然后做一次Left-Rotate(z)。就可以把情况转化成Case 3。 Case 3. uncle是黑色,并且z是z->parent的左子节点。到了这一步,我们就剩最后一步了。只要把z->parent设成黑色,把 grandfather设成红色,再做一次Right-Rotate(grandfather),整棵树就修补完毕了。可以思考一下,这样一次操作之后, 确实满足了所有红黑树的性质。Case 2和Case 3如下图:
3-1-2 插入时uncle是黑色的情况 反复进行迭代,直到某一次迭代开始时z->parent为黑色而告终,也就是当遇到Case 3后,做完它而告终。 3.2 删除 二叉搜索树的删除节点z的过程: 如果z没有子节点,那么直接删除即可; 如果z只有一个子节点,那么让这个子节点来代替z的 位置,然 后把z删除即可; 如果z有两个子节点,那么找到z在中序遍历中的后继节点s(也就是从z->rchild开始向左下方一直走到底的那一个节点),把 s的key赋值给z的key,然后删除s。 红黑树中删除一个节点z的方法也是首先按部就班以上的过程。 如果删除的节点是黑色的,那么显然它所在的路径上就少一个黑色节点,那么红黑树的性质就被破坏了。这时我们就要执行一个称为Delete-Fixup的过程,来修补这棵树: 一个节点被删除之后,一定有一个它的子节点代替了它的位置(即使是叶节点被删除后,也会有一个空节点来代替它的位置。前面说过,在红黑树中,空节点是一个实际存在的节点。)。我们就设指针x指向这个代替位置的节点。 (1)如果x是红色的,那么我们只要把它设成黑色,它所在的路径上就重新多出了一个黑色节点,那么红黑树的性质就满足了。 (2)如果x是黑色的,那我们就要假想x上背负了2个单位的黑色。那么红黑树的性质也同样不破坏,但是我们要找到某一个红色的节点,把x上“超载”的这1个单位的黑色丢给它,这样才算完成。Delete-Fixup做的就是这个工作。 Delete-Fixup同样是一个循环迭代的过程。每一次迭代开始时: 如果指针x指向一个红色节点,那么大功告成,把它设成黑色即告终。 如果x黑色,那么我们就会面对以下4种情况: 这里引入另一个指针w,指向x的兄弟。这里我们都默认x是x->parent的左子节点,则w是x->parent的右子节点。(如果实际遇到相反的情况,只要把所有操作中的左、右互反一下就可以了。) Case 1. w是红色。这时我们根据红黑树的性质可以肯定x->parent是黑色、w->lchild是黑色。我们把x->parent与w的颜 色互换,然后做一次Left-Rotate(x->parent)。做完之后x就有了一个新的兄弟:原w->lchild,前面说过它一定是 黑色的。那么我们就在不破坏红黑树性质的前提下,把Case 1转换成了Case2、3、4中的一个,也就是w是黑色的情况。如下图:
3-2-1 w是红色 Case 2. w是黑色,并且w的两个子节点都是黑色。这时我们只要把w设成红色。然后把x移到x->parent,开始下一轮迭代(注意,那“超载”的1单位的 黑色始终是跟着指针x走的,直到x走到了一个红色节点上才能把它“卸下”)。这一次操作不会破坏红黑树的性质。这如下图(图中节点B不一定是红 色,也可能是黑色):
3-2-2 w是黑色 Case 3. w是黑色,并且w的右子节点也是黑色。这时我们把w与w->lchild的颜色互换,然后做Right-Rotate(w)。思考一下,这样做之后 不会破坏红黑树的性质。这时x的新的兄弟就是原w->lchild。而Case 3被转化成了Case 4。
3-2-3 w是黑色 Case 4. w是黑色,并且w的右子节点是红色。一但遇到Case 4,就胜利在望了。我看下面一张图。先把w与x->parent的颜色互换,再做Left-Rotate(x->parent)。这时图中节 点E(也就是原w->rchild)所在的路径就肯定少了一个黑色,而x所在的路径则多了一个黑色。那么我们就把x上多余的1个单位的黑色丢给E就 可以了。至此,Delete-Fixup就顺利完成了。
3-2-4 w是黑色 数据项只能存储在内部结点中(internal node)。我们所指的"叶结点"在其父结点中可能仅仅用一个NULL 指针表示,但是将它也看作一个实际的结点有助于描述红黑树的插入与删除算法,叶结点一律为黑色。 定理:一棵拥有n个内部结点的红黑树的树高h<=2log(n+1) (译者:我认为原文中的有关上述定理的证明是错误的,下面的证明方法是参考CLRS中的证明写出的。) 证明:首先定义一颗红黑树的黑高度Bh为:从这颗红黑树的根结点(但不包括这个根结点)到叶结点的路 径上包含的黑色结点(注意,包括叶结点)数量。另外规定叶结点的黑高度为0。 下面我们首先证明一颗有n个内部结点的红黑树满足n>=2^Bh-1。这可以用数学归纳法证明,施归纳于 树高h。当h=0时,这相当于是一个叶结点,黑高度Bh为0,而内部结点数量n为0,此时0>=2^0-1 成立。假设树高h<=t时,n>=2^Bh-1成立,我们记一颗树高为t+1的红黑树的根结点的左子树的内部 结点数量为nl,右子树的内部结点数量为nr,记这两颗子树的黑高度为Bh'(注意这两颗子树的黑高度必然 一样),显然这两颗子树的树高<=t,于是有nl>=2^Bh'-1以及nr>=2^Bh'-1,将这两个不等式相加 有nl+nr>=2^(Bh'+1)-2,将该不等式左右加1,得到n>=2^(Bh'+1)-1,很显然Bh'+1>=Bh,于是 前面的不等式可以变为n>=2^Bh-1,这样就证明了一颗有n个内部结点的红黑树满足n>=2^Bh-1。 下面我们完成剩余部分的证明,记红黑树树高为h。我们先证明Bh>=h/2。在任何一条从根结点到叶结点 的路径上(不包括根结点,但包括叶结点),假设其结点数量为m,注意其包含的黑色结点数量即为Bh。 当m为偶数时,根据性质5可以看出每一对儿相邻的结点至多有一个红色结点,所以有Bh>=m/2;而当 m为奇数时,这条路径上除去叶结点后有偶数个结点,于是这些结点中的黑色结点数B'满足B'>=(m-1)/2, 将该不等式前后加1得出Bh>=(m+1)/2,可以进一步得出Bh>m/2,综合m为偶数的情况可以得出 Bh>=m/2,而m在最大的情况下等于树高h,因此可以证明Bh>=h/2。将Bh>=h/2代入n>=2^Bh-1, 最终得到h<=2log(n+1)。证明完毕。 本文余下的内容将阐释如何在不破坏红黑树性