fbpx
维基百科

红黑树

红黑树(英語:Red–black tree)是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型用途是实现关联数组。它在1972年由鲁道夫·贝尔发明,被称为「对称二叉B树」,它现代的名字源于利奧尼達斯·J·吉巴斯罗伯特·塞奇威克于1978年写的一篇论文。红黑树的结构复杂,但它的操作有着良好的最坏情况运行时间,并且在实践中高效:它可以在时间内完成查找、插入和删除,这里的是树中元素的数目。

歷史 编辑

1972年,鲁道夫·拜尔發明了一種資料結構,那是一個B樹的四階特殊情況。此结构会維持所有從根到樹葉的路径都具有相同數量的節點,創造了一個完美的平衡樹。然而,他們並不是二元搜尋樹。拜爾在他的論文中稱呼他們為「對稱二元B樹」(Symmetric binary B-tree),後來他們以2-3-4樹或是2-4樹的形式開始流行。

在一篇1972年的論文〈A Dichromatic Framework for Balanced Trees〉中,利奧尼達斯·J·吉巴斯罗伯特·塞奇威克從對稱二元B樹+中推導出了紅黑樹。之所以選擇「紅色」是因為這是作者在帕羅奧多研究中心公司(Xerox PARC)工作時用彩色雷射列印機可以產生的最好看的顏色。另一種說法來自吉巴斯,是因為紅色和黑色的筆是他們當時可用來繪製樹的顏色。

用途和好处 编辑

红黑树和AVL树一样都对插入时间、删除时间和查找时间提供了最好可能的最坏情况担保。这不只是使它们在时间敏感的应用,如实时应用中有价值,而且使它们有在提供最坏情况担保的其他数据结构中作为基础模板的价值;例如,在计算几何中使用的很多数据结构都可以基于红黑树实现。

红黑树在函数式编程中也特别有用,在这里它们是最常用的持久数据结构之一,它们用来构造关联数组集合,每次插入、删除之后它们能保持为以前的版本。除了 的时间之外,红黑树的持久版本对每次插入或删除需要 的空间。

红黑树是2-3-4树的一种等同。换句话说,对于每个2-3-4树,都存在至少一个数据元素是同样次序的红黑树。在2-3-4树上的插入和删除操作也等同于在红黑树中颜色翻转和旋转。这使得2-3-4树成为理解红黑树背后的逻辑的重要工具,这也是很多介绍算法的教科书在红黑树之前介绍2-3-4树的原因,尽管2-3-4树在实践中不经常使用。

红黑树相对于AVL树来说,牺牲了部分平衡性以换取插入和删除操作时少量的旋转操作,整体来说性能要优于AVL树。

性质 编辑

红黑树是每个节点都带有颜色属性的二叉查找树,颜色为红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:

  1. 节点是红色或黑色。
  2. 根是黑色。
  3. 所有叶子都是黑色(叶子是NIL节点)。
  4. 每个红色节点必须有两个黑色的子节点。(或者说从每个叶子到根的所有路径上不能有两个连续的红色节点。)(或者说不存在两个相邻的红色节点,相邻指两个节点是父子关系。)(或者说红色节点的父节点和子节点均是黑色的。)
  5. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。

下面是一个具体的红黑树的图例:

 
An example of a red-black tree

这些约束确保了红黑树的关键特性:从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果是这个树大致上是平衡的。因为操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树

要知道为什么这些性质确保了这个结果,注意到性质4导致了路径不能有两个毗连的红色节点就足够了。最短的可能路径都是黑色节点,最长的可能路径有交替的红色和黑色节点。因为根据性质5所有最长的路径都有相同数目的黑色节点,这就表明了没有路径能多于任何其他路径的两倍长。

在很多树数据结构的表示中,一个节点有可能只有一个子节点,而叶子节点包含数据。用这种范例表示红黑树是可能的,但是这会改变一些性质并使算法复杂。为此,本文中我们使用「nil叶子」,如上图所示,它不包含数据而只充当树在此结束的指示。这些节点在绘图中经常被省略,导致了这些树好像同上述原则相矛盾,而实际上不是这样。与此有关的结论是所有节点都有两个子节点,尽管其中的一个或两个可能是空叶子。

操作 编辑

因为每一个红黑树也是一个特化的二叉查找树,因此红黑树上的只读操作与普通二叉查找树上的只读操作相同。然而,在红黑树上进行插入操作和删除操作会导致不再符合红黑树的性质。恢复红黑树的性质需要少量( )的颜色变更(实际是非常快速的)和不超过三次树旋转(对于插入操作是两次)。虽然插入和删除很复杂,但操作时间仍可以保持为 次。

插入 编辑

我们首先以二叉查找树的方法增加节点并标记它为红色。(如果设为黑色,就会导致根到叶子的路径上有一条路上,多一个额外的黑节点,这个是很难调整的。但是设为红色节点后,可能会导致出现两个连续红色节点的冲突,那么可以通过颜色调换(color flips)和树旋转来调整。)下面要进行什么操作取决于其他临近节点的颜色。同人类的家族树中一样,我们将使用术语叔父节点来指一个节点的父节点的兄弟节点。注意:

  • 性质1和性质3总是保持着。
  • 性质4只在增加红色节点、重绘黑色节点为红色,或做旋转时受到威胁。
  • 性质5只在增加黑色节点、重绘红色节点为黑色,或做旋转时受到威胁。

在下面的示意图中,将要插入的节点标为N,N的父节点标为P,N的祖父节点标为G,N的叔父节点标为U。在图中展示的任何颜色要么是由它所处情形这些所作的假定,要么是假定所暗含(imply)的。

对于每一种情形,我们将使用C示例代码来展示。通过下列函数,可以找到一个节点的叔父和祖父节点:

 node* grandparent(node *n){  return n->parent->parent;  }  node* uncle(node *n){  if(n->parent == grandparent(n)->left)  return grandparent (n)->right;  else  return grandparent (n)->left;  } 

情形1:新节点N位于树的根上,没有父节点。在这种情形下,我们把它重绘为黑色以满足性质2。因为它在每个路径上对黑节点数目增加一,性质5符合。

 void insert_case1(node *n){  if(n->parent == NULL)  n->color = BLACK;  else  insert_case2 (n);  } 

情形2:新节点的父节点P是黑色,所以性质4没有失效(新节点是红色的)。在这种情形下,树仍是有效的。性质5也未受到威胁,尽管新节点N有两个黑色叶子子节点;但由于新节点N是红色,通过它的每个子节点的路径就都有同通过它所取代的黑色的叶子的路径同样数目的黑色节点,所以依然满足这个性质。

 void insert_case2(node *n){  if(n->parent->color == BLACK)  return; /* 树仍旧有效*/  else  insert_case3 (n);  } 

注意:在下列情形下我们假定新节点的父节点为红色,所以它有祖父节点;因为如果父节点是根节点,那父节点就应当是黑色。所以新节点总有一个叔父节点,尽管在情形4和5下它可能是叶子节点。

 
情形3示意图

情形3:如果父节点P和叔父节点U二者都是红色,(此时新插入节点N做为P的左子节点或右子节点都属于情形3,这里右图仅显示N做为P左子的情形)则我们可以将它们两个重绘为黑色并重绘祖父节点G为红色(用来保持性质5)。现在我们的新节点N有了一个黑色的父节点P。因为通过父节点P或叔父节点U的任何路径都必定通过祖父节点G,在这些路径上的黑节点数目没有改变。但是,红色的祖父节点G可能是根节点,这就违反了性质2,也有可能祖父节点G的父节点是红色的,这就违反了性质4。为了解决这个问题,我们在祖父节点G上递归地进行情形1的整个过程。(把G当成是新加入的节点进行各种情形的检查)

 void insert_case3(node *n){  if(uncle(n) != NULL && uncle (n)->color == RED) {  n->parent->color = BLACK;  uncle (n)->color = BLACK;  grandparent (n)->color = RED;  insert_case1(grandparent(n));  }  else  insert_case4 (n);  } 

注意:在余下的情形下,我们假定父节点P是其祖父G的左子节点。如果它是右子节点,情形4和情形5中的应当对调。

 
情形4示意图

情形4:父节点P是红色而叔父节点U是黑色或缺少,并且新节点N是其父节点P的右子节点而父节点P又是其父节点的左子节点。在这种情形下,我们进行一次左旋转调换新节点和其父节点的角色;接着,我们按情形5处理以前的父节点P以解决仍然失效的性质4。注意这个改变会导致某些路径通过它们以前不通过的新节点N(比如图中1号叶子节点)或不通过节点P(比如图中3号叶子节点),但由于这两个节点都是红色的,所以性质5仍有效。

 void insert_case4(node *n){  if(n == n->parent->right && n->parent == grandparent(n)->left) {  rotate_left(n);  n = n->left;  } else if(n == n->parent->left && n->parent == grandparent(n)->right) {  rotate_right(n);  n = n->right;  }  insert_case5 (n);  } 
 
情形5示意图

情形5:父节点P是红色而叔父节点U是黑色或缺少,新节点N是其父节点的左子节点,而父节点P又是其父节点G的左子节点。在这种情形下,我们进行针对祖父节点G的一次右旋转;在旋转产生的树中,以前的父节点P现在是新节点N和以前的祖父节点G的父节点。我们知道以前的祖父节点G是黑色,否则父节点P就不可能是红色(如果P和G都是紅色就違反了性質4,所以G必須是黑色)。我们切换以前的父节点P和祖父节点G的颜色,结果的树满足性质4。性质5也仍然保持满足,因为通过这三个节点中任何一个的所有路径以前都通过祖父节点G,现在它们都通过以前的父节点P。在各自的情形下,这都是三个节点中唯一的黑色节点。

 void insert_case5(node *n){  n->parent->color = BLACK;  grandparent (n)->color = RED;  if(n == n->parent->left && n->parent == grandparent(n)->left) {  rotate_right(n->parent);  } else {  /* Here, n == n->parent->right && n->parent == grandparent (n)->right */  rotate_left(n->parent);  }  } 

注意插入实际上是原地算法,因为上述所有调用都使用了尾部递归

删除 编辑

如果需要删除的节点有两个儿子,那么问题可以被转化成删除另一个只有一个儿子的节点的问题(为了表述方便,这里所指的儿子,为非叶子节点的儿子)。对于二叉查找树,在删除带有两个非叶子儿子的节点的时候,我们要么找到它左子树中的最大元素、要么找到它右子树中的最小元素,并把它的值转移到要删除的节点中(如在这里所展示的那样)。我们接着删除我们从中复制出值的那个节点,它必定有少于两个非叶子的儿子。因为只是复制了一个值(没有复制颜色),不违反任何性质,这就把问题简化为如何删除最多有一个儿子的节点的问题。它不关心这个节点是最初要删除的节点还是我们从中复制出值的那个节点。

在本文余下的部分中,我们只需要讨论删除只有一个儿子的节点(如果它两个儿子都为空,即均为叶子,我们任意将其中一个看作它的儿子)。如果我们删除一个红色节点(此时该节点的儿子将都为叶子节点),它的父亲和儿子一定是黑色的。所以我们可以简单的用它的黑色儿子替换它,并不会破坏性质3和性质4。通过被删除节点的所有路径只是少了一个红色节点,这样可以继续保证性质5。另一种简单情况是在被删除节点是黑色而它的儿子是红色的时候。如果只是去除这个黑色节点,用它的红色儿子顶替上来的话,会破坏性质5,但是如果我们重绘它的儿子为黑色,则曾经通过它的所有路径将通过它的黑色儿子,这样可以继续保持性质5。

需要进一步讨论的是在要删除的节点和它的儿子二者都是黑色的时候,这是一种复杂的情况(这种情况下该节点的两个儿子都是叶子节点,否则若其中一个儿子是黑色非叶子节点,另一个儿子是叶子节点,那么从该节点通过非叶子节点儿子的路径上的黑色节点数最小为2,而从该节点到另一个叶子节点儿子的路径上的黑色节点数为1,违反了性质5)。我们首先把要删除的节点替换为它的儿子。出于方便,称呼这个儿子为N(在新的位置上),称呼它的兄弟(它父亲的另一个儿子)为S。在下面的示意图中,我们还是使用P称呼N的父亲,SL称呼S的左儿子,SR称呼S的右儿子。我们将使用下述函数找到兄弟节点:

struct node * sibling(struct node *n) {  if(n == n->parent->left)  return n->parent->right;  else  return n->parent->left; } 

我们可以使用下列代码进行上述的概要步骤,这里的函数replace_node替换childn在树中的位置。出于方便,在本章节中的代码将假定空叶子被用不是NULL的实际节点对象来表示(在插入章节中的代码可以同任何一种表示一起工作)。

void delete_one_child(struct node *n) {  /*  * Precondition: n has at most one non-null child.  */  struct node *child = is_leaf(n->right)? n->left : n->right;    replace_node(n, child);  if(n->color == BLACK){  if(child->color == RED)  child->color = BLACK;  else  delete_case1 (child);  }  free (n); } 

如果N和它初始的父亲是黑色,则删除它的父亲导致通过N的路径都比不通过它的路径少了一个黑色节点。因为这违反了性质5,树需要被重新平衡。有几种情形需要考虑:

情形1: N是新的根。在这种情形下,我们就做完了。我们从所有路径去除了一个黑色节点,而新根是黑色的,所以性质都保持着。

void delete_case1(struct node *n) {  if(n->parent != NULL)  delete_case2 (n); } 

注意:在情形2、5和6下,我们假定N是它父亲的左儿子。如果它是右儿子,则在这些情形下的应当对调。

 
情形2示意图

情形2: S是红色。在这种情形下我们在N的父亲上做左旋转,把红色兄弟转换成N的祖父,我们接着对调N的父亲和祖父的颜色。完成这两个操作后,尽管所有路径上黑色节点的数目没有改变,但现在N有了一个黑色的兄弟和一个红色的父亲(它的新兄弟是黑色因为它是红色S的一个儿子),所以我们可以接下去按情形4情形5情形6来处理。

(注意:这里的图中没有显示出来,N是删除了黑色节点后替换上来的子节点,所以这个过程中由P->X->N变成了P->N,实际上是少了一个黑色节点,也可以理解为Parent(Black)和Silbing(Red)那么他们的孩子黑色节点的数目肯定不等,让他们做新兄弟肯定是不平衡的,还需后面继续处理。这里看英文版本的[1] (页面存档备份,存于互联网档案馆)比较的明了)

void delete_case2(struct node *n) {  struct node *s = sibling (n);    if(s->color == RED){  n->parent->color = RED;  s->color = BLACK;  if(n == n->parent->left)  rotate_left(n->parent);  else  rotate_right(n->parent);  }   delete_case3 (n); } 
 
情形3示意图

情形3: N的父亲、S和S的儿子都是黑色的。在这种情形下,我们简单的重绘S为红色。结果是通过S的所有路径,它们就是以前通过N的那些路径,都少了一个黑色节点。因为删除N的初始的父亲使通过N的所有路径少了一个黑色节点,这使事情都平衡了起来。但是,通过P的所有路径现在比不通过P的路径少了一个黑色节点,所以仍然违反性质5。要修正这个问题,我们要从情形1开始,在P上做重新平衡处理。

void delete_case3(struct node *n) {  struct node *s = sibling (n);    if((n->parent->color == BLACK)&& (s->color == BLACK)&& (s->left->color == BLACK)&& (s->right->color == BLACK)) {  s->color = RED;  delete_case1(n->parent);  } else  delete_case4 (n); } 
 
情形4示意图

情形4: S和S的儿子都是黑色,但是N的父亲是红色。在这种情形下,我们简单的交换N的兄弟和父亲的颜色。这不影响不通过N的路径的黑色节点的数目,但是它在通过N的路径上对黑色节点数目增加了一,添补了在这些路径上删除的黑色节点。

void delete_case4(struct node *n) {  struct node *s = sibling (n);    if(n->parent->color == RED)&& (s->color == BLACK)&& (s->left->color == BLACK)&& (s->right->color == BLACK)) {  s->color = RED;  n->parent->color = BLACK;  } else  delete_case5 (n); } 
 
情形5示意图

情形5: S是黑色,S的左儿子是红色,S的右儿子是黑色,而N是它父亲的左儿子。在这种情形下我们在S上做右旋转,这样S的左儿子成为S的父亲和N的新兄弟。我们接着交换S和它的新父亲的颜色。所有路径仍有同样数目的黑色节点,但是现在N有了一个黑色兄弟,他的右儿子是红色的,所以我们进入了情形6。N和它的父亲都不受这个变换的影响。

void delete_case5(struct node *n) {  struct node *s = sibling (n);    ifs->color == BLACK){ /* this if statement is trivial,  due to Case 2(even though Case two changed the sibling to a sibling's child,  the sibling's child can't be red, since no red parent can have a red child). */ // the following statements just force the red to be on the left of the left of the parent,  // or right of the right, so case six will rotate correctly.  if((n == n->parent->left)&& (s->right->color == BLACK)&& (s->left->color == RED)) { // this last test is trivial too due to cases 2-4.  s->color = RED;  s->left->color = BLACK;  rotate_right (s);  } else if((n == n->parent->right)&& (s->left->color == BLACK)&& (s->right->color == RED)) {// this last test is trivial too due to cases 2-4.  s->color = RED;  s->right->color = BLACK;  rotate_left (s);  }  }  delete_case6 (n); } 
 
情形6示意图

情形6: S是黑色,S的右儿子是红色,而N是它父亲的左儿子。在这种情形下我们在N的父亲上做左旋转,这样S成为N的父亲(P)和S的右儿子的父亲。我们接着交换N的父亲和S的颜色,并使S的右儿子为黑色。子树在它的根上的仍是同样的颜色,所以性质3没有被违反。但是,N现在增加了一个黑色祖先:要么N的父亲变成黑色,要么它是黑色而S被增加为一个黑色祖父。所以,通过N的路径都增加了一个黑色节点。

此时,如果一个路径不通过N,则有两种可能性:

  • 它通过N的新兄弟。那么它以前和现在都必定通过S和N的父亲,而它们只是交换了颜色。所以路径保持了同样数目的黑色节点。
  • 它通过N的新叔父,S的右儿子。那么它以前通过S、S的父亲和S的右儿子,但是现在只通过S,它被假定为它以前的父亲的颜色,和S的右儿子,它被从红色改变为黑色。合成效果是这个路径通过了同样数目的黑色节点。

在任何情况下,在这些路径上的黑色节点数目都没有改变。所以我们恢复了性质4。在示意图中的白色节点可以是红色或黑色,但是在变换前后都必须指定相同的颜色。

void delete_case6(struct node *n) {  struct node *s = sibling (n);    s->color = n->parent->color;  n->parent->color = BLACK;    if(n == n->parent->left){  s->right->color = BLACK;  rotate_left(n->parent);  } else {  s->left->color = BLACK;  rotate_right(n->parent);  } } 

同样的,函数调用都使用了尾部递归,所以算法是原地算法。此外,在旋转之后不再做递归调用,所以进行了恒定数目(最多3次)的旋转。

C++示例代码 编辑

#define NIL NULL #define BLACK 1 #define RED 0 #include <iostream> using namespace std; class bst { private:  struct Node {  int value;  bool color;  Node *leftTree, *rightTree, *parent;  Node() : value(0), color(RED), leftTree(NULL), rightTree(NULL), parent(NULL) { }   Node* grandparent() {  if(parent == NULL){  return NULL;  }  return parent->parent;  }  Node* uncle() {  if(grandparent() == NULL) {  return NULL;  }  if(parent == grandparent()->rightTree)  return grandparent()->leftTree;  else  return grandparent()->rightTree;  }  Node* sibling() {  if(parent->leftTree == this)  return parent->rightTree;  else  return parent->leftTree;  }  };  void rotate_right(Node *p){  Node *gp = p->grandparent();  Node *fa = p->parent;  Node *y = p->rightTree;  fa->leftTree = y;  if(y != NIL)  y->parent = fa;  p->rightTree = fa;  fa->parent = p;  if(root == fa)  root = p;  p->parent = gp;  if(gp != NULL){  if(gp->leftTree == fa)  gp->leftTree = p;  else  gp->rightTree = p;  }  }  void rotate_left(Node *p){  if(p->parent == NULL){  root = p;  return;  }  Node *gp = p->grandparent();  Node *fa = p->parent;  Node *y = p->leftTree;  fa->rightTree = y;  if(y != NIL)  y->parent = fa;  p->leftTree = fa;  fa->parent = p;  if(root == fa)  root = p;  p->parent = gp;  if(gp != NULL){  if(gp->leftTree == fa)  gp->leftTree = p;  else  gp->rightTree = p;  }  }  void inorder(Node *p){  if(p == NIL)  return;  if(p->leftTree)  inorder(p->leftTree);  cout << p->value << " ";    if(p->rightTree)  inorder(p->rightTree);  }  string outputColor (bool color) {  return color ? "BLACK" : "RED";  }  Node* getSmallestChild(Node *p){  if(p->leftTree == NIL)  return p;  return getSmallestChild(p->leftTree);  }  bool delete_child(Node *p, int data){  if(p->value > data){  if(p->leftTree == NIL){  return false;  }  return delete_child(p->leftTree, data);  } else if(p->value < data){  if(p->rightTree == NIL){  return false;  }  return delete_child(p->rightTree, data);  } else if(p->value == data){  if(p->rightTree == NIL){  delete_one_child (p);  return true;  }  Node *smallest = getSmallestChild(p->rightTree);  swap(p->value, smallest->value);  delete_one_child (smallest);  return true;  }else{  return false;  }  }  void delete_one_child(Node *p){  Node *child = p->leftTree == NIL ? p->rightTree : p->leftTree;  if(p->parent == NULL && p->leftTree == NIL && p->rightTree == NIL){  p = NULL;  root = p;  return;  }    if(p->parent == NULL){  delete p;  child->parent = NULL;  root = child;  root->color = BLACK;  return;  }    if(p->parent->leftTree == p){  p->parent->leftTree = child;  } else {  p->parent->rightTree = child;  }  child->parent = p->parent;  if(p->color == BLACK){  if(child->color == RED){  child->color = BLACK;  } else  delete_case (child);  }  delete p;  }  void delete_case(Node *p){  if(p->parent == NULL){  p->color = BLACK;  return;  }  if(p->sibling()->color == RED) {  p->parent->color = RED;  p->sibling()->color = BLACK;  if(p == p->parent->leftTree)  //rotate_left(p->sibling());  rotate_left(p->parent);  else  //rotate_right(p->sibling());  rotate_right(p->parent);  }  if(p->parent->color == BLACK && p->sibling()->color == BLACK  && p->sibling()->leftTree->color == BLACK && p->sibling()->rightTree->color == BLACK) {  p->sibling()->color = RED;  delete_case(p->parent);  } else if(p->parent->color == RED && p->sibling()->color == BLACK  && p->sibling()->leftTree->color == BLACK && p->sibling()->rightTree->color == BLACK) {  p->sibling()->color = RED;  p->parent->color = BLACK;  } else {  if(p->sibling()->color == BLACK) {  if(p == p->parent->leftTree && p->sibling()->leftTree->color == RED  && p->sibling()->rightTree->color == BLACK) {  p->sibling()->color = RED;  p->sibling()->leftTree->color = BLACK;  rotate_right(p->sibling()->leftTree);  } else if(p == p->parent->rightTree && p->sibling()->leftTree->color == BLACK  && p->sibling()->rightTree->color == RED) {  p->sibling()->color = RED;  p->sibling()->rightTree->color = BLACK;  rotate_left(p->sibling()->rightTree);  }  }  p->sibling()->color = p->parent->color;  p->parent->color = BLACK;  if(p == p->parent->leftTree){  p->sibling()->rightTree->color = BLACK;  rotate_left(p->sibling());  } else {  p->sibling()->leftTree->color = BLACK;  rotate_right(p->sibling());  }  }  }  void insert(Node *p, int data){  if(p->value >= data){  if(p->leftTree != NIL)  insert(p->leftTree, data);  else {  Node *tmp = new Node();  tmp->value = data;  tmp->leftTree = tmp->rightTree = NIL;  tmp->parent = p;  p->leftTree = tmp;  insert_case (tmp);  }  } else {  if(p->rightTree != NIL)  insert(p->rightTree, data);  else {  Node *tmp = new Node();  tmp->value = data;  tmp->leftTree = tmp->rightTree = NIL;  tmp->parent = p;  p->rightTree = tmp;  insert_case (tmp);  }  }  }  void insert_case(Node *p){  if(p->parent == NULL){  root = p;  p->color = BLACK;  return;  }  if(p->parent->color == RED){  if(p->uncle()->color == RED) {  p->parent->color = p->uncle()->color = BLACK;  p->grandparent()->color = RED;  insert_case(p->grandparent());  } else {  if(p->parent->rightTree == p && p->grandparent()->leftTree == p->parent) {  rotate_left(p);  p->color = BLACK;  p->parent->color = RED;  rotate_right(p);  } else if(p->parent->leftTree == p && p->grandparent()->rightTree == p->parent) {  rotate_right(p);  p->color = BLACK;  p->parent->color = RED;  rotate_left(p);  } else if(p->parent->leftTree == p && p->grandparent()->leftTree == p->parent) {  p->parent->color = BLACK;  p->grandparent()->color = RED;  rotate_right(p->parent);  } else if(p->parent->rightTree == p && p->grandparent()->rightTree == p->parent) {  p->parent->color = BLACK;  p->grandparent()->color = RED;  rotate_left(p->parent);  }  }  }  }  void DeleteTree(Node *p){  if(!p || p == NIL){  return;  }  DeleteTree(p->leftTree);  DeleteTree(p->rightTree);  delete p;  } public:  bst() {  NIL = new Node();  NIL->color = BLACK;  root = NULL;  }  ~bst() {  if (root)  DeleteTree (root);  delete NIL;  }  void inorder() {  if(root == NULL)  return;  inorder (root);  cout << endl;  }  void insert (int x) {  if(root == NULL){  root = new Node();  root->color = BLACK;  root->leftTree = root->rightTree = NIL;  root->value = x;  } else {  insert(root, x);  }  }  bool delete_value (int data) {  return delete_child(root, data);  } private:  Node *root, *NIL; }; 

渐近边界的证明 编辑

包含n个内部节点的红黑树的高度是 

定义

  •  表示以节点 为根的子树的高度。
  •  表示从 到子树中任何叶子的黑色节点的数目(如果 是黑色则不计数它,也叫做黑色高度)。

引理:以节点 为根的子树有至少 个内部节点。

引理的证明(通过归纳高度):

基础: 

如果 的高度是零则它必定是NIL,因此 。所以:

 

归纳假设:   个内部节点暗示了h( ) = k+1的  个内部节点。

因为 有h( )> 0所以它是个内部节点。同样的它有黑色高度要么是bh( )要么是bh( )-1(依据 是红色还是黑色)的两个儿子。通过归纳假设每个儿子都有至少 个内部接点,所以 有:

 

个内部节点。

使用这个引理我们现在可以展示出树的高度是对数性的。因为在从根到叶子的任何路径上至少有一半的节点是黑色(根据红黑树性质4),根的黑色高度至少是 。通过引理我们得到:

 

因此根的高度是 

参见 编辑

引用 编辑

  • Mathworld: Red-Black Tree (页面存档备份,存于互联网档案馆
  • San Diego State University: CS 660: Red-Black tree notes (页面存档备份,存于互联网档案馆), by Roger Whitney
  • Cormen, Leiserson, Rivest, Stein. Introduction to Algorithms. Massachusetts: The MIT Press, 2002. pp273-77. ISBN 0-07-013151-1

外部链接 编辑

  • An applet + quick explanation (页面存档备份,存于互联网档案馆
  • Red/Black Tree Demonstration (页面存档备份,存于互联网档案馆
  • (animated GIF, 200KB)
  • (static picture)
  • (pictures, source code, and Java interactive animation)
  • by David M. Howard
  • RBT: A SmallEiffel Red-Black Tree Library (页面存档备份,存于互联网档案馆
  • libredblack: A C Red-Black Tree Library (页面存档备份,存于互联网档案馆
  • Data Structures and Algorithm Analysis in C (2nd edition) (页面存档备份,存于互联网档案馆
  • by Thomas Niemann

红黑树, 英語, black, tree, 是一种自平衡二叉查找树, 是在计算机科学中用到的一种数据结构, 典型用途是实现关联数组, 它在1972年由鲁道夫, 贝尔发明, 被称为, 对称二叉b树, 它现代的名字源于利奧尼達斯, 吉巴斯和罗伯特, 塞奇威克于1978年写的一篇论文, 的结构复杂, 但它的操作有着良好的最坏情况运行时间, 并且在实践中高效, 它可以在o, displaystyle, text, 时间内完成查找, 插入和删除, 这里的n, displaystyle, 是树中元素的数目, 目录, 歷史, 用. 红黑树 英語 Red black tree 是一种自平衡二叉查找树 是在计算机科学中用到的一种数据结构 典型用途是实现关联数组 它在1972年由鲁道夫 贝尔发明 被称为 对称二叉B树 它现代的名字源于利奧尼達斯 J 吉巴斯和罗伯特 塞奇威克于1978年写的一篇论文 红黑树的结构复杂 但它的操作有着良好的最坏情况运行时间 并且在实践中高效 它可以在O log n displaystyle text O log n 时间内完成查找 插入和删除 这里的n displaystyle n 是树中元素的数目 目录 1 歷史 2 用途和好处 3 性质 4 操作 4 1 插入 4 2 删除 4 3 C 示例代码 5 渐近边界的证明 6 参见 7 引用 8 外部链接歷史 编辑1972年 鲁道夫 拜尔發明了一種資料結構 那是一個B樹的四階特殊情況 此结构会維持所有從根到樹葉的路径都具有相同數量的節點 創造了一個完美的平衡樹 然而 他們並不是二元搜尋樹 拜爾在他的論文中稱呼他們為 對稱二元B樹 Symmetric binary B tree 後來他們以2 3 4樹或是2 4樹的形式開始流行 在一篇1972年的論文 A Dichromatic Framework for Balanced Trees 中 利奧尼達斯 J 吉巴斯和罗伯特 塞奇威克從對稱二元B樹 中推導出了紅黑樹 之所以選擇 紅色 是因為這是作者在帕羅奧多研究中心公司 Xerox PARC 工作時用彩色雷射列印機可以產生的最好看的顏色 另一種說法來自吉巴斯 是因為紅色和黑色的筆是他們當時可用來繪製樹的顏色 用途和好处 编辑红黑树和AVL树一样都对插入时间 删除时间和查找时间提供了最好可能的最坏情况担保 这不只是使它们在时间敏感的应用 如实时应用中有价值 而且使它们有在提供最坏情况担保的其他数据结构中作为基础模板的价值 例如 在计算几何中使用的很多数据结构都可以基于红黑树实现 红黑树在函数式编程中也特别有用 在这里它们是最常用的持久数据结构之一 它们用来构造关联数组和集合 每次插入 删除之后它们能保持为以前的版本 除了O log n displaystyle text O log n nbsp 的时间之外 红黑树的持久版本对每次插入或删除需要O log n displaystyle text O log n nbsp 的空间 红黑树是2 3 4树的一种等同 换句话说 对于每个2 3 4树 都存在至少一个数据元素是同样次序的红黑树 在2 3 4树上的插入和删除操作也等同于在红黑树中颜色翻转和旋转 这使得2 3 4树成为理解红黑树背后的逻辑的重要工具 这也是很多介绍算法的教科书在红黑树之前介绍2 3 4树的原因 尽管2 3 4树在实践中不经常使用 红黑树相对于AVL树来说 牺牲了部分平衡性以换取插入和删除操作时少量的旋转操作 整体来说性能要优于AVL树 性质 编辑红黑树是每个节点都带有颜色属性的二叉查找树 颜色为红色或黑色 在二叉查找树强制一般要求以外 对于任何有效的红黑树我们增加了如下的额外要求 节点是红色或黑色 根是黑色 所有叶子都是黑色 叶子是NIL节点 每个红色节点必须有两个黑色的子节点 或者说从每个叶子到根的所有路径上不能有两个连续的红色节点 或者说不存在两个相邻的红色节点 相邻指两个节点是父子关系 或者说红色节点的父节点和子节点均是黑色的 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点 下面是一个具体的红黑树的图例 nbsp An example of a red black tree这些约束确保了红黑树的关键特性 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长 结果是这个树大致上是平衡的 因为操作比如插入 删除和查找某个值的最坏情况时间都要求与树的高度成比例 这个在高度上的理论上限允许红黑树在最坏情况下都是高效的 而不同于普通的二叉查找树 要知道为什么这些性质确保了这个结果 注意到性质4导致了路径不能有两个毗连的红色节点就足够了 最短的可能路径都是黑色节点 最长的可能路径有交替的红色和黑色节点 因为根据性质5所有最长的路径都有相同数目的黑色节点 这就表明了没有路径能多于任何其他路径的两倍长 在很多树数据结构的表示中 一个节点有可能只有一个子节点 而叶子节点包含数据 用这种范例表示红黑树是可能的 但是这会改变一些性质并使算法复杂 为此 本文中我们使用 nil叶子 如上图所示 它不包含数据而只充当树在此结束的指示 这些节点在绘图中经常被省略 导致了这些树好像同上述原则相矛盾 而实际上不是这样 与此有关的结论是所有节点都有两个子节点 尽管其中的一个或两个可能是空叶子 操作 编辑因为每一个红黑树也是一个特化的二叉查找树 因此红黑树上的只读操作与普通二叉查找树上的只读操作相同 然而 在红黑树上进行插入操作和删除操作会导致不再符合红黑树的性质 恢复红黑树的性质需要少量 O log n displaystyle text O log n nbsp 的颜色变更 实际是非常快速的 和不超过三次树旋转 对于插入操作是两次 虽然插入和删除很复杂 但操作时间仍可以保持为O log n displaystyle text O log n nbsp 次 插入 编辑 我们首先以二叉查找树的方法增加节点并标记它为红色 如果设为黑色 就会导致根到叶子的路径上有一条路上 多一个额外的黑节点 这个是很难调整的 但是设为红色节点后 可能会导致出现两个连续红色节点的冲突 那么可以通过颜色调换 color flips 和树旋转来调整 下面要进行什么操作取决于其他临近节点的颜色 同人类的家族树中一样 我们将使用术语叔父节点来指一个节点的父节点的兄弟节点 注意 性质1和性质3总是保持着 性质4只在增加红色节点 重绘黑色节点为红色 或做旋转时受到威胁 性质5只在增加黑色节点 重绘红色节点为黑色 或做旋转时受到威胁 在下面的示意图中 将要插入的节点标为N N的父节点标为P N的祖父节点标为G N的叔父节点标为U 在图中展示的任何颜色要么是由它所处情形这些所作的假定 要么是假定所暗含 imply 的 对于每一种情形 我们将使用C示例代码来展示 通过下列函数 可以找到一个节点的叔父和祖父节点 node grandparent node n return n gt parent gt parent node uncle node n if n gt parent grandparent n gt left return grandparent n gt right else return grandparent n gt left 情形1 新节点N位于树的根上 没有父节点 在这种情形下 我们把它重绘为黑色以满足性质2 因为它在每个路径上对黑节点数目增加一 性质5符合 void insert case1 node n if n gt parent NULL n gt color BLACK else insert case2 n 情形2 新节点的父节点P是黑色 所以性质4没有失效 新节点是红色的 在这种情形下 树仍是有效的 性质5也未受到威胁 尽管新节点N有两个黑色叶子子节点 但由于新节点N是红色 通过它的每个子节点的路径就都有同通过它所取代的黑色的叶子的路径同样数目的黑色节点 所以依然满足这个性质 void insert case2 node n if n gt parent gt color BLACK return 树仍旧有效 else insert case3 n 注意 在下列情形下我们假定新节点的父节点为红色 所以它有祖父节点 因为如果父节点是根节点 那父节点就应当是黑色 所以新节点总有一个叔父节点 尽管在情形4和5下它可能是叶子节点 nbsp 情形3示意图情形3 如果父节点P和叔父节点U二者都是红色 此时新插入节点N做为P的左子节点或右子节点都属于情形3 这里右图仅显示N做为P左子的情形 则我们可以将它们两个重绘为黑色并重绘祖父节点G为红色 用来保持性质5 现在我们的新节点N有了一个黑色的父节点P 因为通过父节点P或叔父节点U的任何路径都必定通过祖父节点G 在这些路径上的黑节点数目没有改变 但是 红色的祖父节点G可能是根节点 这就违反了性质2 也有可能祖父节点G的父节点是红色的 这就违反了性质4 为了解决这个问题 我们在祖父节点G上递归地进行情形1的整个过程 把G当成是新加入的节点进行各种情形的检查 void insert case3 node n if uncle n NULL amp amp uncle n gt color RED n gt parent gt color BLACK uncle n gt color BLACK grandparent n gt color RED insert case1 grandparent n else insert case4 n 注意 在余下的情形下 我们假定父节点P是其祖父G的左子节点 如果它是右子节点 情形4和情形5中的左和右应当对调 nbsp 情形4示意图情形4 父节点P是红色而叔父节点U是黑色或缺少 并且新节点N是其父节点P的右子节点而父节点P又是其父节点的左子节点 在这种情形下 我们进行一次左旋转调换新节点和其父节点的角色 接着 我们按情形5处理以前的父节点P以解决仍然失效的性质4 注意这个改变会导致某些路径通过它们以前不通过的新节点N 比如图中1号叶子节点 或不通过节点P 比如图中3号叶子节点 但由于这两个节点都是红色的 所以性质5仍有效 void insert case4 node n if n n gt parent gt right amp amp n gt parent grandparent n gt left rotate left n n n gt left else if n n gt parent gt left amp amp n gt parent grandparent n gt right rotate right n n n gt right insert case5 n nbsp 情形5示意图情形5 父节点P是红色而叔父节点U是黑色或缺少 新节点N是其父节点的左子节点 而父节点P又是其父节点G的左子节点 在这种情形下 我们进行针对祖父节点G的一次右旋转 在旋转产生的树中 以前的父节点P现在是新节点N和以前的祖父节点G的父节点 我们知道以前的祖父节点G是黑色 否则父节点P就不可能是红色 如果P和G都是紅色就違反了性質4 所以G必須是黑色 我们切换以前的父节点P和祖父节点G的颜色 结果的树满足性质4 性质5也仍然保持满足 因为通过这三个节点中任何一个的所有路径以前都通过祖父节点G 现在它们都通过以前的父节点P 在各自的情形下 这都是三个节点中唯一的黑色节点 void insert case5 node n n gt parent gt color BLACK grandparent n gt color RED if n n gt parent gt left amp amp n gt parent grandparent n gt left rotate right n gt parent else Here n n gt parent gt right amp amp n gt parent grandparent n gt right rotate left n gt parent 注意插入实际上是原地算法 因为上述所有调用都使用了尾部递归 删除 编辑 如果需要删除的节点有两个儿子 那么问题可以被转化成删除另一个只有一个儿子的节点的问题 为了表述方便 这里所指的儿子 为非叶子节点的儿子 对于二叉查找树 在删除带有两个非叶子儿子的节点的时候 我们要么找到它左子树中的最大元素 要么找到它右子树中的最小元素 并把它的值转移到要删除的节点中 如在这里所展示的那样 我们接着删除我们从中复制出值的那个节点 它必定有少于两个非叶子的儿子 因为只是复制了一个值 没有复制颜色 不违反任何性质 这就把问题简化为如何删除最多有一个儿子的节点的问题 它不关心这个节点是最初要删除的节点还是我们从中复制出值的那个节点 在本文余下的部分中 我们只需要讨论删除只有一个儿子的节点 如果它两个儿子都为空 即均为叶子 我们任意将其中一个看作它的儿子 如果我们删除一个红色节点 此时该节点的儿子将都为叶子节点 它的父亲和儿子一定是黑色的 所以我们可以简单的用它的黑色儿子替换它 并不会破坏性质3和性质4 通过被删除节点的所有路径只是少了一个红色节点 这样可以继续保证性质5 另一种简单情况是在被删除节点是黑色而它的儿子是红色的时候 如果只是去除这个黑色节点 用它的红色儿子顶替上来的话 会破坏性质5 但是如果我们重绘它的儿子为黑色 则曾经通过它的所有路径将通过它的黑色儿子 这样可以继续保持性质5 需要进一步讨论的是在要删除的节点和它的儿子二者都是黑色的时候 这是一种复杂的情况 这种情况下该节点的两个儿子都是叶子节点 否则若其中一个儿子是黑色非叶子节点 另一个儿子是叶子节点 那么从该节点通过非叶子节点儿子的路径上的黑色节点数最小为2 而从该节点到另一个叶子节点儿子的路径上的黑色节点数为1 违反了性质5 我们首先把要删除的节点替换为它的儿子 出于方便 称呼这个儿子为N 在新的位置上 称呼它的兄弟 它父亲的另一个儿子 为S 在下面的示意图中 我们还是使用P称呼N的父亲 SL称呼S的左儿子 SR称呼S的右儿子 我们将使用下述函数找到兄弟节点 struct node sibling struct node n if n n gt parent gt left return n gt parent gt right else return n gt parent gt left 我们可以使用下列代码进行上述的概要步骤 这里的函数replace node替换child到n在树中的位置 出于方便 在本章节中的代码将假定空叶子被用不是NULL的实际节点对象来表示 在插入章节中的代码可以同任何一种表示一起工作 void delete one child struct node n Precondition n has at most one non null child struct node child is leaf n gt right n gt left n gt right replace node n child if n gt color BLACK if child gt color RED child gt color BLACK else delete case1 child free n 如果N和它初始的父亲是黑色 则删除它的父亲导致通过N的路径都比不通过它的路径少了一个黑色节点 因为这违反了性质5 树需要被重新平衡 有几种情形需要考虑 情形1 N是新的根 在这种情形下 我们就做完了 我们从所有路径去除了一个黑色节点 而新根是黑色的 所以性质都保持着 void delete case1 struct node n if n gt parent NULL delete case2 n 注意 在情形2 5和6下 我们假定N是它父亲的左儿子 如果它是右儿子 则在这些情形下的左和右应当对调 nbsp 情形2示意图情形2 S是红色 在这种情形下我们在N的父亲上做左旋转 把红色兄弟转换成N的祖父 我们接着对调N的父亲和祖父的颜色 完成这两个操作后 尽管所有路径上黑色节点的数目没有改变 但现在N有了一个黑色的兄弟和一个红色的父亲 它的新兄弟是黑色因为它是红色S的一个儿子 所以我们可以接下去按情形4 情形5或情形6来处理 注意 这里的图中没有显示出来 N是删除了黑色节点后替换上来的子节点 所以这个过程中由P gt X gt N变成了P gt N 实际上是少了一个黑色节点 也可以理解为Parent Black 和Silbing Red 那么他们的孩子黑色节点的数目肯定不等 让他们做新兄弟肯定是不平衡的 还需后面继续处理 这里看英文版本的 1 页面存档备份 存于互联网档案馆 比较的明了 void delete case2 struct node n struct node s sibling n if s gt color RED n gt parent gt color RED s gt color BLACK if n n gt parent gt left rotate left n gt parent else rotate right n gt parent delete case3 n nbsp 情形3示意图情形3 N的父亲 S和S的儿子都是黑色的 在这种情形下 我们简单的重绘S为红色 结果是通过S的所有路径 它们就是以前不通过N的那些路径 都少了一个黑色节点 因为删除N的初始的父亲使通过N的所有路径少了一个黑色节点 这使事情都平衡了起来 但是 通过P的所有路径现在比不通过P的路径少了一个黑色节点 所以仍然违反性质5 要修正这个问题 我们要从情形1开始 在P上做重新平衡处理 void delete case3 struct node n struct node s sibling n if n gt parent gt color BLACK amp amp s gt color BLACK amp amp s gt left gt color BLACK amp amp s gt right gt color BLACK s gt color RED delete case1 n gt parent else delete case4 n nbsp 情形4示意图情形4 S和S的儿子都是黑色 但是N的父亲是红色 在这种情形下 我们简单的交换N的兄弟和父亲的颜色 这不影响不通过N的路径的黑色节点的数目 但是它在通过N的路径上对黑色节点数目增加了一 添补了在这些路径上删除的黑色节点 void delete case4 struct node n struct node s sibling n if n gt parent gt color RED amp amp s gt color BLACK amp amp s gt left gt color BLACK amp amp s gt right gt color BLACK s gt color RED n gt parent gt color BLACK else delete case5 n nbsp 情形5示意图情形5 S是黑色 S的左儿子是红色 S的右儿子是黑色 而N是它父亲的左儿子 在这种情形下我们在S上做右旋转 这样S的左儿子成为S的父亲和N的新兄弟 我们接着交换S和它的新父亲的颜色 所有路径仍有同样数目的黑色节点 但是现在N有了一个黑色兄弟 他的右儿子是红色的 所以我们进入了情形6 N和它的父亲都不受这个变换的影响 void delete case5 struct node n struct node s sibling n if s gt color BLACK this if statement is trivial due to Case 2 even though Case two changed the sibling to a sibling s child the sibling s child can t be red since no red parent can have a red child the following statements just force the red to be on the left of the left of the parent or right of the right so case six will rotate correctly if n n gt parent gt left amp amp s gt right gt color BLACK amp amp s gt left gt color RED this last test is trivial too due to cases 2 4 s gt color RED s gt left gt color BLACK rotate right s else if n n gt parent gt right amp amp s gt left gt color BLACK amp amp s gt right gt color RED this last test is trivial too due to cases 2 4 s gt color RED s gt right gt color BLACK rotate left s delete case6 n nbsp 情形6示意图情形6 S是黑色 S的右儿子是红色 而N是它父亲的左儿子 在这种情形下我们在N的父亲上做左旋转 这样S成为N的父亲 P 和S的右儿子的父亲 我们接着交换N的父亲和S的颜色 并使S的右儿子为黑色 子树在它的根上的仍是同样的颜色 所以性质3没有被违反 但是 N现在增加了一个黑色祖先 要么N的父亲变成黑色 要么它是黑色而S被增加为一个黑色祖父 所以 通过N的路径都增加了一个黑色节点 此时 如果一个路径不通过N 则有两种可能性 它通过N的新兄弟 那么它以前和现在都必定通过S和N的父亲 而它们只是交换了颜色 所以路径保持了同样数目的黑色节点 它通过N的新叔父 S的右儿子 那么它以前通过S S的父亲和S的右儿子 但是现在只通过S 它被假定为它以前的父亲的颜色 和S的右儿子 它被从红色改变为黑色 合成效果是这个路径通过了同样数目的黑色节点 在任何情况下 在这些路径上的黑色节点数目都没有改变 所以我们恢复了性质4 在示意图中的白色节点可以是红色或黑色 但是在变换前后都必须指定相同的颜色 void delete case6 struct node n struct node s sibling n s gt color n gt parent gt color n gt parent gt color BLACK if n n gt parent gt left s gt right gt color BLACK rotate left n gt parent else s gt left gt color BLACK rotate right n gt parent 同样的 函数调用都使用了尾部递归 所以算法是原地算法 此外 在旋转之后不再做递归调用 所以进行了恒定数目 最多3次 的旋转 C 示例代码 编辑 define NIL NULL define BLACK 1 define RED 0 include lt iostream gt using namespace std class bst private struct Node int value bool color Node leftTree rightTree parent Node value 0 color RED leftTree NULL rightTree NULL parent NULL Node grandparent if parent NULL return NULL return parent gt parent Node uncle if grandparent NULL return NULL if parent grandparent gt rightTree return grandparent gt leftTree else return grandparent gt rightTree Node sibling if parent gt leftTree this return parent gt rightTree else return parent gt leftTree void rotate right Node p Node gp p gt grandparent Node fa p gt parent Node y p gt rightTree fa gt leftTree y if y NIL y gt parent fa p gt rightTree fa fa gt parent p if root fa root p p gt parent gp if gp NULL if gp gt leftTree fa gp gt leftTree p else gp gt rightTree p void rotate left Node p if p gt parent NULL root p return Node gp p gt grandparent Node fa p gt parent Node y p gt leftTree fa gt rightTree y if y NIL y gt parent fa p gt leftTree fa fa gt parent p if root fa root p p gt parent gp if gp NULL if gp gt leftTree fa gp gt leftTree p else gp gt rightTree p void inorder Node p if p NIL return if p gt leftTree inorder p gt leftTree cout lt lt p gt value lt lt if p gt rightTree inorder p gt rightTree string outputColor bool color return color BLACK RED Node getSmallestChild Node p if p gt leftTree NIL return p return getSmallestChild p gt leftTree bool delete child Node p int data if p gt value gt data if p gt leftTree NIL return false return delete child p gt leftTree data else if p gt value lt data if p gt rightTree NIL return false return delete child p gt rightTree data else if p gt value data if p gt rightTree NIL delete one child p return true Node smallest getSmallestChild p gt rightTree swap p gt value smallest gt value delete one child smallest return true else return false void delete one child Node p Node child p gt leftTree NIL p gt rightTree p gt leftTree if p gt parent NULL amp amp p gt leftTree NIL amp amp p gt rightTree NIL p NULL root p return if p gt parent NULL delete p child gt parent NULL root child root gt color BLACK return if p gt parent gt leftTree p p gt parent gt leftTree child else p gt parent gt rightTree child child gt parent p gt parent if p gt color BLACK if child gt color RED child gt color BLACK else delete case child delete p void delete case Node p if p gt parent NULL p gt color BLACK return if p gt sibling gt color RED p gt parent gt color RED p gt sibling gt color BLACK if p p gt parent gt leftTree rotate left p gt sibling rotate left p gt parent else rotate right p gt sibling rotate right p gt parent if p gt parent gt color BLACK amp amp p gt sibling gt color BLACK amp amp p gt sibling gt leftTree gt color BLACK amp amp p gt sibling gt rightTree gt color BLACK p gt sibling gt color RED delete case p gt parent else if p gt parent gt color RED amp amp p gt sibling gt color BLACK amp amp p gt sibling gt leftTree gt color BLACK amp amp p gt sibling gt rightTree gt color BLACK p gt sibling gt color RED p gt parent gt color BLACK else if p gt sibling gt color BLACK if p p gt parent gt leftTree amp amp p gt sibling gt leftTree gt color RED amp amp p gt sibling gt rightTree gt color BLACK p gt sibling gt color RED p gt sibling gt leftTree gt color BLACK rotate right p gt sibling gt leftTree else if p p gt parent gt rightTree amp amp p gt sibling gt leftTree gt color BLACK amp amp p gt sibling gt rightTree gt color RED p gt sibling gt color RED p gt sibling gt rightTree gt color BLACK rotate left p gt sibling gt rightTree p gt sibling gt color p gt parent gt color p gt parent gt color BLACK if p p gt parent gt leftTree p gt sibling gt rightTree gt color BLACK rotate left p gt sibling else p gt sibling gt leftTree gt color BLACK rotate right p gt sibling void insert Node p int data if p gt value gt data if p gt leftTree NIL insert p gt leftTree data else Node tmp new Node tmp gt value data tmp gt leftTree tmp gt rightTree NIL tmp gt parent p p gt leftTree tmp insert case tmp else if p gt rightTree NIL insert p gt rightTree data else Node tmp new Node tmp gt value data tmp gt leftTree tmp gt rightTree NIL tmp gt parent p p gt rightTree tmp insert case tmp void insert case Node p if p gt parent NULL root p p gt color BLACK return if p gt parent gt color RED if p gt uncle gt color RED p gt parent gt color p gt uncle gt color BLACK p gt grandparent gt color RED insert case p gt grandparent else if p gt parent gt rightTree p amp amp p gt grandparent gt leftTree p gt parent rotate left p p gt color BLACK p gt parent gt color RED rotate right p else if p gt parent gt leftTree p amp amp p gt grandparent gt rightTree p gt parent rotate right p p gt color BLACK p gt parent gt color RED rotate left p else if p gt parent gt leftTree p amp amp p gt grandparent gt leftTree p gt parent p gt parent gt color BLACK p gt grandparent gt color RED rotate right p gt parent else if p gt parent gt rightTree p amp amp p gt grandparent gt rightTree p gt parent p gt parent gt color BLACK p gt grandparent gt color RED rotate left p gt parent void DeleteTree Node p if p p NIL return DeleteTree p gt leftTree DeleteTree p gt rightTree delete p public bst NIL new Node NIL gt color BLACK root NULL bst if root DeleteTree root delete NIL void inorder if root NULL return inorder root cout lt lt endl void insert int x if root NULL root new Node root gt color BLACK root gt leftTree root gt rightTree NIL root gt value x else insert root x bool delete value int data return delete child root data private Node root NIL 渐近边界的证明 编辑包含n个内部节点的红黑树的高度是O log n displaystyle text O log n nbsp 定义 h v displaystyle h v nbsp 表示以节点v displaystyle v nbsp 为根的子树的高度 b h v displaystyle bh v nbsp 表示从v displaystyle v nbsp 到子树中任何叶子的黑色节点的数目 如果v displaystyle v nbsp 是黑色则不计数它 也叫做黑色高度 引理 以节点v displaystyle v nbsp 为根的子树有至少2 b h v 1 displaystyle 2 bh v 1 nbsp 个内部节点 引理的证明 通过归纳高度 基础 h v 0 displaystyle h v 0 nbsp 如果v displaystyle v nbsp 的高度是零则它必定是NIL 因此b h v 0 displaystyle bh v 0 nbsp 所以 2 b h v 1 2 0 1 1 1 0 displaystyle 2 bh v 1 2 0 1 1 1 0 nbsp 归纳假设 h v k displaystyle h v k nbsp 的v displaystyle v nbsp 有2 b h v 1 1 displaystyle 2 bh v 1 1 nbsp 个内部节点暗示了h v displaystyle v nbsp k 1的v displaystyle v nbsp 有2 b h v 1 displaystyle 2 bh v 1 nbsp 个内部节点 因为v displaystyle v nbsp 有h v displaystyle v nbsp gt 0所以它是个内部节点 同样的它有黑色高度要么是bh v displaystyle v nbsp 要么是bh v displaystyle v nbsp 1 依据v displaystyle v nbsp 是红色还是黑色 的两个儿子 通过归纳假设每个儿子都有至少2 b h v 1 1 displaystyle 2 bh v 1 1 nbsp 个内部接点 所以v displaystyle v nbsp 有 2 b h v 1 1 2 b h v 1 1 1 2 b h v 1 displaystyle 2 bh v 1 1 2 bh v 1 1 1 2 bh v 1 nbsp 个内部节点 使用这个引理我们现在可以展示出树的高度是对数性的 因为在从根到叶子的任何路径上至少有一半的节点是黑色 根据红黑树性质4 根的黑色高度至少是h r o o t 2 displaystyle frac h root 2 nbsp 通过引理我们得到 n 2 h r o o t 2 1 log n 1 h r o o t 2 h r o o t 2 log n 1 displaystyle n geqslant 2 frac h root 2 1 leftrightarrow log n 1 geqslant frac h root 2 leftrightarrow h root leqslant 2 log n 1 nbsp 因此根的高度是O log n displaystyle text O log n nbsp 参见 编辑AVL樹 B樹 跳跃列表 一种有可能替代平衡树的数据结构 dancing tree 英语 dancing tree 伸展樹 2 3 4树 Treap引用 编辑Mathworld Red Black Tree 页面存档备份 存于互联网档案馆 San Diego State University CS 660 Red Black tree notes 页面存档备份 存于互联网档案馆 by Roger Whitney Cormen Leiserson Rivest Stein Introduction to Algorithms Massachusetts The MIT Press 2002 pp273 77 ISBN 0 07 013151 1外部链接 编辑维基共享资源中相关的多媒体资源 红黑树An applet quick explanation 页面存档备份 存于互联网档案馆 Red Black Tree Demonstration 页面存档备份 存于互联网档案馆 An example animated GIF 200KB An example static picture Another explanation pictures source code and Java interactive animation Red Black Tree Demonstration by David M Howard RBT A SmallEiffel Red Black Tree Library 页面存档备份 存于互联网档案馆 libredblack A C Red Black Tree Library 页面存档备份 存于互联网档案馆 Data Structures and Algorithm Analysis in C 2nd edition 页面存档备份 存于互联网档案馆 Red Black Tree C Code Red Black Trees by Thomas Niemann 红黑树的介绍和实现 取自 https zh wikipedia org w index php title 红黑树 amp oldid 79282967, 维基百科,wiki,书籍,书籍,图书馆,

文章

,阅读,下载,免费,免费下载,mp3,视频,mp4,3gp, jpg,jpeg,gif,png,图片,音乐,歌曲,电影,书籍,游戏,游戏。