图说红黑树算法

1. 二叉树

对于一颗普通的二叉树来说,查找时候还是蛮有效率的,只要不是极端的情况。例如下面这种满二叉树来说,时间复杂度为O(logn)。
二叉树
但是呢,有些时候并不是那么如你所愿,例如,从10到1一直插入到一颗二叉树时,就会出现以下这种情况,查找的时候复杂度实在难受,O(n)
极端二叉树
然而对于这种情况的二叉树,每次插入的时候都只管找位置就坐,可能导致树的左右不平衡发展了,这,就是懒的结果了。那么,为了让一颗二叉树保持很好的查询效率,于是乎就产生了:会自我管理树的平衡性的二叉树,称为:红黑树(Red-Black tree)

2. 什么是红黑树

先来看看一颗标准的红黑树是怎么样的:
红黑树例子

  1. 节点只有两种颜色:红和黑;
  2. 根节点和空叶子节点(nil)都为黑
  3. 如果某个节点为红色,它的孩子结果都是黑色
  4. 任意一个节点到它的空叶子节点(nil)路径上包含相同数量的黑色节点,称之为黑色高度

### 需要额外注意的是:最长的路径(路上节点从黑开始黑红相隔)不能大于2倍的最短路径(路上节点全是黑)

3. 红黑树的操作

  1. 查找
  2. 插入
  3. 删除

对于插入和删除两个操作,有可能会破坏红黑树的平衡,使其不满足红黑树的所有属性。对于破坏平衡的情况,需要对节点进行调整来保持平衡,自我管理能力杠杠的。
那怎么调整呢?大体思路是:调整子树来改变树的结构和修改节点颜色两个主要操作来保持平衡。同时需要朝两个目标处理,

  1. 降低树的高度(以防出现一边倒的情况)
    • 保持最大高度为O(logn)
    • 大颗的子树向上移,小颗的子树向下挪
  2. 不影响树的有序性

修改节点颜色,主要是:红变黑,或黑变红。现在主要来看一下,如何调整子树结构。

3.1逆时针旋转(左旋转)和顺时针旋转(右旋转)

个人觉得,顺逆时针会比较好理解,文章以此来描述。旋转操作是指通过旋转某个节点,同时来调整子树的结构,牵一发而动子树的效果。举个逆时针的例子,如下图

把节点A按逆时针旋转,步骤是:

  1. 把节点A的右孩子,变成它的父节点(P’)
  2. 把P’的左孩子变成节点A的右孩子

解析:由于第1步操作完之后,P’可能存在3个子节点,不符合二叉树的性质,所以需要把其中一个过继到其它节点上;完成第二步之后,就可以把P’的子节点变成2个了
根据步骤把上面的树旋转一下(颜色可以先忽略,主要是讲旋转逻辑):
逆时针旋转
好了,逆时针的例子解析完了,对于顺时针应该是可以举一反三了,因为它们是对称的,操作的时候把节点的左换成右,右换成左即可。下面把原始图的结果列一下。
顺时针旋转

也许聪明的小伙伴可以看到,对于同一个子树,逆时顺和顺时针操作完之后,其实就变回去原来的结构了,可以仔细看看上面两个例子

3.2 插入操作

维护一颗红黑树性质不变,当插入或移除节点的时候,如果这个操作破坏了红黑树的属性,则需要把它调整一下。主要步骤有:

  1. 插入节点Node, 并且标记为红色(改不了字体颜色,算了)
  2. 把相关节点调整颜色和旋转节点来保持红黑树的属性。

敲黑板了,为什么要标红色?因为:标红只会破坏红黑树的两条属性。(往上翻,第2条和第3条)

当插入一个新的节点(Node)时,此时可能有以下4种情况:

  1. Node为根节点
  2. Node的叔叔为红色
  3. Node的叔叔为黑色(这种情况还细分为两种)
    1. Node的爷爷, 父亲,和Node自己本身都是一个分支方向继承下来的,称之为直线型关系。如下图:在这里插入图片描述
    2. Node的爷爷和父亲关系方向,与Node和Node父亲的关系方向不一样,称之为三角型关系。如下图:在这里插入图片描述

现在来详说一下对于以上情况每一种情况的调整步骤,统一定义新插入的节点为Node
Case 1: Node为根节点

  • 把Node从红色标为黑色即可,这是最简单的情况

Case 2: Node的叔叔为红色

  • 把祖上两辈的节点颜色取反上色,即把爷爷,父亲和叔叔节点取反上色。过程图如下:在这里插入图片描述
    Case 3: Node的叔叔为黑色(直接型关系)
  • 旋转爷爷节点到另一侧(根据当前状态选择顺逆时针)
  • 把Node的原始爷爷节点和父亲节点重新上色在这里插入图片描述
    Case 4:Node的叔叔为黑色(三色型关系)
  • 顺时针旋转Node的父节点在这里插入图片描述
    以上就是红黑树的基础内容与插入操作相关情况和步骤。下面我们来用例子演示一下。

4.例子

我们先从简单的开始吧。模拟插入数据【10, 5, 1】

  1. 在一颗空树上插入一个节点10(符合插入操作的Case 1的情况)插入10时
  2. 再插入一个节点5,符合正常红黑树,不需要处理插入5时
  3. 再插入一个节点1,破坏规则3(红色节点的子节点都是黑色),故需要调整。根据目前状态选择调整策略:节点1的叔叔节点为黑色的空节点,并且是直线型关系,所以采用Case 3处理过程。根据当前状态,顺时针旋转爷爷节点,然后再把原始的爷爷节点和父亲节点的颜色修改一下,于是可以恢复到正常的红黑树了。插入1时

这个就是简单的自我调整平衡的二叉树,亦即是红黑树。它的应用场景比较明确,就是有序的map会使用到它,例如C++的map,它是有序的,并且查找时间复杂度为O(logn)。篇幅有限,删除操作留待后面再补,因为实在太长了。感谢你的耐心阅读,希望能对你有所帮助。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章