原文:红黑树深入剖析及Java实现,本文修改了原文的一些小错误,如果想看红黑树的Java实现可以到原文去看。
红黑树是平衡二叉查找树的一种。为了深入理解红黑树,我们需要从二叉查找树开始讲起。

BST

二叉查找树(Binary Search Tree,简称BST)是一棵二叉树,它的左子节点的值比父节点的值要小,右节点的值要比父节点的值大。它的高度决定了它的查找效率。在理想的情况下,二叉查找树增删查改的时间复杂度为O(logN)(其中N为节点数),最坏的情况下为O(N)。当它的高度为logN+1时,我们就说二叉查找树是平衡的。

BST的查找操作

T  key = a search key
Node root = point to the root of a BSTwhile(true){if(root==null){break;}if(root.value.equals(key)){return root;}else if(key.compareTo(root.value)<0){root = root.left;}else{root = root.right;}
}
return null;

从程序中可以看出,当BST查找的时候,先与当前节点进行比较:

  • 如果相等的话就返回当前节点;
  • 如果小于当前节点则继续查找当前节点的左节点;
  • 如果大于当前节点则继续查找当前节点的右节点。

直到当前节点指针为空或者查找到对应的节点,程序查找结束。

BST的插入操作

Node node = create a new node with specify value
Node root = point the root node of a BST
Node parent = null;//find the parent node to append the new node
while(true){if(root==null)break;parent = root;if(node.value.compareTo(root.value)<=0){root = root.left;  }else{root = root.right;}
}
if(parent!=null){if(node.value.compareTo(parent.value)<=0){//append to leftparent.left = node;}else{//append to rightparent.right = node;}
}

插入操作先通过循环查找到待插入的节点的父节点,和查找父节点的逻辑一样,都是比大小,小的往左,大的往右。找到父节点后,对比父节点,小的就插入到父节点的左节点,大就插入到父节点的右节点上。

BST的删除操作

在二叉查找树中删除一个给定的结点p有三种情况:

  1. 结点p无左右孩子,则直接删除该结点,修改父节点相应指针。
  2. 结点p只有左孩子(右孩子),则把p的父节点和p的左孩子(右孩子)相连,然后删除p。
  3. 左右孩子同时存在,找到p的中序直接前驱s,也就是以p的左孩子为根结点的子树中最右的结点,把s的右孩子和p的右孩子相连,p的父节点和p的左孩子相连,然后删除p。
//从二叉查找树中删除指针p所指向的结点
if(p.right == null) //p的右子树为空
{  p = p.left;
}
else if(p.left == null) //p的左子树为空
{  p = p.right;
}
else //左右子树均不空
{  BSTNode s = p.left; //左孩子  while(s.right != null) //寻找结点p的中序前驱结点,                      {                   //也就是以s为根结点的子树中最右的结点   s = s.right;      }  s.right = p.right; //p的右孩子和s的右孩子相连   p = p.left; //p的左孩子和p的父节点相连
}  

RBTree

然而BST的问题是:数在插入的时候会导致树倾斜,不同的插入顺序会导致树的高度不一样,而树的高度直接的影响了树的查找效率。理想的高度是logN,最坏的情况是所有的节点都在一条斜线上,这样的树的高度为N。

基于BST存在的问题,一种新的树——平衡二叉查找树(Balanced BST)产生了。平衡树在插入和删除的时候,会通过旋转操作将高度保持在logN。其中两款具有代表性的平衡树分别为AVL树和红黑树。AVL树追求全局平衡,导致插入和删除性能差,在实际应用中不如追求局部平衡的红黑树(Red-Black Tree,简称RBTree),比如Linux内核中的完全公平调度器、高精度计时器、ext3文件系统等,各种语言的函数库如Java的TreeMap和TreeSet,C++ STL的map、multimap、multiset等,Java 8中的HashMap也用到了RBTree取代过长的链表。

RBTree的定义

RBTree的定义如下:

  1. 任何一个节点都有颜色,黑色或者红色;
  2. 根节点是黑色的;
  3. 父子节点之间不能出现两个连续的红节点;
  4. 任何一个节点向下遍历到其子孙的叶子节点,所经过的黑节点个数必须相等;
  5. 空节点被认为是黑色的。

数据结构表示如下:

class  Node<T>{public   T value;public   Node<T> parent;public   boolean isRed;public   Node<T> left;public   Node<T> right;
}

RBTree在理论上还是一棵BST树,但是它在对BST的插入和删除操作时会维持树的平衡,即保证树的高度在[logN,logN+1](极端的情况下可以出现RBTree的高度达到2*logN,但实际上很难遇到)。这样RBTree的查找时间复杂度始终保持在O(logN)从而接近于理想的BST。RBTree的删除和插入操作的时间复杂度也是O(logN)。
RBTree的查找操作就是BST的查找操作。

RBTree的插入操作

RBTree的插入与BST的插入方式是一致的,只不过是在插入过后,可能会导致树的不平衡,这时就需要对树进行旋转操作和颜色修复(在这里简称插入修复),使得它符合RBTree的定义。

新插入的节点是红色的,插入修复操作如果遇到父节点的颜色为黑则修复操作结束。也就是说,只有在父节点为红色节点的时候是需要插入修复操作的。

插入修复操作分为以下的三种情况

插入操作-case 1

case-1:新插入节点的叔叔节点为红色。

此时操作是将父节点和叔叔节点与祖父节点的颜色互换,这样就符合了RBTRee的定义。下图中,操作完成后A节点变成了新的节点。如果A节点的父节点不是黑色的话,则继续做修复操作

插入操作-case 2

case-2:新插入节点的叔叔节点为空,且祖父节点、父节点和新节点处于一条斜线上。

此时操作是将B节点进行右旋操作,并且和父节点A互换颜色。通过该修复操作RBTRee的高度和颜色都符合红黑树的定义。如果B和C节点都是右节点的话,只要将操作变成左旋就可以了。

插入操作-case 3

case-3:叔叔节点为空,且祖父节点、父节点和新节点不处于一条斜线上。

此时操作是将C节点进行左旋,这样就从case 3转换成case 2了,然后针对case 2进行操作处理就行了。case 2操作做了一个右旋操作和颜色互换来达到目的。如果树的结构是下图的镜像结构,则只需要将对应的左旋变成右旋,右旋变成左旋即可。

插入操作的总结

插入后的修复操作是一个向root节点回溯的操作,一旦牵涉的节点都符合了红黑树的定义,修复操作结束。之所以会向上回溯是由于case 1操作会将父节点,叔叔节点和祖父节点进行换颜色,有可能会导致祖父节点不平衡(红黑树定义3)。这个时候需要对祖父节点为起点进行调节,向上回溯,直到root节点为止,根据定义root节点永远是黑色的。

RBTree的删除操作

删除操作首先需要做的也是BST的删除操作,删除后就需要做删除修复操作,使的树符合红黑树的定义,符合定义的红黑树高度是平衡的。

删除修复操作是针对删除黑色节点才有的,当黑色节点被删除后会让整个树不符合RBTree的定义的第四条。需要做的处理是从兄弟节点上借调黑色的节点过来,如果兄弟节点没有黑节点可以借调的话,就只能往上追溯,将每一级的黑节点数减去一个,使得整棵树符合红黑树的定义。
删除修复操作在遇到被删除的节点是红色节点或者到达root节点时,修复操作完毕。

删除修复操作分为四种情况:

删除操作-case 1

case-1:待删除的节点的兄弟节点是红色的节点。

由于兄弟节点是红色节点的时候,无法借调黑节点,所以需要将兄弟节点上升到父节点,由于兄弟节点是红色的,根据RBTree的定义,兄弟节点的子节点是黑色的,就可以从它的子节点借调了。

case 1这样转换之后就会变成后面的case 2,case 3,或者case 4中的一种了。上升操作需要对兄弟节点做一个左旋或右旋操作,然后和父结点变换颜色,兄弟节点的左孩子旋转后作为它原本父结点的右孩子

删除操作-case 2

case-2:待删除的节点的兄弟节点是黑色的节点,且兄弟节点的子节点都是黑色的。

因为兄弟节点和兄弟节点的子节点都是黑色的,所以可以将兄弟节点变红,但因为兄弟节点的父结点可红可黑,这个时候需要将父节点A变成新的节点,继续向上调整,直到整颗树的颜色符合RBTree的定义为止。

删除操作-case 3

case-3:待删除节点的兄弟节点是黑色的节点,且兄弟节点的左子节点是红色的,右子节点是黑色的(这是兄弟节点在右边的情况),如果兄弟节点在左边的话,就是兄弟节点的右子节点是红色的,左子节点是黑色的。我理解是和待删除节点离得近的是红的。

此时操作是对这个红色的结点做旋转上升,然后变换它和它原本父结点的颜色,转换为case-4。

删除操作-case 4

case-4:待删除节点的兄弟节点是黑色的节点,且兄弟节点的右子节点是红色的(兄弟节点在右边的情况),如果兄弟节点在左边,就是左子节点是红色的。现在是离得远的是红的。

如下图A是待删除节点,此时操作是对兄弟节点D做旋转上升,变换D和父结点B的颜色,D的左孩子C现在变为B的右孩子,D的红色右孩子变为黑色。

删除操作的总结

红黑树的删除操作是最复杂的操作,复杂的地方就在于当删除了黑色节点的时候,如何从兄弟节点去借调黑节点,以保证树的颜色符合定义。由于红色的兄弟节点是没法借调出黑节点的,这样只能通过旋转操作让他上升到父节点,而由于它是红节点,所以它的子节点就是黑的,可以借调。

对于兄弟节点是黑色节点的可以分成3种情况来处理,第1种情况是:当兄弟节点的子节点都是黑色节点时,可以直接将兄弟节点变红,这样局部的红黑树颜色是符合定义的。但是整颗树不一定是符合红黑树定义的,需要往上追溯继续调整。

第2种情况是:兄弟节点的子节点为左红右黑。第3种情况是:兄弟节点的右子结点是红的(左子结点随意)。我们可以先将第2情况通过旋转变为第3种情况,这时兄弟节点为黑,兄弟节点的右节点为红,可以借调出两个节点出来做黑节点,这样就可以保证删除了黑节点,整棵树还是符合红黑树的定义的,因为黑色节点的个数没有改变。

红黑树算法原理(从二叉搜索树讲起)相关推荐

  1. 【算法导论】 二叉搜索树、AVL树、和红黑树

    二叉搜索树 二叉搜索树是一颗二叉树或一颗空树且满足以下性质: 1)根节点 x的key值大于任意左子树上节点的key值,小于右子树上任意节点的key值 : 2)其左右子树也分别是一颗二叉搜索树. 使用二 ...

  2. 算法导论习题—二叉搜索树、红黑树、区间树

    算法基础习题-二叉搜索树.红黑树.区间树 1.二叉搜索树: 2.红黑树: 3.区间树: 1.二叉搜索树: 设 T T T是一棵二叉搜索树,其关键字互不相同;设 x x x是一个叶结点, y y y为其 ...

  3. 数据结构与算法 整理笔记---二叉搜索树

    二叉搜索树 查找问题是计算机中非常重要的基础问题 二分查找法 对于有序数组,才能使用二分查找法(排序作用) public class BinarySearch {public static int b ...

  4. 九章基础算法04:二叉搜索树与哈希表

    目录 1. 什么是二叉搜索树 1.1 二叉搜索树结构 1.2 二叉搜索树特性应用 2. 二叉搜索树基础实现 2.1 BST类型与构造函数 2.2 插入操作 2.2.1 思路分析 2.2.2 递归实现 ...

  5. 08_Python算法+数据结构笔记-二叉搜索树查询/删除-AVL树旋转/插入/应用-贪心算法

    b站视频:路飞IT学城 清华计算机博士带你学习Python算法+数据结构_哔哩哔哩_bilibili #71 二叉搜索树:查询 import randomclass BiTreeNode:def __ ...

  6. 红黑树算法原理及实现

    红黑树 一.简介 红黑树(Red Black Tree)是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组. 红黑树是一种特化的AVL树(平衡二叉树),都是在进行插入 ...

  7. 算法:不同二叉搜索树||

    给定一个整数 n,生成所有由 1 ... n 为节点所组成的 二叉搜索树 . 示例: 输入:3 输出: [   [1,null,3,2],   [3,2,null,1],   [3,1,null,nu ...

  8. 算法:验证二叉搜索树

    给定一个二叉树,判断其是否是一个有效的二叉搜索树. 假设一个二叉搜索树具有如下特征: 节点的左子树只包含小于当前节点的数. 节点的右子树只包含大于当前节点的数. 所有左子树和右子树自身必须也是二叉搜索 ...

  9. 【数据结构与算法】3.二叉搜索树(BST)、c++代码

    二叉搜索树(BST).c++代码 参考 https://blog.csdn.net/c_living/article/details/81021510

最新文章

  1. Java 统计字母个数
  2. 【代数结构】群 ( 群的定义 | 群的基本性质 | 群的证明方法 | 交换群 )
  3. 为ASP.NET MVC扩展异步Action功能(下)
  4. 【学习笔记】30、Python基础综合练习
  5. 售前笔记(四)——呈现沟通(PPT交流)
  6. 发送临时文件被服务器拒绝,临时会话说服务器拒绝了您发送离线文件的请求 - 卡饭网...
  7. mysql+after+commit_Spring事务aftercommit原理及实践
  8. 理解 python 装饰器
  9. docker-compose.yml配置文件详解
  10. 你的袜子还是干的吗?
  11. 在mac上安装cgal4.11时,QT5 和qglviewer的设置
  12. reids学习笔记汇总
  13. [转载] QT中的connect用法总结
  14. 做减肥产品微商地推用什么做引流?如何选择转化率较高的地推方式
  15. Python函数的输出
  16. Agent XPs disable
  17. 2020-2021 ICPC - Gran Premio de Mexico - Repechaje
  18. word加上尾注之后参考文献下面的横线去除
  19. 专升本计算机和数学怎么备考,数学与应用数学专接本怎么复习
  20. Java基础学习(2)---Java基础语法

热门文章

  1. QT打开外部程序Windows Mac
  2. Linux命令之passwd命令
  3. 华为平板android最新版本号,MediaPad ICS正式版升级包发布 华为平板电脑率先进入Android 4.0时代...
  4. 26岁销售转岗程序员,薪资从3K—2W的绝地翻盘
  5. 老活新整——矩阵转置(C语言版矩阵转置)
  6. STM32睡眠模式低功耗(停止模式)
  7. linux egrep用法,grep,egrep及相应的正则表达式用法详解
  8. grep与egrep
  9. 怎样解决Java Web项目更改项目名后报错以及不能找到web路径问题
  10. 我放弃 Axios,改用 Alova