1 引言 红黑树 是树的数据结构中最为重要的一种。Java的容器TreeSet、TreeMap均使用红黑树实现。JDK1.8中HashMap中也加入了红黑树。C++ STL中的map和set同样使用红黑树实现。之前的文章已经详细介绍了2-3-4树的性质与操作。本篇文章将从2-3-4树与红黑树关系出发,详细阐明红黑树。 2-3-4树和红黑树是完全等价的,但是2-3-4树的编程实现相对复杂,所以一般是通过实现红黑树来实现替代2-3-4树,而红黑树也同样保证在O(logN)的时间内完成查找、插入和删除操作。
2 定义 红黑树 是每个节点都带有颜色属性的平衡二叉查找树 ,颜色为红色 或黑色 。除了二叉查找树一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
(1)节点是要么红色或要么是黑色。 (2)根一定是黑色节点。 (3)每个叶子节点都带有两个空的黑色节点(称之为NIL节点,它又被称为黑哨兵)。 (4)每个红色节点的两个子节点都是黑色(或者说从每个叶子到根的所有路径上不能有两个连续的红色节点)。 (5)从任一节点到它所能到达得叶子节点的所有简单路径都包含相同数目的黑色节点。
例如:图2.1所示的一棵红黑树:
图2.1
定义节点名称:父节点——P(Parent) 祖父节点——G(GrandParent) 叔叔节点——U(Uncle) 当前节点——C(Current) 兄弟节点——B(Brother) 左孩子——L(Left) 右孩子——R(Right)
3 性质 根节点到任意叶子节点的路径长度,最多相差一半。若树存在最短路径,则最短路径上均为黑色节点,那么第五条性质保证根节点到达最长路径与最短路径所包含的黑色节点数目相同,若最短路径长为N,则最长路径M=N+红色节点数目,性质4要求红色节点必定不连续,因此红色节点数目最多为N,则最长路径与最短路径最多相差N。
4 2-3-4树和红黑树的等价关系 如果一棵树满足红黑树,把红色节点收缩到其父节点,就变成了2-3-4树,所有红色节点都与其父节点构成3或4节点,其它节点为2节点。一颗红黑树对应唯一形态的2-3-4树,但是一颗2-3-4树可以对应多种形态的红黑树(主要是3节点可以对应两种不同的红黑树形态)。
例如:
5 查找 红黑树的查找操作与二叉搜索树查找方式一致,这里不再赘述。
6 插入 6.1 插入节点的父节点为黑色 若待插入节点的父节点为黑色,则直接插入节点,并将插入的节点涂红,插入结束。父节点为黑色,插入红色节点并不会使红黑树违背5条性质。 此种情形对应的在2-3-4树中的插入操作为:待插入位置的节点为2-节点或者3-节点,直接插入节点。 图解:
6.2 插入节点的父节点为红色,叔叔节点为黑色 若待插入节点的父节点为红色,叔叔节点为黑色,此种情形又需要区分节点的插入位置,根据插入位置的不同进行相应的调整。此种情形共有四种:
6.2.1 父节点P为G左孩子,插入位置为左孩子 若待插入节点C的父节点P是祖父节点G的左孩子,并且插入节点的位置位于父节点P的左孩子。调整过程如下:
图解: 1:插入后违背性质4,首先以祖父节点G为中心,执行右旋操作。 2:右旋操作结束,将父节点P涂黑色,祖父节点G涂红色,调整完毕。
6.2.2 父节点P为G左孩子,插入位置为右孩子 若待插入节点C的父节点P是祖父节点G的左孩子,并且插入节点的位置位于父节点P的右孩子。调整过程如下:
图解: 1:插入后违背性质4,首先以父节点P为中心,执行左旋操作。 2:左旋操作结束后,仍然违背性质4,此时的情形符合6.2.1。在以节点G为中心,执行右旋操作。右旋结束后,将节点C涂黑色,节点G涂红色,调整完毕。
6.2.3 父节点P为G右孩子,插入位置为右孩子 若待插入节点C的父节点P是祖父节点G的右孩子,并且插入节点的位置位于父节点P的右孩子。此种情形是6.2.1情形的镜像。调整过程如下:
图解: 1:插入后违背性质4,首先以祖父节点G为中心,执行左旋操作。 2:左旋操作结束,将父节点P涂黑色,祖父节点G涂红色,调整完毕。
6.2.4 父节点P为G右孩子,插入位置为左孩子 若待插入节点C的父节点P是祖父节点G的右孩子,并且插入节点的位置位于父节点P的左孩子。此种情形是6.2.2的镜像。调整过程如下:
图解: 1:插入后违背性质4,首先以父节点P为中心,执行右旋操作。 2:右旋操作结束后,仍然违背性质4,此时的情形符合6.2.3。在以节点G为中心,执行左旋操作。左旋结束后,将节点C涂黑色,节点G涂红色,调整完毕。
6.3 插入节点的父节点为红色,叔叔节点为红色 若待插入节点C的父节点P为红色,且叔叔节点U为红色,则需要根据插入位置的不同,做出相应的调整。此种情形共有两种:
6.3.1 插入位置为左子树
6.3.2 插入位置为右子树
7 删除 7.1 删除红色叶子节点 图解:
7.2 删除红色节点,只有左子树或只有右子树 图解:
7.3 删除红色节点,既有左子树又有右子树 如果要删除的节点不是叶子节点,用要删除节点的后继节点替换(只进行数据替换即可,颜色不变,此时也不需要调整结构),然后删除后继节点,后继节点的删除同样要遵循红黑树的删除过程。
7.4 删除的黑色节点仅有左子树或者仅有右子树 图解:
图中填充为绿色的节点代表既可以是红色也可以是黑色,下同。
7.5 删除黑色的叶子节点 删除黑色的叶子节点情况相对复杂,主要分为8种情况:
情形1:节点D的兄弟节点B为红色,且D是左孩子。
图解:
情形2:节点D的兄弟节点B为红色,且D是右孩子。
图解:
情形3:D为左孩子,D兄弟节点B为黑色,且远侄子节点为红色。
图解:
情形4:D为右孩子,D兄弟节点B为黑色,且远侄子节点为红色。
图解:
情形5:D为左孩子,兄弟节点B为黑色,远侄子节点为黑色,近侄子节点为红色。
图解:
情形6:D为右孩子,兄弟节点B为黑色,远侄子节点为黑色,近侄子节点为红色。
图解:
情形7:父节点P为红色,兄弟节点B和兄弟节点的两个孩子(只能是NIL节点)都为黑色的情况。
图解:
情形7对应2-3-4树删除操作中兄弟节点为2节点,父节点至少是个3节点,父节点key下移与兄弟节点合并。
情形8:父节点P,兄弟节点B和兄弟节点的两个孩子(只能为NIL节点)都为黑色的情况。
图解: 情形8对应2-3-4树删除操作中兄弟节点为2节点,父亲节点也为2节点,父节点key下移与兄弟节点合并,已父节点看成新的节点D,继续回溯。
代码实现 RBTree.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 #pragma once enum Color{ RED, BLACK }; template <class K ,class V >struct RBTreeNode { K key; V value; Color color; RBTreeNode<K, V>* left; RBTreeNode<K, V>* right; RBTreeNode<K, V>* parent; RBTreeNode(const K& key=K(), const V&value=V()) :key(key) , value(value) , color(RED) , left(NULL ) , right(NULL ) , parent(NULL ) {} }; template <class K ,class V >class RBTree { public : RBTree() :root(NULL ) {} bool Insert (const K& key, const V& value) ; void InOrder () { InOrder(root); cout << endl ; } bool Check () ; protected : void RotateL (RBTreeNode<K, V>*& parent) ; void RotateR (RBTreeNode<K, V>*& parent) ; void InOrder (RBTreeNode<K, V>* root) ; bool CheckRBTree (RBTreeNode<K, V>* root, int blackNum,int CBNum) ; protected : RBTreeNode<K, V>*root; }; template <class K , class V >bool RBTree <K, V>: :Insert(const K& key, const V& value){ if (root == NULL ) { root = new RBTreeNode<K, V>(key, value); root->color = BLACK; return true ; } RBTreeNode<K, V>* cur = root; RBTreeNode<K, V>* parent = NULL ; while (cur) { if (cur->key > key) { parent = cur; cur = cur->left; } else if (cur->key < key) { parent = cur; cur = cur->right; } else { return false ; } } cur = new RBTreeNode<K, V>(key, value); cur->parent = parent; if (parent->key > key) parent->left = cur; else if (parent->key < key) parent->right = cur; while (parent&&parent->color==RED) { RBTreeNode<K, V>* grandfather = parent->parent; if (parent == grandfather->left) { RBTreeNode<K, V>* uncle = grandfather->right; if (uncle&&uncle->color == RED) { grandfather->color = RED; parent->color =BLACK; uncle->color = BLACK; cur = grandfather; parent = grandfather->parent; } else if ( (uncle&&uncle->color==BLACK)||uncle==NULL ) { if (cur == parent->left) { parent->color = BLACK; grandfather->color = RED; RotateR(grandfather); } else { RotateL(parent); parent->color = BLACK; grandfather->color = RED; RotateR(grandfather); } break ; } } else if (parent == grandfather->right) { RBTreeNode<K, V>* uncle = grandfather->left; if (uncle&&uncle->color == RED) { grandfather->color = RED; parent->color = BLACK; uncle->color = BLACK; cur = grandfather; parent = cur->parent; } else if ( (uncle&&uncle->color == BLACK)||uncle==NULL ) { if (cur == parent->right) { parent->color = BLACK; grandfather->color = RED; RotateL(grandfather); } else if (cur==parent->left) { RotateR(parent); parent->color = BLACK; grandfather->color = RED; RotateL(grandfather); } break ; } } } root->color = BLACK; return true ; } template <class K , class V >void RBTree <K, V>: :RotateL(RBTreeNode<K, V>*& parent){ RBTreeNode<K, V>* ppNode = parent->parent; RBTreeNode<K, V>* subR = parent->right; RBTreeNode<K, V>* subRL = subR->left; parent->right = subRL; if (subRL) subRL->parent = parent; subR->left = parent; parent->parent = subR; if (ppNode == NULL ) { subR->parent = NULL ; root = subR; } else { subR->parent = ppNode; if (ppNode->left == parent) ppNode->left = subR; else if (ppNode->right == parent) ppNode->right = subR; } parent = subR; } template <class K , class V >void RBTree <K, V>: :RotateR(RBTreeNode<K, V>*& parent){ RBTreeNode<K, V>* ppNode = parent->parent; RBTreeNode<K, V>* subL = parent->left; RBTreeNode<K, V>* subLR = subL->right; parent->left = subLR; if (subLR) subLR->parent = parent; subL->right = parent; parent->parent = subL; if (ppNode==NULL ) { subL->parent = NULL ; root = subL; } else { subL->parent = ppNode; if (parent == ppNode->left) ppNode->left = subL; else ppNode->right = subL; } parent = subL; } template <class K , class V >void RBTree <K, V>: :InOrder(RBTreeNode<K, V>* root){ if (root == NULL ) return ; InOrder(root->left); cout << root->key << " " ; InOrder(root->right); } template <class K , class V >bool RBTree <K, V>: :Check(){ int blackNum = 0 ; RBTreeNode<K, V>* cur = root; while (cur) { if (cur->color == BLACK) blackNum++; cur = cur->left; } int CBNum = 0 ; return CheckRBTree(root,blackNum,CBNum); } template <class K , class V >bool RBTree <K, V>: :CheckRBTree(RBTreeNode<K, V>* root, int blackNum, int CBNum){ if (root == NULL ) return true ; if (root->color == BLACK) { CBNum++; if (root->left == NULL &&root->right == NULL ) { if (blackNum == CBNum) { return true ; } else { cout << "叶子节点为" << root->key << "路径的黑色节点数目与最左侧支路的黑色节点数目不相等 !" << endl ; return false ; } } } else if (root->parent&&root->parent->color == RED) { cout << root->parent->key << " 和 " << root->key << " 为两个连续的红色节点" << endl ; return false ; } return CheckRBTree(root->left, blackNum, CBNum) && CheckRBTree(root->right, blackNum, CBNum); }
main.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #define _CRT_SECURE_NO_WARNINGS 1 #include <iostream> using namespace std ;#include "RBTree.h" void TestRBTree () { int array [] = { 16 , 3 , 7 , 11 , 9 , 26 , 18 , 14 , 15 , 20 , 100 ,0 ,1 ,2 ,5 }; RBTree<int , int > tree; for (int i = 0 ; i < sizeof (array ) / sizeof (array [0 ]); i++) { tree.Insert(array [i], i); } tree.InOrder(); bool ret=tree.Check(); if (ret == true ) cout << "RBTree is in Balanced !" << endl ; else cout << "RBTree not in a state of balance !" << endl ; } int main () { TestRBTree(); system("pause" ); return 0 ; }