数据结构:红黑树(进击的Java新人Week.6学习笔记)

Author: Kelvin

一、红黑树介绍

  红黑树(Red-Black Tree, 简称RBTee),是一种特殊的二叉查找树。它满足二叉查找树的特征: 任意一个节点所包含的键值,大于左孩子的键值,小于等于右孩子的键值。
  顾名思义,红黑树的每个节点上具有一个表示颜色的属性,红色或者黑色。

  • 红黑树的节点定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class RBTNode<T extends Comparable<T>> {
boolean color;
T key;
RBTNode<T> left;
RBTNode<T> right;
RBTNode<T> parent;

public RBTNode(boolean color, T key) {
this.color = color;
this.key = key;
}

public RBTNode(boolean color, T key, RBTNode<T> left, RBTNode<T> right,
RBTNode<T> parent) {
super();
this.color = color;
this.key = key;
this.left = left;
this.right = right;
this.parent = parent;
}

}
  • 红黑树的特征
    必须满足以下特征的二叉查找树才是红黑树。
      (1) 每个节点或者是红色,或者是黑色
      (2) 根节点一定是黑色
      (3) 所有叶子节点都是黑色的。【红黑树中的叶子节点也叫NIL节点,是指空的叶子节点】
      (4) 如果一个节点是红色的,则它的子节点必须是黑色的。(任何两个父子节点不可能同时为红色)
      (5) 任意节点到其所有分支叶子节点的简单路径上的黑色节点个数相同。(该属性保证红黑树始终是一颗接近平衡的二叉树)

二、红黑树的基本操作(左旋和右旋)

  • 左旋
    左旋示意图
    代码实现:
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
/* 
* 对红黑树的节点(x)进行左旋转
* 左旋示意图(对节点x进行左旋):
* px px
* / /
* x y
* / \ --(左旋)-. / \
* lx y x ry
* / \ / \
* ly ry lx ly
*/
public void leftRotate(RBTNode<T> p){
RBTNode<T> parent = p.parent; // 父节点
RBTNode<T> pRightSon = p.right; //右孩子
RBTNode<T> pRrigtGrandLeftSon = pRightSon.left;//右孩子的左孩子

pRightSon.parent = parent;
if(parent != null){
if(p == parent.left){
parent.left = pRightSon;
}else if(p == parent.right){
parent.right = pRightSon;
}
}else {
this.mRoot = pRightSon;
}

pRightSon.left = p;
p.parent = pRightSon;

p.right = pRrigtGrandLeftSon;
if(pRrigtGrandLeftSon != null){
pRrigtGrandLeftSon.parent = p;
}
}
  • 右旋
    右旋示意图
    代码实现:
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
/* 
* 对红黑树的节点(y)进行右旋转
*
* 右旋示意图(对节点y进行右旋):
* py py
* / /
* y x
* / \ --(右旋)-. / \
* x ry lx y
* / \ / \
* lx rx rx ry
*
*/
public void rightRotate(RBTNode<T> p){
RBTNode<T> parent = p.parent; //父节点
RBTNode<T> pLeftSon = p.left; // 左节点
RBTNode<T> pleftGrandRightSon = pLeftSon.right; // 左孙,即左节点的右节点

pLeftSon.parent = parent;
if(parent != null){ //判断旋转的节点是否有父节点
if(p == parent.left){
parent.left = pLeftSon;
}else if(p == parent.right){
parent.right = pLeftSon;
}
}else {
this.mRoot = pLeftSon;
}

pLeftSon.right = p;
p.parent = pLeftSon;

p.left = pleftGrandRightSon;
if(pleftGrandRightSon != null){
pleftGrandRightSon.parent = p;
}
}

三、添加节点

我们知道,一棵树之所以成为红黑树,必须要满足红黑树的五个特征。对红黑树的插入操作,必然有可能会影响红黑树特性,因此我们需要对其进行修正。红黑树的插入主要分为三个步骤:
  1. 将红黑树当作一颗二叉查找树,将节点插入
  2.将插入的节点着色为”红色”
  3.通过一系列的旋转或着色等操作,使之重新成为一颗红黑树。

  • 插入节点后,对红黑树的影响分析
    新插入的节点,默认是红色(因为如果是黑色,就会直接影响红黑树的特性5,调整很麻烦);我们设置新插入的节点为N,其父节点为P,其祖父节点为G,其父亲P的兄弟节点为U。当父节点P是黑色的,不需要调整,下面主要讨论P是红色的情况。
  1. 当U是红色的。将P和U重绘为黑色并重绘结点G为红色。现在新结点N有 了一个黑色的父结点P,因为通过父结点P或叔父结点U的任何路径都必定通过祖父结点G, 在这些路径上的黑结点数目没有改变。但是,红色的祖父结点G的父结点也有可能是红色 的,这就违反了性质3。为了解决这个问题,我们在祖父结点G递归向上调整颜色
    image
  2. 当U是黑色,且N是左孩子。对祖父结点G 的一次右旋转; 在旋转产生的树中,以前的父结点P现在是新结点N和以前的祖父节点 G 的父结点,然后交换以前的父结点P和祖父结点G的颜色,结果仍满足红黑树性质
    image
  3. 当U时黑色,且N是右孩子。我们对P进行一次左旋转调换新结点和其父 结点的角色,就把问题转化成了第二种情况,此时对G做一次有旋转即可。
    image

    以上所分析的是当P是左孩子时的情况,当P时右孩子的情况跟是左孩子的情况一样,旋转的方向不一样而已,请读者自行分析。
  • 红黑树的插入代码实现
    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
    //添加节点
    public void insert(T key){
    if(null==key){
    return;
    }
    this.insert(new RBTNode<T>(BLACK, key));
    }

    private void insert(RBTNode<T> node){
    if(this.mRoot==null){
    this.mRoot = node;
    return;
    }
    RBTNode<T> curr = this.mRoot;
    int rs = 0;
    while(true){
    rs = node.key.compareTo(curr.key);
    if(rs < 0){
    if(curr.left != null){
    curr = curr.left;
    }else {
    curr.left = node;
    node.parent = curr;
    break;
    }
    }else {
    if(curr.right != null){
    curr = curr.right;
    }else {
    curr.right = node;
    node.parent = curr;
    break;
    }
    }
    }
    node.color = RED;

    //由于新插入节点会影响红黑树的结构,需要修正使其满足红黑树的性质
    insertFixUp(node);
    }

    //插入节点后修正红黑树
    private void insertFixUp(RBTNode<T> node) {
    RBTNode<T> parent, gparent;
    parent = parentOf(node);
    //若父节点存在,且父节点的颜色是红色
    while((parent = parentOf(node))!=null && isRed(parent)){
    gparent = parentOf(parent);
    if(gparent==null){
    break;
    }

    //若父节点是祖父节点的左孩子
    if(parent == gparent.left){
    //叔叔节点
    RBTNode<T> uncle = gparent.right;

    // case 1: 叔叔节点是红色
    if(uncle!=null && isRed(uncle)){
    setBlack(uncle);
    setBlack(parent);
    if(gparent != this.mRoot){
    setRed(gparent);
    }
    node = gparent;
    continue;
    }
    //case 2: 叔叔是黑色,当前节点是右孩子或者叔叔是null
    else if(parent.right==node){
    leftRotate(parent);
    setBlack(node);
    if(gparent != this.mRoot){
    setRed(gparent);
    }
    rightRotate(gparent);
    node = parent;
    }
    //case 3: 叔叔是黑色,当前节点是左孩子或者叔叔是null
    else{
    setBlack(parent);
    if(gparent != this.mRoot){
    setRed(gparent);
    }
    rightRotate(gparent);
    }
    }else { //父节点是祖父节点的右孩子
    RBTNode<T> uncle = gparent.left;
    // case 4: 叔叔节点是红色
    if(uncle!=null && isRed(uncle)){
    setBlack(uncle);
    setBlack(parent);
    if(gparent != this.mRoot){
    setRed(gparent);
    }
    node = gparent;
    continue;
    }

    //case 5:叔叔节点是黑色,且当前节点是左孩子或者叔叔是null
    else if(parent.left == node){
    rightRotate(parent);
    setBlack(node);
    if(gparent != this.mRoot){
    setRed(gparent);
    }
    leftRotate(gparent);
    node = parent;
    }
    else {//case 6:叔叔节点是黑色,且当前节点是右孩子或者叔叔是null
    setBlack(parent);
    if(gparent != this.mRoot){
    setRed(gparent);
    }
    leftRotate(gparent);
    }
    }

    }
    }

四、删除节点

由于红黑树的删除较复杂,请看另一篇博客 红黑树的删除

五、红黑树代码实现

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
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
package cn.kelvin.oocl.rbtree;

import java.util.ArrayDeque;
import java.util.Queue;

public class RBTree<T extends Comparable<T>> {
RBTNode<T> mRoot;
private static final boolean RED = false;
private static final boolean BLACK = true;

public RBTree() {
}

public RBTree(T rootKey) {
this.mRoot = new RBTNode<T>(BLACK, rootKey);
}

//添加节点
public void insert(T key){
if(null==key){
return;
}
this.insert(new RBTNode<T>(BLACK, key));
}

private void insert(RBTNode<T> node){
if(this.mRoot==null){
this.mRoot = node;
return;
}
RBTNode<T> curr = this.mRoot;
int rs = 0;
while(true){
rs = node.key.compareTo(curr.key);
if(rs < 0){
if(curr.left != null){
curr = curr.left;
}else {
curr.left = node;
node.parent = curr;
break;
}
}else {
if(curr.right != null){
curr = curr.right;
}else {
curr.right = node;
node.parent = curr;
break;
}
}
}
node.color = RED;

//由于新插入节点会影响红黑树的结构,需要修正使其满足红黑树的性质
insertFixUp(node);
}

//插入节点后修正红黑树
private void insertFixUp(RBTNode<T> node) {
RBTNode<T> parent, gparent;
parent = parentOf(node);
//若父节点存在,且父节点的颜色是红色
while((parent = parentOf(node))!=null && isRed(parent)){
gparent = parentOf(parent);
if(gparent==null){
break;
}

//若父节点是祖父节点的左孩子
if(parent == gparent.left){
//叔叔节点
RBTNode<T> uncle = gparent.right;

// case 1: 叔叔节点是红色
if(uncle!=null && isRed(uncle)){
setBlack(uncle);
setBlack(parent);
if(gparent != this.mRoot){
setRed(gparent);
}
node = gparent;
continue;
}
//case 2: 叔叔是黑色,当前节点是右孩子或者叔叔是null
else if(parent.right==node){
leftRotate(parent);
setBlack(node);
if(gparent != this.mRoot){
setRed(gparent);
}
rightRotate(gparent);
node = parent;
}
//case 3: 叔叔是黑色,当前节点是左孩子或者叔叔是null
else{
setBlack(parent);
if(gparent != this.mRoot){
setRed(gparent);
}
rightRotate(gparent);
}
}else { //父节点是祖父节点的右孩子
RBTNode<T> uncle = gparent.left;
// case 4: 叔叔节点是红色
if(uncle!=null && isRed(uncle)){
setBlack(uncle);
setBlack(parent);
if(gparent != this.mRoot){
setRed(gparent);
}
node = gparent;
continue;
}

//case 5:叔叔节点是黑色,且当前节点是左孩子或者叔叔是null
else if(parent.left == node){
rightRotate(parent);
setBlack(node);
if(gparent != this.mRoot){
setRed(gparent);
}
leftRotate(gparent);
node = parent;
}
else {//case 6:叔叔节点是黑色,且当前节点是右孩子或者叔叔是null
setBlack(parent);
if(gparent != this.mRoot){
setRed(gparent);
}
leftRotate(gparent);
}
}

}
}

//删除节点
public void remove(T key){
RBTNode<T> node = search(this.mRoot, key);
if(null!=node){
remove(node);
}
}

private void remove(RBTNode<T> node) {
RBTNode<T> child=null,parent=null;
boolean color=false;

//删除的节点有两个孩子,转化为删除该节点的后继节点
if((node.left!=null) && (node.right!=null)){
//记录后继节点
RBTNode<T> replace = node.right;
while(replace.left!=null)
replace = replace.left;

child = replace.right;
parent = replace.parent;
if(parent==node){
node.right = child;
if(child!=null){
child.parent = node;
}
}else {
parent.left = child;
if(child!=null){
child.parent = parent;
}
}

//将后继节点数据拷贝到待删除节点
node.key = replace.key;
color = replace.color;
if(color==BLACK){
removeFixUp(child,parent);
}
replace = null;
return;
}

if(node.left!=null){
child = node.left;
}else {
child = node.right;
}
parent = node.parent;

if(parent!=null){
if(parent.left==node){
parent.left = child;
}else {
parent.right = child;
}
}else {
this.mRoot = child;
}

if(child!=null){
child.parent = parent;
}
if(node.color==BLACK){
removeFixUp(child,parent);
}
node = null;
}

//删除节点后,修正红黑树
private void removeFixUp(RBTNode<T> node, RBTNode<T> parent) {
//node的兄弟节点
RBTNode<T> silNode = null;

//如果node是黑色的话,那么必为NIL节点
while((node == null || isBlack(node)) && (node != this.mRoot)){
if (parent.left == node) {
silNode = parent.right;
if(isRed(silNode)){ //case 1: 兄弟节点是红色节点,则parent为黑色,silNode的两个儿子都为黑色
setBlack(silNode);
setRed(parent );
leftRotate(parent);
silNode = parent.right;
}

//case 2: 兄弟节点是黑色节点,其左右孩子都为黑色
if((silNode.left==null || isBlack(silNode.left) && (silNode.right==null || isBlack(silNode.right)))){
setRed(silNode);
node = parent;
parent = parentOf(node);
continue;
}else {
//case 3: 兄弟节点是黑色,左孩子是红色,右孩子是黑色
if(silNode.right==null || isBlack(silNode.right)){
if(silNode.left!=null)
setBlack(silNode.left);
setRed(silNode);
rightRotate(silNode);
silNode = parent.right;
}

//case 4: 兄弟节点黑色,左孩子任意颜色,右孩子是红色
silNode.color = parent.color;
setBlack(parent);
if(silNode.right!=null){
setBlack(silNode.right);
}
leftRotate(parent);
break;
}
}else {
silNode = parent.left;
if(isRed(silNode)){
setBlack(silNode);
setRed(parent );
rightRotate(parent);
silNode = parent.left;
}

if((silNode.left==null || isBlack(silNode.left) && (silNode.right==null || isBlack(silNode.right)))){
setRed(silNode);
node = parent;
parent = parentOf(node);
continue;
}else {
//case 3: 兄弟节点是黑色,左孩子是黑色,右孩子是红色
if(silNode.left==null || isBlack(silNode.left)){
if(silNode.right!=null)
setBlack(silNode.right);
setRed(silNode);
leftRotate(silNode);
silNode = parent.left;
}

//case 4: 兄弟节点黑色,右孩子任意颜色,左孩子是红色
silNode.color = parent.color;
setBlack(parent);
if(silNode.left!=null){
setBlack(silNode.left);
}
rightRotate(parent);
break;
}
}
}

//如果node不是NIL节点,则必为红色
if(node != null)
setBlack(node);
}

private void setRed(RBTNode<T> node) {
if(node != null)
node.color = RED;
}

private void setBlack(RBTNode<T> node) {
if(node != null)
node.color = BLACK;
}

private boolean isRed(RBTNode<T> node) {
return ((node!=null) && (node.color==RED)) ? true : false;
}

private boolean isBlack(RBTNode<T> node) {
return ((node!=null) && (node.color==BLACK)) ? true : false;
}

private RBTNode<T> parentOf(RBTNode<T> node) {
return (node!=null) ? node.parent : null;
}

/*
* 对红黑树的节点(x)进行左旋转
* 左旋示意图(对节点x进行左旋):
* px px
* / /
* x y
* / \ --(左旋)-. / \ #
* lx y x ry
* / \ / \
* ly ry lx ly
*/
public void leftRotate(RBTNode<T> p){
RBTNode<T> parent = p.parent; // 父节点
RBTNode<T> pRightSon = p.right; //右孩子
RBTNode<T> pRrigtGrandLeftSon = pRightSon.left;//右孩子的左孩子

pRightSon.parent = parent;
if(parent != null){
if(p == parent.left){
parent.left = pRightSon;
}else if(p == parent.right){
parent.right = pRightSon;
}
}else {
this.mRoot = pRightSon;
}

pRightSon.left = p;
p.parent = pRightSon;

p.right = pRrigtGrandLeftSon;
if(pRrigtGrandLeftSon != null){
pRrigtGrandLeftSon.parent = p;
}
}

/*
* 对红黑树的节点(y)进行右旋转
*
* 右旋示意图(对节点y进行右旋):
* py py
* / /
* y x
* / \ --(右旋)-. / \
* x ry lx y
* / \ / \
* lx rx rx ry
*
*/
public void rightRotate(RBTNode<T> p){
RBTNode<T> parent = p.parent; //父节点
RBTNode<T> pLeftSon = p.left; // 左节点
RBTNode<T> pleftGrandRightSon = pLeftSon.right; // 左孙,即左节点的右节点

pLeftSon.parent = parent;
if(parent != null){ //判断旋转的节点是否有父节点
if(p == parent.left){
parent.left = pLeftSon;
}else if(p == parent.right){
parent.right = pLeftSon;
}
}else {
this.mRoot = pLeftSon;
}

pLeftSon.right = p;
p.parent = pLeftSon;

p.left = pleftGrandRightSon;
if(pleftGrandRightSon != null){
pleftGrandRightSon.parent = p;
}
}

//查找节点
public RBTNode<T> search(RBTNode<T> root,T key){
if(null==root || null==key){
return null;
}
int cmp = key.compareTo(root.key);
if(cmp<0)
return search(root.left, key);
else if(cmp>0)
return search(root.right, key);
else
return root;
}

public void inOrder(RBTNode<T> root){
if(root!=null){
inOrder(root.left);
System.out.print(root.key.toString()+ " ");
inOrder(root.right);
}
}

public void preOrder(RBTNode<T> root){
if(root!=null){
System.out.print(root.key.toString()+ " ");
preOrder(root.left);
preOrder(root.right);
}
}

public void postOrder(RBTNode<T> root){
if(root!=null){
postOrder(root.left);
postOrder(root.right);
System.out.print(root.key.toString()+ " ");
}
}

/**
* 层次遍历
* @param root
*/
public void listOrder(RBTNode<T> root){
if(root==null){
return;
}
Queue<RBTNode<T>> queue = new ArrayDeque<>();
queue.offer(root);
RBTNode<T> curr = null;
while(!queue.isEmpty()){
curr = queue.poll();
System.out.print(curr.key.toString()+" ");
if(curr.left != null){
queue.offer(curr.left);
}
if(curr.right != null){
queue.offer(curr.right);
}
}
}
public void listOrderWithColor(RBTNode<T> root){
if(root==null){
return;
}
Queue<RBTNode<T>> queue = new ArrayDeque<>();
queue.offer(root);
RBTNode<T> curr = null;
while(!queue.isEmpty()){
curr = queue.poll();
System.out.print(curr.key.toString()+"->"+(curr.color ? "B":"R")+" ");
if(curr.left != null){
queue.offer(curr.left);
}
if(curr.right != null){
queue.offer(curr.right);
}
}
}

public class RBTNode<T extends Comparable<T>> {
boolean color;
T key;
RBTNode<T> left;
RBTNode<T> right;
RBTNode<T> parent;

public RBTNode(boolean color, T key) {
this.color = color;
this.key = key;
}

public RBTNode(boolean color, T key, RBTNode<T> left, RBTNode<T> right,
RBTNode<T> parent) {
super();
this.color = color;
this.key = key;
this.left = left;
this.right = right;
this.parent = parent;
}

}
}