红黑树 RBTree

概述:R-B Tree,又称为“红黑树”。本文参考了《算法导论》中红黑树相关知识,加之自己的解,然后以图文的形式对红黑树进行说明。本文的主要内容包括:红黑树的特性,红黑树的时间复杂度和它的证明,红黑树的左旋、右旋、插入等操作。

1 R-B Tree简介

R-B Tree,全称是Red-Black Tree,又称为“红黑树”,它一种特殊的二叉查找树。红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black)。

红黑树的特性:
(1)每个节点或者是黑色,或者是红色。
(2)根节点是黑色。
(3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
(4)如果一个节点是红色的,则它的子节点必须是黑色的。
(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

2 R-B Tree时间复杂度

红黑树的时间复杂度为: O(lgn)
定理:一棵含有n个节点的红黑树的高度至多为2log(n+1).

3 R-B Tree基本操作

R-B Tree的基本操作是添加删除。 添加和删除操作,都会用到两个基本的方法:左旋右旋,统称为旋转。旋转是为了保持红黑树的特性而提供的辅助方法,因为当我们进行添加、删除节点时,可能改变红黑树的特性(例如,删除一个黑色节点之后,就不满足“从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点”这个特性);这里,我们就需要旋转方法的辅助来让树保持红黑树的特性。

3.4 添加操作

向一颗含有n个节点的红黑树中插入一个节点,可以在时间O(lgn)内完成。

将节点z插入红黑树T内。需要执行的操作依次时:首先,将T当作一颗二叉树,将z插入;然后,将z着色为红色;最后,通过RB-INSERT-FIXUP来对节点重新着色并旋转,以此来保证删除节点后的树仍然是一颗红黑树。

(01) 将T当作一颗二叉树,将z插入。
    因为红黑树本身就是一颗二叉树,所以,我们可以根据二叉树的性质将z插入。

(02) 将z着色为红色。
  在介绍为什么将则着色为红色之前,我们重新温习一下红黑树的特性:
(1)每个节点或者是黑色,或者是红色。
(2)根节点是黑色。
(3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
(4)如果一个节点是红色的,则它的子节点必须是黑色的。
(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

将插入的节点着色为红色,不会违背“特性(5)”;而若将插入的节点着色为黑色,会违背该特性。

(03) 通过RB-INSERT-FIXUP来对节点重新着色并旋转。
  因为(02)中插入一个红色节点之后,虽然没有违背“特性(5)”,但是却可能违背了其它特性(例如,若被插入节点的父节点也是红色;插入后,则违背了“特性(4)”)。我们需要通过RB-INSERT-FIXUP进行节点颜色的调整以及旋转等工作,让树仍然是一颗红黑树。

总的来说:当节点z被着色为红色节点,并插入二叉树时,有三种情况。

情况一:被插入的节点是根节点。
    直接把此节点涂为黑色。

情况二:被插入的节点的父节点是黑色。
    什么也不需要做。节点被插入后,仍然是红黑树。

情况三:被插入的节点的父节点是红色。
    那么,该情况与红黑树的“特性(5)”相冲突。情况三包含了“Case 1”、“Case 2” 和“Case 3”三种情况,情况三的目的是恢复红黑树的特性,它的处理思想是:将红色的节点移到根节点;然后,将根节点设为黑色。下面介绍情况三的三种情况。

Case 1:叔叔是红色

Case 1 现象说明:当前节点的父节点是红色,且当前节点的祖父节点的另一个子节点(叔叔节点)也是红色。
Case 1 处理策略:
    (01) 将“父节点”设为黑色。
    (02) 将“叔叔节点”设为黑色。
    (03) 将“祖父节点”设为“红色”。
    (04) 将“祖父节点”设为“当前节点”(红色节点);即,之后继续对“当前节点”进行操作。

Case 2:叔叔是黑色,且当前节点是右孩子

Case 2 现象说明:当前节点的父节点是红色,叔叔节点是黑色,且当前节点是其父节点的右孩子
Case 2 处理策略:
    (01) 将“父节点”作为“新的当前节点”。
    (02) 以“新的当前节点”为支点进行左旋。

Case 3:叔叔是黑色,当前节点是做孩子

Case 3:叔叔是黑色,且当前节点是左孩子
Case 3 现象说明:当前节点的父节点是红色,叔叔节点是黑色,且当前节点是其父节点的左孩子
Case 3 处理策略:
    (01) 将“父节点”设为“黑色”。
    (02) 将“祖父节点”设为“红色”。
    (03) 以“祖父节点”为支点进行右旋。

 #include<iostream>
using namespace std;
enum Color
{
 BLACK,
 RED
};
template<class K,class V>
struct RBTreeNode
{
 RBTreeNode(const K& key,const V&value,const Color col = RED)
 :_left(NULL)
 ,_right(NULL)
 ,_parent(NULL)
 ,_col(col)
 ,_key(key)
 ,_value(value)
 {}
 RBTreeNode<K, V>* _left;
 RBTreeNode<K, V>* _right;
 RBTreeNode<K, V>* _parent;
 Color _col;
 K _key;
 V _value; 
};
template<class K,class V>
class RBTree
{
 typedef RBTreeNode<K, V> Node;
public:
 RBTree()
  :_root(NULL)
 {}
 bool Insert(const K& key, const V& value)
 {
  if (_root == NULL)
  {
   _root = new Node(key, value, BLACK);
   return true;
  }
  Node* parent = NULL;
  Node* cur = _root;
  while (cur)
  {
   if (cur->_key > key)
   {
    parent = cur;
    cur = cur->_left;
   }
   else if (cur->_key < key)
   {
    parent = cur;
    cur = cur->_right;
   }
   else
   {
    break;
   }
  }
  cur = new Node(key, value, BLACK);
  if (parent->_key <key)
  {
   parent->_right = cur;
   cur->_parent = parent;
  }
  else
  {
   parent->_left = cur;
   cur->_parent = parent;
  }
  //调色
  while (cur != _root && parent->_col == RED)
  {
   Node* grandfather = parent->_parent;
   if (parent == grandfather->_left)
   {
    Node* uncle = parent->_right;
    if (uncle && uncle->_col == RED)
    {
     uncle->_col = parent->_col = BLACK;
     grandfather->_col = RED;
     cur = grandfather;
     parent = cur->_parent;
    }
    //当叔叔节点为黑色,且S为F的右孩子,处理步骤;1 以父节点进行左旋 2将父节点变黑祖父节点变红,3然后进行右旋
    else
    {
     if (cur == parent->_right)
     {
      RotateL(parent);
      swap(cur, parent);
     }
     RotateR(grandfather);
     parent->_col = BLACK;
     grandfather->_col = RED;
    }
   }
   else  //往右子树插
   {
    Node* uncle = grandfather->_left;
    if (uncle && uncle->_col == RED)
    {
     uncle->_col = parent->_col = BLACK;
     grandfather->_col = RED;
     cur = grandfather;
     parent = cur->_parent;
    }
    else
    {
     if (cur == parent->_left)
     {
      RotateR(parent);
      swap(cur, parent);
     }
     RotateL(grandfather);
     parent->_col = BLACK;
     grandfather->_col = RED;
    }
   }
  }
  _root->_col = BLACK;
  return true;
 }
 bool IsBalanceTree()
 {
  return _IsBalance(_root);
 }
 void InOrder()
 {
  _InOrder(_root);
  cout << endl;
 }
protected:
 void RotateL(Node* parent)
 {
  Node* subR = parent->_right;
  Node* subRL = subR->_left;
  parent->_right = subRL;
  if(subRL)
  {
   subRL->_parent = parent;
  }
  subR->_left = parent;
  subR->_parent = parent->_parent;
  parent = subR;
  if (parent->_parent == NULL)
  {
   _root = parent;
  }
  else
  {
   if (parent->_key < parent->_parent->_key)
   {
    parent->_parent->_left = parent;
   }
   else
   {
    parent->_parent->_right = parent;
   }
  }
 }
 void RotateR(Node* parent)
 {
  Node* subL = parent->_left;
  Node* subLR = subL->_right;
  parent->_left = subLR;
  if(subLR)
  {
   subLR->_parent = parent;
  }
  subL->_right = parent;
  subL->_parent = parent->_parent;
  parent->_parent = subL;
  parent = subL;
  if (parent->_parent == NULL)
  {
   _root = parent;
  }
  else
  {
   if (parent->_key < parent->_parent->_key)
   {
    parent->_parent->_left = parent;
   }
   else
   {
    parent->_parent->_right = parent;
   }
  }
 }
 void _InOrder(Node*& root)
 {
  if (root == NULL)
  {
   return;
  }
  _InOrder(root->_left);
  cout << root->_key << " ";
  _InOrder(root->_right);
 }
 int _Height(Node* root)
 {
  if (root == NULL)
  {
   return 0;
  }
  int left = _Height(root->_left);
  int right = _Height(root->_right);
  return (left > right)? left : right;
 }
 bool _IsBalance(Node* root)
 {
  if (root == NULL)
  {
   return true;
  }
  int left = _Height(root->_left);
  int right = _Height(root->_right);
   
  int bf = abs(left - right);
  if (bf > 1)
  {
   return false;
  }
  return _IsBalance(root->_left) && _IsBalance(root->_right);
 }
protected:
 Node* _root;
};
void TestRBTree()
{
 RBTree<int, int> t1;
 int a[10] = { 5, 2, 9, 6, 7, 3, 40, 1, 8 };
 for (size_t i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
 {
  t1.Insert(a[i], i);
  t1.InOrder();
 }
 cout << "IsBalanceTree:" << t1.IsBalanceTree() << endl;
}
int main()
{
 TestRBTree();
 system("pause");
 return 0;
}

4 运行结果

 5 红黑树的应用

红黑树的应用比较广泛,主要是用它来存储有序的数据,它的时间复杂度是O(lgn),效率非常之高。
例如,Java中的TreeSet和TreeMap,C++ STL中的set、map,以及Linux虚拟内存的管理,都是通过红黑树去实现的。

这里大致介绍下,红黑树和AVL树的差异。AVL树也是特殊的二叉树,它的特性是“任何节点的左右子树的高度之差不超过1”。基本上,用到红黑树的地方都可以用AVL树(自平衡二叉查找树)去替换。但是一般情况下,在执行添加、删除节点时,AVL树比红黑树执行的操作更多一些,效率更低一些;而且红黑树也是相对平衡的二叉树(从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点)。因此,红黑树的效率会高更一点。

时间: 2024-10-23 01:49:31

红黑树 RBTree的相关文章

红黑树(RBTREE)之上-------构造红黑树

该怎么说呢,现在写代码的速度还是很快的,很高兴,o(^▽^)o. 光棍节到了,早上没忍住,手贱了一般,看到*D的优惠,买了个机械键盘,晚上就到了,敲着还是很舒服的,和老婆炫耀了一把哈哈. 光棍节再去*mall买个,带着上班用. 正题,构造红黑树,就是节点的插入与调整,具体的理论我就不说了,图我也不画了,别人画的很好,我在纸上画的我自己都不想看.   贴几个网址作为参考吧: 参考的文档:1.http://www.cnblogs.com/zhb-php/p/5504481.html (推荐) 2.h

平衡搜索树--红黑树 RBTree

红黑树是一棵二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是Red或Black. 通过对任何一条从根到叶子节点简单路径上的颜色来约束树的高度,红黑树保证最长路径不超过最短路径的两倍,因而近似于平衡. 红黑树是满足下面红黑性质的二叉搜索树: 1. 每个节点,不是红色就是黑色的 2. 根节点是黑色的 3. 如果一个节点是红色的,则它的两个子节点是黑色的(不存在连续的红色节点) 4. 对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点. 思考:为什么满足上面

java——红黑树 RBTree

对于完全随机的数据,普通的二分搜索树就很好用,只是在极端情况下会退化成链表. 对于查询较多的情况,avl树很好用. 红黑树牺牲了平衡性,但是它的统计性能更优(综合增删改查所有的操作). 红黑树java实现(不完整,没有进行删除节点的操作): (默认左倾红黑树) package RedBlackTree; //从任意一个节点到叶子节点,经过的黑色节点是一样的--红黑树是保持"黑平衡"的二叉树 //因为23树中的每一个节点到叶子节点的深度是相同的 //红黑树在严格意义上不是平衡二叉树,最大

【转】B树、B-树、B+树、B*树、红黑树、 二叉排序树、trie树Double Array 字典查找树简介

B  树 即二叉搜索树: 1.所有非叶子结点至多拥有两个儿子(Left和Right): 2.所有结点存储一个关键字: 3.非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树: 如: B树的搜索,从根结点开始,如果查询的关键字与结点的关键字相等,那么就命中:否则,如果查询关键字比结点关键字小,就进入左儿子:如果比结点关键字大,就进入右儿子:如果左儿子或右儿子的指针为空,则报告找不到相应的关键字: 如果B树的所有非叶子结点的左右子树的结点数目均保持差不多(平衡),那么B树的搜索性

二叉搜索树—RBTree(红黑树)

  红黑树又称二叉搜索树,它主要是通过红和黑两种颜色(red.black)来标识节点.通过对任何一条从根节点到叶子节点路径上的节点颜色进行约束,红黑树保证最长路径不超过最短路径的两倍,所以说:红黑树是近似于平衡的. ■下面是红黑树的主要特点: (1)红黑树的根节点是黑色的. (2)红黑树中若一个节点是红色的,则它的两个子节点必须是黑色的. (3)红黑树中从该节点到后代叶节点的路径上,黑色节点数目是相同的.        ◆红黑树的应用: C++库.linux内核.java库等        ◆红

RBTree(红黑树)--C++

红黑树是满足下面性质的二叉搜索树 1. 每个节点,不是红色就是黑色的 2. 根节点是黑色的 3. 如果一个节点是红色的,则它的两个子节点是黑色的 4. 对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点. #pragma once enum Color{ RED, BLACK }; template<class K, class V> struct RBtreeNode { RBtreeNode(const K& key, const V& v, Co

红黑树(RB-tree)比AVL树的优势在哪?

1. 如果插入一个node引起了树的不平衡,AVL和RB-Tree都是最多只需要2次旋转操作,即两者都是O(1):但是在删除node引起树的不平衡时,最坏情况下,AVL需要维护从被删node到root这条路径上所有node的平衡性,因此需要旋转的量级O(logN),而RB-Tree最多只需3次旋转,只需要O(1)的复杂度. 2. 其次,AVL的结构相较RB-Tree来说更为平衡,在插入和删除node更容易引起Tree的unbalance,因此在大量数据需要插入或者删除时,AVL需要rebalan

二叉树之红黑树(RBTree)

详解以后再补充... 红黑树和AVL树6层模式下的最少结点数 通过图可以看到红黑树可以实现更少的结点,反过来说就是同样的结点数红黑树最大数高会超过AVL树 https://www.cs.usfca.edu/~galles/visualization/Algorithms.html这个网站可以测试动态效果,下图就是截图于此 红黑树插入删除步骤 输出 代码 其余代码:https://github.com/Duacai/Data-Structure-and-Algorithms/tree/master

平衡二叉树--红黑树(RB-Tree)

关键字:修复双黑.插入修复.变色.旋转.nill.红黑 前言: 新添加的节点默认是红色的. nil节点默认是黑色的.              自平衡方式--旋转 1.定义:节点是黑色或者红色,且满足以下五条性质的自平衡二叉树 性质: (1)性质1:节点是红色或黑色 (2)性质2:根节点是黑色的: (3)性质3:叶子节点是黑色的: (4)性质4:红色节点的子节点是黑色的:(红色节点不能相邻) (5)性质5:从根节点到任意一个叶子节点的路径上,黑色节点的个数必须相等(这个个数就称为黑高度 ).也就