Baixe Capítulo 43 - Árboles Rojo-Negro - Liang-Java-Comp11e e outras Notas de estudo em PDF para Informática, somente na Docsity!
Objectives
■ ■ To know what a red-black tree is (§43.1).
■ ■ To convert a red-black tree to a 2–4 tree and vice versa (§43.2).
■ ■ To design the RBTree class that extends the BST class (§43.3).
■ ■ To insert an element in a red-black tree and resolve the double-red
violation if necessary (§43.4).
■ ■ To delete an element from a red-black tree and resolve the double-black
problem if necessary (§43.5).
■ ■ To implement and test the RBTree class (§§43.6–43.7).
■ ■ To compare the performance of AVL trees, 2–4 trees, and RBTree
Red-Black Trees
CHAPTER
43-2 Chapter 43 Red-Black Trees
43.1 Introduction
A red-black tree is a balanced binary search tree derived from a 2–4 tree. A red-black
tree corresponds to a 2-4 tree.
Each node in a red-black tree has a color attribute red or black, as shown in Figure 43.1(a).
A node is called external if its left or right subtree is empty. Note that a leaf node is exter-
nal, but an external node is not necessarily a leaf node. For example, node 25 is external,
but it is not a leaf. The black depth of a node is defined as the number of black nodes in a
path from the node to the root. For example, the black depth of node 25 is 2 , and that of
node 27 is 2.
Note The red nodes appear in blue in the text.
A red-black tree has the following properties:
1. The root is black.
2. Two adjacent nodes cannot be both red.
3. All external nodes have the same black depth.
The red-black tree in Figure 43.1(a) satisfies all three properties. A red-black tree can be
converted to a 2-4 tree, and vice versa. Figure 43.1(b) shows an equivalent 2-4 tree for the
red-black tree in Figure 43.1(a).
Figure 43.1 A red-black tree can be represented using a 2-4 tree, and vice versa.
20
(^15 )
3 16 25
27
50 15 20 34
3 16 25 27^50
(a) A red-black tree (b) A 2-4 tree
Point
Key
43.2 Conversion between Red-Black Trees
and 2-4 Trees
This section discusses the correspondence between a red-black tree and a 2-4 tree.
You can design insertion and deletion algorithms for red-black trees without having knowledge
of 2-4 trees. However, the correspondence between red-black trees and 2-4 trees provides use-
ful intuition about the structure of red-black trees and operations. For this reason, this section
discusses the correspondence between these two types of trees.
To convert a red-black tree to a 2-4 tree, simply merge every red node with its parent to
create a 3-node or a 4-node. For example, the red nodes 15 and 34 are merged to their parent
to create a 4-node, and the red node 27 is merged to its parent to create a 3-node, as shown in
Figure 43.1(b).
Point
Key
43-4 Chapter 43 Red-Black Trees
You can prove the conversion results in a red-black tree that satisfies all three properties.
Property 1. The root is black.
Proof: If the root of a 2-4 tree is a 2-node, the root of the red-black tree is black.
If the root of a 2-4 tree is a 3-node or 4-node, the transformation produces a black
parent at the root.
Property 2. Two adjacent nodes cannot be both red.
Proof: Since the parent of a red node is always black, no two adjacent nodes can
be both red.
Property 3. All external nodes have the same black depth.
Proof: When you covert a node in a 2-4 tree to red-black tree nodes, you get one
black node and zero, one, or two red nodes as its children, depending on whether
the original node is a 2-, 3-, or 4-node. Only a leaf 2-4 node may produce external
red-black nodes. Since a 2-4 tree is perfectly balanced, the number of black nodes
in any path from the root to an external node is the same.
43.2.1 What is a red-black tree? What is an external node? What is black depth? 43.2.2 Describe the properties of a red-black tree. 43.2.3 How do you convert a red-black tree to a 2-4 tree? Is the conversion unique? 43.2.4 How do you convert a 2-4 tree to a red-black tree? Is the conversion unique?
43.3 Designing Classes for Red-Black Trees
A red-black tree designs a class for a red-black tree.
A red-black tree is a binary search tree. So, you can define the RBTree class to extend the BST
class, as shown in Figure 43.4. The BST and TreeNode classes are defined in §26.2.5.
Each node in a red-black tree has a color property. Because the color is either red or black,
it is efficient to use the boolean type to denote it. The RBTreeNode class can be defined to
extend BST.TreeNode with the color property. For convenience, we also provide the methods
for checking the color and setting a new color. Note that TreeNode is defined as a static inner
class in BST. RBTreeNode will be defined as a static inner class in RBTree. Note that BSTNode
contains the data fields element , left , and right , which are inherited in RBTreeNode. So,
RBTreeNode contains four data fields, as pictured in Figure 43.5.
Point
Check
Point
Key
Figure 43.4 The RBTree class extends BST with new implementations for the insert and delete methods.
1 Link
RBTreeNode
–red: boolean
+RBTreeNode() +RBTreeNode(e: E) +isRed(): boolean +isBlack(): boolean +setRed(): void +setBlack(): void
TreeNode
+RBTree() +RBTree(objects: E[]) #createNewNode(): RBTreeNode +insert(o: E): boolean +delete(o: E): boolean
RBTree
BST
m 0 Creates a default red-black tree. Creates an RBTree from an array of objects. Override this method to create an RBTreeNode. Returns true if the element is added successfully. Returns true if the element is removed from the tree successfully.
43.4 Overriding the insert Method 43-
In the BST class, the createNewNode() method creates a TreeNode object. This method
is overridden in the RBTree class to create an RBTreeNode. Note the return type of the
createNewNode() method in the BST class is TreeNode , but the return type of the creat-
eNewNode() method in RBTree class is RBTreeNode. This is fine, since RBTreeNode is a
subtype of TreeNode.
Searching an element in a red-black tree is the same as searching in a regular binary search
tree. So, the search method defined in the BST class also works for RBTree.
The insert and delete methods are overridden to insert and delete an element and per-
form operations for coloring and restructuring if necessary to ensure that the three properties
of the red-black tree are satisfied.
Pedagogical Note Run from http://liveexample.pearsoncmg.com/dsanimation/ RBTree.html to see how a red-black tree works, as shown in Figure 43.6.
F igure 43.6 The animation tool enables you to insert, delete, and search elements in a red-black
tree visually.
43.4 Overriding the insert Method
This section discusses how to insert an element to red-black tree.
A new element is always inserted as a leaf node. If the new node is the root, color it black.
Otherwise, color it red. If the parent of the new node is red, it violates Property 2 of the red-
black tree. We call this a double-red violation.
Let u denote the new node inserted, v the parent of u , w the parent of v , and x the sibling
of v. To fix the double-red violation, consider two cases:
Case 1: x is black or x is null. There are four possible configurations for u , v , w , and x , as shown
in Figures 43.7(a), 43.8(a), 43.9(a), and 43.10(a). In this case, u , v , and w form a 4-node in
the corresponding 2-4 tree, as shown in Figures 43.7(c), 43.8(c), 43.9(c), and 43.10(c), but
are represented incorrectly in the red-black tree. To correct this error, restructure and recolor
three nodes u , v , and w , as shown in Figures 43.7(b), 43.8(b), 43.9(b), and 43.10(b). Note x ,
y 1 , y 2 , and y 3 may be null.
Point
Key
Figure 43.5 An RBTreeNode contains data fields element , red , left , and right.
node: RBTreeNode #element: E -red: boolean #left: TreeNode #right: TreeNode
43.4 Overriding the insert Method 43-
in Figure 43.11(a). After recoloring, the nodes are shown in Figure 43.12(c). Now w is red,
if w ’s parent is black, the double-red violation is fixed. Otherwise, a new double-red violation
occurs at node w. We need to continue the same process to eliminate the double-red violation
at w , recursively.
A more detailed algorithm for inserting an element is described in Listing 43.1.
Listing 43.1 Inserting an Element to a Red-Black Tree
1 public boolean insert(E e) { 2 boolean successful = super .insert(e); 3 if (!successful) 4 return false ; // e is already in the tree 5 else { 6 ensureRBTree(e); 7 } 8 9 return true ; // e is inserted 10 } 11 12 /** Ensure that the tree is a red-black tree */ 13 private void ensureRBTree(E e) { 14 Get the path that leads to element e from the root.
Figure 43.11 Case 2 has four possible configurations.
30
25
40
u
v
w
50
x 30
35
40
u
v
w
50
x
(a) (b)
(c) (d)
30
45
40
u
v
w
50
x 30
55
40
u
v
w
50
x
Figure 43.12 Splitting a 4-node corresponds to recoloring the nodes in the red-black tree.
(a) A 4-node (b) Splitting a 4-node (c) Recoloring nodes
30
25
40
u
v
w
50
x
30 40 50
Insert a new element
40
v 50
x
Insert to its parent
25 30
43-8 Chapter 43 Red-Black Trees
15 int i = path.size() – 1 ; // Index to the current node in the path 16 Get u, v from the path. u is the node that contains e and v 17 is the parent of u. 18 Color u red; 19 20 if (u == root) // If e is inserted as the root, set root black 21 u.setBlack(); 22 else if (v.isRed()) 23 fixDoubleRed(u, v, path, i); // Fix double-red violation at u 24 } 25 26 /** Fix double-red violation at node u */ 27 private void fixDoubleRed(RBTreeNode u, RBTreeNode v, 28 ArrayList<TreeNode> path, int i) { 29 Get w from the path. w is the grandparent of u. 30 31 // Get v’s sibling named x 32 RBTreeNode x = (w.left == v)? 33 (RBTreeNode)(w.right) : (RBTreeNode)(w.left); 34 35 if (x == null || x.isBlack()) { 36 // Case 1: v's sibling x is black 37 if (w.left == v && v.left == u) { 38 // Case 1.1: u < v < w, Restructure and recolor nodes 39 } 40 else if (w.left == v && v.right == u) { 41 // Case 1.2: v < u < w, Restructure and recolor nodes 42 } 43 else if (w.right == v && v.right == u) { 44 // Case 1.3: w < v < u, Restructure and recolor nodes 45 } 46 else { 47 // Case 1.4: w < u < v, Restructure and recolor nodes 48 } 49 } 50 else { // Case 2: v's sibling x is red 51 Color w and u red 52 Color two children of w black. 53 54 if (w is root) { 55 Set w black; 56 } 57 else if (the parent of w is red) { 58 // Propagate along the path to fix new double-red violation 59 u = w; 60 v = parent of w; 61 fixDoubleRed(u, v, path, i – 2); // i – 2 propagates upward 62 } 63 } 64 }
The insert(E e) method (lines 1–10) invokes the insert method in the BST class to create
a new leaf node for the element (line 2). If the element is already in the tree, return false (line
4). Otherwise, invoke ensureRBTree(e) (line 6) to ensure that the tree satisfies the color and
black depth property of the red-black tree.
The ensureRBTree(E e) method (lines 13–24) obtains the path that leads to e from the
root (line 14), as shown in Figure 43.13. This path plays an important role to implement the
algorithm. From this path, you get nodes u and v (lines 16–17). If u is the root, color u black
(lines 20–21). If v is red, a double-red violation occurs at node u. Invoke fixDoubleRed to
fix the problem.
43-10 Chapter 43 Red-Black Trees
Figure 43.14 Inserting into a red-black tree: (a) initial empty tree; (b) inserting 34 ;
(c) inserting 3 ; (d) inserting 50 ; (e) inserting 20 causes a double red; (f) after recolor-
ing (Case 2); (g) inserting 15 causes a double red; (h) after restructuring and recoloring
(Case 1.4); (i) inserting 16 causes a double red; (j) after recoloring (Case 2); (k) insert-
ing 25 ; (l) inserting 27 causes a double red at 27 ; (m) a double red at 20 reappears after
recoloring (Case 2); and (n) after restructuring and recoloring (Case 1.2).
root in null^34 (a) (b) (c) (d) (e)
(g) (h)
(j) (k)
(m) (n)
3
34
3
34
50
3
34
50
20
15
34
50
(^320)
3
34
50
20
15
(f)
3
34
50
20
15
34
50
(^320)
16
15
34
50
(^320)
16 25
(l)
15
34
50
(^320)
16 25
27
15
34
50
(^320)
16 25
27
15
20
34
(^3 )
27
50
(i)
15
34
50
(^320)
16
43.5 Overriding the delete Method 43-
A double black in a red-black tree corresponds to an empty node for u (i.e., underflow
situation) in the corresponding 2-4 tree, as shown in Figure 43.16(b). To fix the double-black
problem, we will perform equivalent transfer and fusion operations. Consider three cases:
Case 1 : The sibling y of childOfu is black and has a red child. This case has four possible con-
figurations, as shown in Figures 43.17(a), 43.18(a), 43.19(a), and 43.20(a). The dashed circle
denotes that the node is either red or black. To eliminate the double-black problem, restructure
and recolor the nodes, as shown in Figures 43.17(b), 43.18(b), 43.19(b), and 43.20(b).
Figure 43.16 (a) childOfu is denoted double black. (b) u corresponds to an empty node
in a 2-4 tree.
(a)
u is black
parentOfu
childOfu is black or null
u
parentOfu
(b)
Figure 43.15 u is an external node and childOfu may be null.
(a) Before deleting u (b) After deleting u
u
parentOfu
childOfu
u
parentOfu
childOfu
Figure 43.17 Case 1.1: The sibling y of childOfu is black and y1 is red.
(a) (b)
parent
childOfu
y
y 1
y 2
parent childOfu is y double black
y 1 y 2
Figure 43.18 Case 1.2: The sibling y of childOfu is black and y2 is red.
parent childOfu is y doubleblack
y 1 y 2
parent
childOfu
y 2
y
y 1 y 2 .left y 2 .right
(a) (^) (b)
43.5 Overriding the delete Method 43-
Note Figures 43.22 and 43.23 show that childOfu is a right child of parent. If childOfu is a left child of parent , recoloring is performed identically.
Note Case 2 corresponds to a fusion operation in the 2-4 tree. For example, the corresponding 2-4 tree for Figure 43.22(a) is shown in Figure 43.24(a), and it is transformed into Figure 43.24(b) through a fusion operation.
Case 3: The sibling y of childOfu is red. In this case, perform an adjustment operation. If
y is a left child of parent , let y1 and y2 be the left and right children of y , as shown in
Figure 43.25. If y is a right children of parent , let y1 and y2 be the left and right child of
y , as shown in Figure 43.26. In both cases, color y black and parent red. childOfu is still
a fictitious double-black node. After the adjustment, the sibling of childOfu is now black,
and either Case 1 or Case 2 applies. If Case 1 applies, a one-time restructuring and recoloring
operation eliminates the double-black problem. If Case 2 applies, the double-black problem
cannot reappear, since parent is now red. Therefore, one-time application of Case 1 or Case 2
will complete Case 3.
Note Case 3 results from the fact that a 3-node may be transformed in two ways to a red-black tree, as shown in Figure 43.27.
Figure 43.23 Case 2: Recoloring propagates the double-black problem if parent is black.
parent parent
y (^) y
y (^1) y y (^2 1) y 2
childOfu
(a) (b)
childOfu is double black
Figure 43.24 Case 2 corresponds to a fusion operation in the corresponding 2-4 tree.
(a) (b)
u
parent
childOfu
y parent
childOfu
y
...
Figure 43.25 Case 3.1: y is a left red child of parent.
(a) (b)
parent
y
y 1 y 2
childOfu is double black (^) y parent 1
y 2
y
childOfu is double black
43-14 Chapter 43 Red-Black Trees
Based on the foregoing discussion, Listing 43.2 presents a more detailed algorithm for delet-
ing an element.
Listing 43.2 Deleting an Element from a Red-Black Tree 1 public boolean delete(E e) { 2 Locate the node to be deleted 3 if (the node is not found) 4 return false ; 5 6 if (the node is an internal node) { 7 Find the rightmost node in the subtree of the node; 8 Replace the element in the node with the one in rightmost; 9 The rightmost node is the node to be deleted now; 10 } 11 12 Obtain the path from the root to the node to be deleted; 13 14 // Delete the last node in the path and propagate if needed 15 deleteLastNodeInPath(path); 16
Figure 43.26 Case 3.2: y is a right red child of parent.
(a) (^) (b)
parent
y
childOfu is y 1 y 2 double black y 1
y 2
y
childOfu is double black
parent
Figure 43.27 A 3-node may be transformed in two ways to red-black tree nodes.
u
y parent
childOfu
y 1 y 2 or
parent
childOfu is double black
childOfu is double black
y
y 1 y 2
u
childOfu
(a)
(b)
(c)
y
y 1
y 2 u
childOfu
parent
43-16 Chapter 43 Red-Black Trees
77 fixDoubleBlack(grandparent, parent, db, path, i − 1 ); 78 } 79 } 80 else if (y.isRed()) { 81 if (parent.right == db) { 82 // Case 3.1: y is a left red child of parent 83 parent.left = y2; 84 y.right = parent; 85 } 86 else { 87 // Case 3.2: y is a right red child of parent 88 parent.right = y.left; 89 y.left = parent; 90 } 91 92 parent.setRed(); // Color parent red 93 y.setBlack(); // Color y black 94 connectNewParent(grandparent, parent, y); // y is new parent 95 fixDoubleBlack(y, parent, db, path, i − 1 ); 96 } 97 }
The delete(E e) method (lines 1–19) locates the node that contains e (line 2). If the node
does not exist, return false (lines 3–4). If the node is an internal node, find the right most
node in its left subtree and replace the element in the node with the element in the right most
node (lines 6–9). Now the node to be deleted is an external node. Obtain the path from the root
to the node (line 12). Invoke deleteLastNodeInPath(path) to delete the last node in the
path and ensure that the tree is still a red-black tree (line 15).
The deleteLastNodeInPath method (lines 22–36) obtains the last node u , parentOfu ,
grandparendOfu , and childOfu (lines 23–26). If childOfu is the root or u is red, the tree
is fine (lines 29–30). If childOfu is red, color it black (lines 31–32). We are done. Otherwise,
u is black and childOfu is null or black. Invoke fixDoubleBlack to eliminate the double-
black problem (line 35).
The fixDoubleBlack method (lines 39–97) eliminates the double-black problem. Obtain
y , y1 , and y2 (line 42). y is the sibling of the double-black node. y1 and y2 are the left and
right children of y. Consider three cases:
1. If y is black and one of its children is red, the double-black problem can be fixed by
one-time restructuring and recoloring in Case 1 (lines 44–63).
2. If y is black and its children are null or black, change y to red. If parent of y is black,
denote parent to be the new double-black node and invoke fixDoubleBlack recur-
sively (line 77).
3. If y is red, adjust the nodes to make parent a child of y (lines 84, 89) and color parent
red and y black (lines 92–93). Make y the new parent (line 94). Recursively invoke
fixDoubleBlack on the same double-black node with a different color for parent
(line 95).
Figure 43.28 shows the steps of deleting elements. To delete 50 from the tree in Figure 43.28(a),
apply Case 1.2, as shown in Figure 43.28(b). After restructuring and recoloring, the new tree
is as shown in Figure 43.28(c).
When deleting 20 in Figure 43.28(c), 20 is an internal node, and it is replaced by 16 , as
shown in Figure 43.28(d). Now Case 2 applies to deleting the rightmost node, as shown in
Figure 43.28(e). Recolor the nodes results in a new tree, as shown in Figure 43.28(f).
When deleting 15 , connect node 3 with node 20 and color node 3 black, as shown in
Figure 43.28(g). We are done.
43.5 Overriding the delete Method 43-
Figure 43.28 Delete elements from a red-black tree.
15
20
34
(^3 )
27
50
15
20
34
(^3 )
27
null
15
20
27
3 16 25 34
(a) Delete 50 (b) Case 1.2 (c) Delete 20
15
16
27
3 16 25 34
15
16
27
(^3) null 25 34
15
16
27
3 25 34
(d) Copy 16 to replace 20 (e) Case 2 (f) Delete 15
3
16
27
25 34
null
16
27
25 34
16
27
34
null 25
(g) Delete 3 (h) Case 3 (i) Case 2
16
27
34
25
16
27
34
27
null 34
(j) Delete 25 (k) Delete 16 (l) Case 2
27
34
27 root: null
(m) Delete 34 (n) Delete 27 (o) Empty tree
After deleting 25 , the new tree is as shown in Figure 43.28(j). Now delete 16. Apply Case
2, as shown in Figure 43.28(k). The new tree is shown in Figure 43.28(l).
After deleting 34 , the new tree is as shown in Figure 43.28(m).
After deleting 27 , the new tree is as shown in Figure 43.28(n).
43.6 Implementing RBTree Class 43-
41 // v is the parent of of u, if exists 42 RBTreeNode v = (u == root)? null : 43 (RBTreeNode)(path.get(i − 1 )); 44 45 u.setRed(); // It is OK to set u red 46 47 if (u == root) // If e is inserted as the root, set root black 48 u.setBlack(); 49 else if (v.isRed()) 50 fixDoubleRed(u, v, path, i); // Fix double-red violation at u 51 } 52 53 /** Fix double-red violation at node u */ 54 private void fixDoubleRed(RBTreeNode u, RBTreeNode v, 55 ArrayList<TreeNode> path, int i) { 56 // w is the grandparent of u 57 RBTreeNode w = (RBTreeNode)(path.get(i − 2 )); 58 RBTreeNode parentOfw = (w == root)? null : 59 (RBTreeNode)path.get(i – 3 ); 60 61 // Get v's sibling named x 62 RBTreeNode x = (w.left == v)? 63 (RBTreeNode)(w.right) : (RBTreeNode)(w.left); 64 65 if (x == null || x.isBlack()) { 66 // Case 1: v's sibling x is black 67 if (w.left == v && v.left == u) { 68 // Case 1.1: u < v < w, Restructure and recolor nodes 69 restructureRecolor(u, v, w, w, parentOfw); 70 71 w.left = v.right; // v.right is y3 in Figure 43. 72 v.right = w; 73 } 74 else if (w.left == v && v.right == u) { 75 // Case 1.2: v < u < w, Restructure and recolor nodes 76 restructureRecolor(v, u, w, w, parentOfw); 77 v.right = u.left; 78 w.left = u.right; 79 u.left = v; 80 u.right = w; 81 } 82 else if (w.right == v && v.right == u) { 83 // Case 1.3: w < v < u, Restructure and recolor nodes 84 restructureRecolor(w, v, u, w, parentOfw); 85 w.right = v.left; 86 v.left = w; 87 } 88 else { 89 // Case 1.4: w < u < v, Restructure and recolor nodes 90 restructureRecolor(w, u, v, w, parentOfw); 91 w.right = u.left; 92 v.left = u.right; 93 u.left = w; 94 u.right = v; 95 } 96 } 97 else { // Case 2: v's sibling x is red 98 // Recolor nodes 99 w.setRed(); 100 u.setRed();
43-20 Chapter 43 Red-Black Trees
101 ((RBTreeNode)(w.left)).setBlack(); 102 ((RBTreeNode)(w.right)).setBlack(); 103 104 if (w == root) { 105 w.setBlack(); 106 } 107 else if (((RBTreeNode)parentOfw).isRed()) { 108 // Propagate along the path to fix new double-red violation 109 u = w; 110 v = (RBTreeNode)parentOfw; 111 fixDoubleRed(u, v, path, i − 2 ); // i – 2 propagates upward 112 } 113 } 114 } 115 116 /** Connect b with parentOfw and recolor a, b, c for a < b < c / 117 private void restructureRecolor(RBTreeNode a, RBTreeNode b, 118 RBTreeNode c, RBTreeNode w, RBTreeNode parentOfw) { 119 if (parentOfw == null) 120 root = b; 121 else if (parentOfw.left == w) 122 parentOfw.left = b; 123 else 124 parentOfw.right = b; 125 126 b.setBlack(); // b becomes the root in the subtree 127 a.setRed(); // a becomes the left child of b 128 c.setRed(); // c becomes the right child of b 129 } 130 131 @Override /* Delete an element from the RBTree. 132 * Return true if the element is deleted successfully 133 * Return false if the element is not in the tree */ 134 public boolean delete(E e) { 135 // Locate the node to be deleted 136 TreeNode current = root; 137 while (current != null ) { 138 if (e.compareTo(current.element) < 0 ) { 139 current = current.left; 140 } 141 else if (e.compareTo(current.element) > 0 ) { 142 current = current.right; 143 } 144 else 145 break ; // Element is in the tree pointed by current 146 } 147 148 if (current == null ) 149 return false; // Element is not in the tree 150 151 java.util.ArrayList<TreeNode> path; 152 153 // current node is an internal node 154 if (current.left != null && current.right != null ) { 155 // Locate the rightmost node in the left subtree of current 156 TreeNode rightMost = current.left; 157 while (rightMost.right != null ) { 158 rightMost = rightMost.right; // Keep going to the right 159 } 160