红黑树基本讲解
1、基本概念
R-B Tree,全称是Red-Black Tree,又称为“红黑树”,它一种特殊的二叉查找树。红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black)。
红黑树的特性:
1.1 性质
- 性质1: 每个节点要么是黑色,要么是红色。
- 性质2: 根节点是黑色。
- 性质3: 每个叶子节点(NIL) 是黑色。
- 性质4: 每个红色节点的两个子节点一定都是黑色。
- 性质5: 任意节点到每个叶子节 点的路径都包含数量相同的黑结点。从性质5又可以推出:
性质5.1:如果一个节点存在黑子节点, 那么该结点肯定有两个子节点
如图所示:
红黑树 并不是一个完美平衡二叉查找树,从图上可以看到,根结点100 的左子树显然比右子树高,但左子树和右子树的黑结点的层数是相等的,也即任意一个结点到到每 个叶子结点的路径都包含数量相同的黑结点(性质5)。
所以我们叫红黑树这种平衡为黑色完美平衡。
1.2 自平衡操作
- 变色 : 结点的颜色由红变黑或由黑变红
- **左旋 **: 以某个结点作为支点(旋转结点),其右子结点变为旋转结点的父结点,右子结点的左子结点变为旋转结点的右子结点,左子结点保持不变
- 右旋 : 以某个结点作为支点(旋转结点), 其左子结点变为旋转结点的父结点,左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变
红黑树右旋操作如下所示:
红黑树左旋 操作
1.3 红黑树的查找
红黑树的查找和二叉树的查找操作类似
1.4 红黑树的插入
插入操作包括两部分:1.查找要插入节点的父节点 2.插入后进行树的平衡操作
注意: 插入节点,必须为红色,理由很简单,红色的父节点(如果存在)为黑色节点时,红黑树的黑色平衡没被破坏,不需要做自平衡操作。但如果插入结点是黑色,那么插入位置所在的子树黑色结点总是多1,必须做自平衡。
2.红黑树的插入情景分析
情景1:插入的节点为空树
直接把插入的节点作为根节点就可以了,并且把根节点变成黑色
情景2:插入节点的Key已存在
处理:更新当前节点的值,为插入节点的值
情景3 插入结点的父结点为黑结点
由于插入的结点是红色的,并不会影响红黑树的平衡,直接插入即可,无需做自平衡。如图所示:
情景4:插入节点的父节点为红色
红黑树的性质2: 根结点是黑色,如果插入节点的父结点为红结点,那么该父结点不可能为根结点,所以插入结点总是存在祖父结点。
分为两种情况,如下图所示
- 一种是爸爸节点为红色,叔叔节点也是红色
- 一种是爸爸节点为红色,叔叔节点为黑色或者不存在
情景4.1 叔叔结点存在并且为红结点
依据红黑树性质4 可知,红色节点不能相连==>祖父结点肯定为黑结点。因为不可以同时存在两个相连的红结点。那么此时该插入子树的红黑层数的情况是:黑红红。显然最简单的处理方式是把其改为:红黑红
处理:
1 将爸爸节点(P)和叔叔节点(U)节点改为黑色
2 将爷爷PP改为红色
3 将爷爷PP设置为当前节点,进行后续处理
如下图所示:
可以看到,我们把PP结点设为红色了,如果PP的父结点是黑色,那么无需再做任何处理;但如果PP的父结点是红色,则违反了红黑树的性质.所以需要将PP设置为当前节点,继续做插入操作自平衡处理,真到平衡为为止.
插入情景4.2 叔叔结点不存在或为黑结点,并且插入结点的父亲结点是祖父结点的左子结点
注意:单纯从插入前来看,叔叔节点非红即空(NIL节点) ,否则的话破坏了红黑树性质5,此路径会比其它路径多-一个黑色节点。
新插入节点,可能为P节点的左子节点,也可能是P节点的右子节点,所以分为两种情况分别处理
4.2.1 新插入节点,为其父节点的左子节点(LL红色情况)
处理:
- 变颜色:将P设置 为黑色,将PP设置为红色 ,然后以爷爷节点为当前节点
- 对PP节点进行右旋
处理结果如下图所示:
4.2.2:新插入节点,为其父节点的右子节点(LR红色情况)
![](https://ls-picture-oss.oss-cn-hangzhou.aliyuncs.com/数据结构/红黑树/4.2.2 LR情况处理前.png?versionId=CAEQGxiBgMDGgpax2hciIDQ5OWRlYzEyMDYzMzQzYTBiZGU4YzJiNDdjMzRiOTNh)
处理:
- 对P进行左旋
- 将P设置为当前节点,得到LL红色情况
- 按照LL红色情况处理(1.变颜色2.右旋PP)
操作过程如下图所示:
第一步
![](https://ls-picture-oss.oss-cn-hangzhou.aliyuncs.com/数据结构/红黑树/4.2.2 LR情况处理第一步.png?versionId=CAEQGxiBgMDLgpax2hciIDAyZmQ4NzY0NjBjNTQ4MTJiMmUxNDIyZTViZWI1MzBk)
第二步
情况4.3 叔叔结点不存在或为黑结点,并且插入结点的父亲结点是祖父结点的右子结点
和上述操作4.2 相反图,如下所示:
4.3.1 新插入节点,为其父节点的右子节点(RR红色情况)
处理操作:
1.变颜色: 将P设置为黑色,将PP设置为红色
2.对PP节点进行左旋
旋转过程如下图所示:
4.3.2 新插入节点,为其父节点的左子节点(RL红色情况)
如下图所示:
![](https://ls-picture-oss.oss-cn-hangzhou.aliyuncs.com/数据结构/红黑树/4.3.2 RL情况.png?versionId=CAEQGxiBgMClhpax2hciIDc1Yjg2NjViMjIyMjQ4Mzk4MTUyNTEyOWQ2NDA2NGUw)
处理:
1.对P进行右旋
2.将P设置为当前节点,得到RR红色情况
3.按照RR红色情况处理(1.变颜色2.左旋PP)
第一步对P节点进行右旋操作 如下图所示:
第二步:变色+旋转,如下图所示
3.红黑树代码演示
3.1 左右旋参考示意图
3.2 代码演示
RBTree.java
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 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
|
public class RBTree<K extends Comparable<K>, V> {
private static final boolean RED = true;
private static final boolean BLACK = false;
private RBNode root;
public RBNode getRoot() { return root; }
private RBNode parentOf(RBNode node) { if (node != null) { return node.parent; } return null; }
private boolean isRed(RBNode node) { if (node != null) { return node.color == RED; } return false; }
private boolean isBlack(RBNode node) { if (node != null) { return node.color == BLACK; } return false; }
private void setRed(RBNode node) { if (node != null) { node.color = RED; } }
private void setBlack(RBNode node) { if (node != null) { node.color = BLACK; } }
public void inOrderPrint() { inOrderPrint(this.root); }
private void inOrderPrint(RBNode node) { if (node != null) { inOrderPrint(node.left); System.out.println(" key: " + node.key + " ,value:" + node.value); inOrderPrint(node.right); } }
private void leftRotate(RBNode x) { RBNode y = x.right; x.right = y.left; if (y.left != null) { y.left.parent = x; }
if (x.parent != null) { if (x == x.parent.left) { x.parent.left = y; } else { x.parent.right = y; } } else { this.root = y; this.root.parent = null; }
x.parent = y; y.left = x; }
private void rightRotate(RBNode y) { RBNode x = y.left; y.left = x.right; if (x.right != null) { x.right.parent = y; }
if (y.parent != null) { x.parent = y.parent; if (y == y.parent.left) { y.parent.left = x; } else { y.parent.right = x; } } else { this.root = x; this.root.parent = null; }
y.parent = x; x.right = y; }
public void insertNode(K key, V value) { RBNode rbNode = new RBNode(); rbNode.setKey(key); rbNode.setValue(value); rbNode.setColor(RED); insert(rbNode); }
private void insert(RBNode node) { RBNode parent = null; RBNode x = this.root;
while (x != null) { parent = x; int result = node.key.compareTo(x.key); if (result > 0) { x = x.right; } else if (result < 0) { x = x.left; } else { x.setValue(node.getValue()); return; } } node.parent = parent; if (parent != null) { int result = node.key.compareTo(parent.key); if (result > 0) { parent.right = node; } else { parent.left = node; } } else { this.root = node; }
balanceInsertion(node);
}
private void balanceInsertion(RBNode node) { this.root.setColor(BLACK); RBNode parent = parentOf(node); RBNode gparent = parentOf(parent); if (parent != null && isRed(parent)) { RBNode uncle = null; if (parent == gparent.left) { uncle = gparent.right; if (uncle != null && isRed(uncle)) { setBlack(parent); setBlack(uncle); setRed(gparent); balanceInsertion(gparent); return; } if (uncle == null || isBlack(uncle)) { if (node == parent.left) { setBlack(parent); setRed(gparent); rightRotate(gparent); return; } if (node == parent.right) { leftRotate(parent); balanceInsertion(parent); return; } } } else { uncle = gparent.left; if (uncle != null && isRed(uncle)) { setBlack(parent); setBlack(uncle); setRed(gparent); balanceInsertion(gparent); return; }
if (uncle == null || isBlack(uncle)) { if (node == parent.right) { setBlack(parent); setRed(gparent); leftRotate(gparent); return; }
if (node == parent.left) { rightRotate(parent); balanceInsertion(parent); return; }
} } } }
static class RBNode<K extends Comparable<K>, V> { private RBNode parent; private RBNode left; private RBNode right; private boolean color; private K key; private V value;
public RBNode() { }
public RBNode(RBNode parent, RBNode left, RBNode right, boolean color, K key, V value) { this.parent = parent; this.left = left; this.right = right; this.color = color; this.key = key; this.value = value; }
public RBNode getParent() { return parent; }
public void setParent(RBNode parent) { this.parent = parent; }
public RBNode getLeft() { return left; }
public void setLeft(RBNode left) { this.left = left; }
public RBNode getRight() { return right; }
public void setRight(RBNode right) { this.right = right; }
public boolean isColor() { return color; }
public void setColor(boolean color) { this.color = color; }
public K getKey() { return key; }
public void setKey(K key) { this.key = key; }
public V getValue() { return value; }
public void setValue(V value) { this.value = value; } } }
|
3.3 运行结果演示
没做类型处理,插入的都是字符串比较,按照字符串的ASICC进行排序和平衡,颜色就是区分显示,不表示红黑色
4.红黑树学习地址
1
| https://www.cs.usfca.edu/~galles/visualization/RedBlack.html
|