常见的查找算法(五):树表查找之二 ---- 红黑树

时间:2019-08-30
本文章向大家介绍常见的查找算法(五):树表查找之二 ---- 红黑树,主要包括常见的查找算法(五):树表查找之二 ---- 红黑树使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

红黑树是每个节点都带有颜色属性的二叉查找树,颜色为 红色 或 黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:

  1. 节点是红色黑色
  2. 黑色
  3. 所有叶子都是黑色(叶子是NIL节点)。
  4. 每个红色节点必须有两个黑色子节点。(从每个叶子的所有路径上不能有两个连续的红色节点。)
  5. 任一节点其每个叶子的所有简单路径都包含相同数目的黑色节点。

下面是一个具体的红黑树的图例:

旋转 

 旋转是一种能保持二叉搜索树性质的搜索树局部操作。其中两种旋转分别为左旋右旋

 在某个结点 x 上进行左旋时,假设它的右孩子为y而不是树的 T.nil 结点;x为其右孩子而不是 T.nil 结点的树内任意节点。

 左旋以 x 到 y 的链为“支轴”进行,使得 y 成为该子树的新的根节点,x 成为 y 的左孩子,y 的左孩子变成 x 的右孩子;右旋与此相反。

左旋代码:

 1     /**
 2      * 左旋
 3      * 左旋示意图(对节点x进行左旋):
 4      *     px                               px
 5      *     /                                /
 6      *     x                                y
 7      *    / \          --(左旋)-->         /  \
 8      *   lx   y                           x   ry
 9      *    / \                           /  \
10      *  ly  ry                        lx   ly
11      *
12      * @param x
13      */
14     private void leftRotate(RBTNode<T> x) {
15         RBTNode<T> y = x.right; // y是x的右节点
16 
17         x.right = x.left;       // 把x的左节点变为x的右节点
18         // 若y有左子节点,把y的左节点的父节点换成x
19         if (y.left != null) {
20             y.left.parent = x;
21         }
22 
23         y.parent = x.parent;    // y的父节点(原来是x )设为x的父节点
24 
25         // 若x是根节点,y直接变根节点
26         if (x.parent == null) {
27             this.mRoot = y;
28         } else {
29             if (x.parent.left == x) {
30                 x.parent.left = y;  // 如果x是x父节点的左孩子,把x的父节点的左孩子指向y
31             } else {
32                 x.parent.right = y; // 如果x是x父节点的右孩子,把x的父节点的右孩子指向y
33             }
34         }
35 
36         y.left = x;     // 将y的左节点指向x
37         x.parent = y;   // 将x的父节点设为y
38     }

右旋代码:

 1     /**
 2      * 右旋,操作和左旋相反
 3      * 右旋示意图(对节点y进行左旋):
 4      *      py                              py
 5      *     /                                /
 6      *     y                                x
 7      *    / \          --(右旋)-->         /  \
 8      *   x  ry                           lx   y
 9      *   / \                                  / \
10      *  lx  rx                               rx  ry
11      *
12      * @param y
13      */
14     private void rightRotate(RBTNode<T> y) {
15         RBTNode<T> x = y.left;  // y的左孩子
16 
17         y.left = x.right;
18         if (x.right != null) {
19             x.right.parent = y;
20         }
21 
22         x.parent = y.parent;
23 
24         if (y.parent == null) {
25             this.mRoot = x;
26         } else {
27             if (y.parent.left == y) {
28                 y.parent.left = x;
29             } else {
30                 y.parent.right = x;
31             }
32         }
33 
34         x.right = y;
35         y.parent = x;
36     }

红黑树新结点插入代码:

就像一个普通的二叉搜索树一样,将新结点插入树中,并将其着为红色。之后为了能保证红黑的性质,还需要一个辅助代码对结点重新着色并且旋转。

 1     /**
 2      * 插入操作
 3      *
 4      * @param node
 5      */
 6     private void insert(RBTNode<T> node) {
 7         int result;
 8         RBTNode<T> y = null;
 9         RBTNode<T> x = this.mRoot;
10 
11         // 查找树中插入点的父结点y的位置
12         while (x != null) {
13             y = x;  // 注意这里,y不是空的
14             result = node.key.compareTo(x.key);
15             if (result < 0) {
16                 x = x.left;
17             } else {
18                 x = x.right;
19             }
20         }
21 
22         node.parent = y;
23         if (y != null) {
24             result = node.key.compareTo(y.key);
25             if (result < 0) {
26                 y.left = node;
27             } else {
28                 y.right = node;
29             }
30         } else {
31             this.mRoot = node;
32         }
33 
34         node.color = RED;
35 
36         // 插入后修正树
37         insertFixUp(node);
38     }

辅助修正函数:

 1     /**
 2      * 红黑树插入修正函数
 3      *
 4      * @param node
 5      */
 6     private void insertFixUp(RBTNode<T> node) {
 7         RBTNode<T> parent, gparent; // 父节点,祖父节点
 8 
 9         while (((parent = parentOf(node)) != null) && isRed(parent)) {
10             gparent = parentOf(parent);
11 
12             // 父节点是祖父节点的左孩子
13             if (parent == gparent.left) {
14                 RBTNode<T> uncle = gparent.right; // 叔叔节点,祖父的右节点
15 
16                 // ① 叔叔节点是红色的
17                 if ((uncle != null) && isRed(uncle)) {
18                     node.setColor(BLACK);
19                     parent.setColor(BLACK);
20                     gparent.setColor(RED);
21                     node = gparent;
22                     continue;
23                 }
24 
25                 // ② 叔叔是黑色,且当前节点是右孩子
26                 if (parent.right == node) {
27                     RBTNode<T> tmp;
28                     leftRotate(parent);
29                     tmp = parent;
30                     parent = node;
31                     node = tmp;
32                 }
33 
34                 // ③ 叔叔是黑色,且当前节点是左孩子
35                 parent.setColor(BLACK);
36                 gparent.setColor(RED);
37                 rightRotate(gparent);
38 
39             } else {    // 父节点是祖父节点的右孩子
40 
41                 RBTNode<T> uncle = gparent.left; // 叔叔节点,祖父的左节点
42 
43                 // ① 叔叔节点是红色的
44                 if ((uncle != null) && isRed(uncle)) {
45                     uncle.setColor(BLACK);
46                     parent.setColor(BLACK);
47                     gparent.setColor(RED);
48                     node = gparent;
49                     continue;
50                 }
51 
52                 // ② 叔叔是黑色,且当前节点是左孩子
53                 if (parent.left == node) {
54                     RBTNode<T> tmp;
55                     rightRotate(parent);
56                     tmp = parent;
57                     parent = node;
58                     node = tmp;
59                 }
60 
61                 // ③ 叔叔是黑色,且当前节点是右孩子
62                 parent.setColor(BLACK);
63                 gparent.setColor(RED);
64                 leftRotate(gparent);
65             }
66         }
67 
68         this.mRoot.setColor(BLACK);
69     }

修正过程实例:

以下图中的 z 为插入后的结点,y 表示叔结点uncle,图中的每个子树的低端的节点是红黑树代码中的边界,边界中每个节点有黑色的哨兵没有画出来。

下面是介绍的是上面代码中 父节点是祖父节点的左孩子 的代码。

先看图中的第一个树,插入的 z 结点和 z.parent 父节点都是 RED,这违反了性质四。

情况 1(得到的是图中的第二个树):由于图中的第一个树中叔结点是红色,z 结点和 z.parent 父节点都是 RED结点都要被重新着色,并沿着指针 z 上升;

情况 2(得到的是图中的第三个树):由于图中的第二个树中 z 及其父节点 z.parent 都为红色,其叔结点为黑色,左旋父节点 z.parent后得到;

情况 3(得到的是图中的第四个树):z 是其父节点的左孩子,重新着色后右旋的到图中的第四个树,这样之后就是合法的红黑树了。

分析红黑树的插入时间复杂度:

一颗具有 n 个节点的红黑树高度为O(log n),则按照一个普通的二叉查找树的方式插入结点需要花费 O(log n);修正代码中,当情况 1发生,指针 z沿着树上升2层,才会执行 while 循环,while 循环可能执行的总次数为 O(log n)。所以红黑树的插入的总的时间复杂度为 O(log n)。此外,插入算法中总的来说旋转次数不超过 2 次。

红黑树的删除:

 1     /**
 2      * 删除树中某个节点
 3      * @param node
 4      */
 5     private void remove(RBTNode<T> node) {
 6         RBTNode<T> child, parent;
 7         boolean color;
 8 
 9         if ((node.left != null) && (node.right != null)) {
10             RBTNode<T> replace = node;
11 
12             replace = replace.right;
13             while (replace.left != null) {
14                 replace = replace.left;
15             }
16 
17             if (parentOf(node) != null) {
18                 if (parentOf(node).left == node) {
19                     parentOf(node).left = replace;
20                 } else {
21                     parentOf(node).right = replace;
22                 }
23             } else {
24                 this.mRoot = replace;
25             }
26 
27             child = replace.right;
28             parent = parentOf(replace);
29             color = replace.color;
30 
31             if (parent == node) {
32                 parent = replace;
33             } else {
34                 if (child != null)
35                     child.setParent(parent);
36                 parent.left = child;
37 
38                 replace.right = node.right;
39                 node.right.setParent(replace);
40             }
41 
42             replace.parent = node.parent;
43             replace.color = node.color;
44             replace.left = node.left;
45             node.left.parent = replace;
46 
47             if (color == BLACK)
48                 removeFixup(child, parent);
49 
50             node = null;
51             
52             return;
53         }
54 
55         if (node.left != null) {
56             child = node.left;
57         } else {
58             child = node.right;
59         }
60 
61         parent = node.parent;
62         color = node.color;
63 
64         if (child != null)
65             child.parent = parent;
66 
67         if (parent != null) {
68             if (parent.left == node) {
69                 parent.left = child;
70             } else {
71                 parent.right = child;
72             }
73         } else {
74             this.mRoot = child;
75         }
76 
77         if (color == BLACK)
78             removeFixup(child, parent);
79         node = null;
80     }
View Code

删除修正函数:

 1     /**
 2      * 删除修正函数
 3      * @param x
 4      * @param parent
 5      */
 6     private void removeFixup(RBTNode<T> x, RBTNode<T> parent) {
 7         RBTNode<T> w;
 8 
 9         while ((x == null || isBlack(x)) && (x != this.mRoot)) {
10             if (parent.left == x) {
11                 w = parent.right;
12 
13                 // ① x的兄弟结点w是红色的
14                 if (isRed(w)) {
15                     w.setColor(BLACK); // w染黑
16                     parent.setColor(RED);
17                     leftRotate(parent);
18                     w = parent.right;
19                 }
20 
21                 // ② x的兄弟结点w是黑色的,而且w的两个孩子结点都是黑色的
22                 if ((w.left == null || isBlack(w.left)) &&
23                         (w.right == null || isBlack(w.right))) {
24                     w.setColor(RED);
25                     x = parent;
26                     parent = parentOf(x);
27 
28                 } else {
29 
30                     // ③ x的兄弟结点w是黑色的,w的右孩子是黑色的,w的左孩子是红色的
31                     if ((w.right == null || isBlack(w.right)) &&
32                             (w.left == null || isBlack(w.left))) {
33                         w.left.setColor(BLACK);
34                         w.setColor(RED);
35                         rightRotate(w);
36                         w = parent.right;
37                     }
38 
39                     // ④ x的兄弟结点w是黑色的,w的右孩子是红色的
40                     w.setColor(parent.color);
41                     parent.setColor(BLACK);
42                     w.right.setColor(BLACK);
43                     leftRotate(parent);
44                     x = this.mRoot;
45 
46                     break;
47                 }
48 
49             } else {
50 
51                 w = parent.left;
52                 // ① x的兄弟结点w是红色的
53                 if (isRed(w)) {
54                     w.setColor(BLACK);
55                     parent.setColor(RED);
56                     rightRotate(parent);
57                     w = parent.left;
58                 }
59 
60                 // ②  x的兄弟结点w是黑色的,而且w的两个孩子结点都是黑色的
61                 if ((w.left == null || isBlack(w.left)) &&
62                         (w.right == null || isBlack(w.right))) {
63                     w.setColor(RED);
64                     x = parent;
65                     parent = parentOf(x);
66                 } else {
67 
68                     // ③ x的兄弟结点w是黑色的,w的右孩子是黑色的,w的左孩子是红色的
69                     if (w.left == null || isBlack(w.left)) {
70                         w.right.setColor(BLACK);
71                         w.setColor(RED);
72                         leftRotate(w);
73                         w = parent.left;
74                     }
75 
76                     // ④ x的兄弟结点w是黑色的,w的右孩子是红色的
77                     w.setColor(parent.color);
78                     parent.setColor(BLACK);
79                     w.left.setColor(BLACK);
80                     rightRotate(parent);
81                     x = this.mRoot;
82 
83                     break;
84                 }
85             }
86         }
87 
88         if (x != null)
89             x.setColor(BLACK);
90     }
View Code

红黑树总的代码(包含测试代码):

  1 package tree;
  2 
  3 /**
  4  * @program: MyPractice
  5  * @description: 红黑树
  6  * @author: Mr.Wu
  7  * @create: 2019-08-28 17:19
  8  **/
  9 public class RBTree<T extends Comparable<T>> {
 10     // 根节点
 11     private RBTNode<T> mRoot;
 12 
 13     private static final boolean RED = false;
 14     private static final boolean BLACK = true;
 15 
 16     private static final int a[] = {10, 40, 30, 60, 90, 70, 20, 50, 80};
 17     private static final boolean mDebugInsert = true;    // "插入"动作的检测开关(false,关闭;true,打开)
 18 
 19     /**
 20      * 左旋
 21      * 左旋示意图(对节点x进行左旋):
 22      * px                               px
 23      * /                                /
 24      * x                                y
 25      * / \          --(左旋)-->         /  \
 26      * lx   y                           x   ry
 27      * / \                        /  \
 28      * ly  ry                     lx   ly
 29      *
 30      * @param x
 31      */
 32     private void leftRotate(RBTNode<T> x) {
 33         RBTNode<T> y = x.right; // y是x的右节点
 34 
 35         x.right = x.left;       // 把x的左节点变为x的右节点
 36         // 若y有左子节点,把y的左节点的父节点换成x
 37         if (y.left != null) {
 38             y.left.parent = x;
 39         }
 40 
 41         y.parent = x.parent;    // y的父节点(原来是x )设为x的父节点
 42 
 43         // 若x是根节点,y直接变根节点
 44         if (x.parent == null) {
 45             this.mRoot = y;
 46         } else {
 47             if (x.parent.left == x) {
 48                 x.parent.left = y;  // 如果x是x父节点的左孩子,把x的父节点的左孩子指向y
 49             } else {
 50                 x.parent.right = y; // 如果x是x父节点的右孩子,把x的父节点的右孩子指向y
 51             }
 52         }
 53 
 54         y.left = x;     // 将y的左节点指向x
 55         x.parent = y;   // 将x的父节点设为y
 56     }
 57 
 58     /**
 59      * 右旋,操作和左旋相反
 60      * 右旋示意图(对节点y进行左旋):
 61      * py                              py
 62      * /                                /
 63      * y                                x
 64      * / \          --(右旋)-->         /  \
 65      * x  ry                           lx   y
 66      * / \                                  / \
 67      * lx  rx                               rx  ry
 68      *
 69      * @param y
 70      */
 71     private void rightRotate(RBTNode<T> y) {
 72         RBTNode<T> x = y.left;  // y的左孩子
 73 
 74         y.left = x.right;
 75         if (x.right != null) {
 76             x.right.parent = y;
 77         }
 78 
 79         x.parent = y.parent;
 80 
 81         if (y.parent == null) {
 82             this.mRoot = x;
 83         } else {
 84             if (y.parent.left == y) {
 85                 y.parent.left = x;
 86             } else {
 87                 y.parent.right = x;
 88             }
 89         }
 90 
 91         x.right = y;
 92         y.parent = x;
 93     }
 94 
 95     /**
 96      * 新建节点
 97      *
 98      * @param key
 99      */
100     public void insert(T key) {
101         RBTNode<T> node = new RBTNode<>(BLACK, key, null, null, null);
102         if (node != null) {
103             insert(node);
104         }
105     }
106 
107     /**
108      * 插入操作
109      *
110      * @param node
111      */
112     private void insert(RBTNode<T> node) {
113         int result;
114         RBTNode<T> y = null;
115         RBTNode<T> x = this.mRoot;
116 
117         // 查找树中插入点的父结点y的位置
118         while (x != null) {
119             y = x;  // 注意这里,y不是空的
120             result = node.key.compareTo(x.key);
121             if (result < 0) {
122                 x = x.left;
123             } else {
124                 x = x.right;
125             }
126         }
127 
128         node.parent = y;
129         if (y != null) {
130             result = node.key.compareTo(y.key);
131             if (result < 0) {
132                 y.left = node;
133             } else {
134                 y.right = node;
135             }
136         } else {
137             this.mRoot = node;
138         }
139 
140         node.color = RED;
141 
142         // 插入后修正树
143         insertFixUp(node);
144     }
145 
146     /**
147      * 红黑树插入修正函数
148      *
149      * @param node
150      */
151     private void insertFixUp(RBTNode<T> node) {
152         RBTNode<T> parent, gparent; // 父节点 与 祖父节点
153 
154         while (((parent = parentOf(node)) != null) && isRed(parent)) {
155             gparent = parentOf(parent);
156 
157             // 父节点是祖父节点的左孩子
158             if (parent == gparent.left) {
159                 RBTNode<T> uncle = gparent.right; // 叔叔节点,祖父的右节点
160 
161                 // ① 叔叔节点是红色的
162                 if ((uncle != null) && isRed(uncle)) {
163                     node.setColor(BLACK);
164                     parent.setColor(BLACK);
165                     gparent.setColor(RED);
166                     node = gparent;
167                     continue;
168                 }
169 
170                 // ② 叔叔是黑色,且当前节点是右孩子
171                 if (parent.right == node) {
172                     RBTNode<T> tmp;
173                     leftRotate(parent);
174                     tmp = parent;
175                     parent = node;
176                     node = tmp;
177                 }
178 
179                 // ③ 叔叔是黑色,且当前节点是左孩子
180                 parent.setColor(BLACK);
181                 gparent.setColor(RED);
182                 rightRotate(gparent);
183 
184             } else {    // 父节点是祖父节点的右孩子
185 
186                 RBTNode<T> uncle = gparent.left; // 叔叔节点,祖父的左节点
187 
188                 // ① 叔叔节点是红色的
189                 if ((uncle != null) && isRed(uncle)) {
190                     uncle.setColor(BLACK);
191                     parent.setColor(BLACK);
192                     gparent.setColor(RED);
193                     node = gparent;
194                     continue;
195                 }
196 
197                 // ② 叔叔是黑色,且当前节点是左孩子
198                 if (parent.left == node) {
199                     RBTNode<T> tmp;
200                     rightRotate(parent);
201                     tmp = parent;
202                     parent = node;
203                     node = tmp;
204                 }
205 
206                 // ③ 叔叔是黑色,且当前节点是右孩子
207                 parent.setColor(BLACK);
208                 gparent.setColor(RED);
209                 leftRotate(gparent);
210             }
211         }
212 
213         this.mRoot.setColor(BLACK);
214     }
215 
216     private boolean isRed(RBTNode<T> node) {
217         return (node != null) && (node.color == RED);
218     }
219 
220     private boolean isBlack(RBTNode<T> node) {
221         return !isRed(node);
222     }
223 
224     private RBTNode<T> parentOf(RBTNode<T> node) {
225         return (node != null) ? node.parent : null;
226     }
227 
228     public void clear() {
229         destroy(mRoot);
230         mRoot = null;
231     }
232 
233     private void destroy(RBTNode<T> tree) {
234         if (tree == null) {
235             return;
236         }
237         if (tree.left != null) {
238             destroy(tree.left);
239         }
240         if (tree.right != null) {
241             destroy(tree.right);
242         }
243 
244         tree = null;
245     }
246 
247     public void preOrder() {
248         preOrder(mRoot);
249     }
250 
251     private void preOrder(RBTNode<T> tree) {
252         if (tree != null) {
253             System.out.print(tree.key + "");
254             preOrder(tree.left);
255             preOrder(tree.right);
256         }
257     }
258 
259     public void print() {
260         if (mRoot != null) {
261             print(mRoot, mRoot.key, 0);
262         }
263     }
264 
265     private void print(RBTNode<T> tree, T key, int direction) {
266         if (tree != null) {
267             if (direction == 0) {
268                 System.out.printf("%2d(B) is root\n", tree.key);
269             } else {
270                 System.out.printf("%2d(%s) is %2d's %6s child\n", tree.key, isRed(tree) ? "R" : "B",
271                         key, direction == 1 ? "right" : "left");
272             }
273             print(tree.left, tree.key, -1);
274             print(tree.right, tree.key, 1);
275         }
276     }
277 
278     public static void main(String[] args) {
279         int i, ilen = a.length;
280         RBTree<Integer> tree = new RBTree<>();
281 
282         System.out.printf("== 原始数据: ");
283         for (i = 0; i < ilen; i++)
284             System.out.printf("%d ", a[i]);
285         System.out.printf("\n");
286 
287         for (i = 0; i < ilen; i++) {
288             tree.insert(a[i]);
289             // 设置mDebugInsert=true,测试"添加函数"
290             if (mDebugInsert) {
291                 System.out.printf("== 添加节点: %d\n", a[i]);
292                 System.out.printf("== 树的详细信息: \n");
293                 tree.print();
294                 System.out.printf("\n");
295             }
296         }
297 
298         System.out.printf("== 前序遍历: ");
299         tree.preOrder();
300 
301 //        System.out.printf("\n== 中序遍历: ");
302 //        tree.inOrder();
303 //
304 //        System.out.printf("\n== 后序遍历: ");
305 //        tree.postOrder();
306         System.out.printf("\n");
307 
308         System.out.printf("== 最小值: %s\n", tree.minimum());
309         System.out.printf("== 最大值: %s\n", tree.maximum());
310         System.out.printf("== 树的详细信息: \n");
311         tree.print();
312         System.out.printf("\n");
313 
314         // 销毁二叉树
315         tree.clear();
316     }
317 
318     private RBTNode<T> maximum(RBTNode<T> tree) {
319         if (tree == null)
320             return null;
321         while (tree.right != null)
322             tree = tree.right;
323         return null;
324     }
325 
326     public T maximum() {
327         RBTNode<T> p = maximum(mRoot);
328         if (p != null)
329             return p.key;
330         return null;
331     }
332 
333     private RBTNode<T> minimum(RBTNode<T> tree) {
334         if (tree == null)
335             return null;
336         while (tree.left != null)
337             tree = tree.left;
338         return tree;
339     }
340 
341     public T minimum() {
342         RBTNode<T> p = minimum(mRoot);
343         if (p != null)
344             return p.key;
345         return null;
346     }
347 
348     public class RBTNode<T extends Comparable<T>> {
349         boolean color;   // 颜色
350         T key;           // 关键字
351         RBTNode<T> left;
352         RBTNode<T> right;
353         RBTNode<T> parent;
354 
355         public RBTNode(boolean color, T key, RBTNode<T> left, RBTNode<T> right, RBTNode<T> parent) {
356             this.color = color;
357             this.key = key;
358             this.left = left;
359             this.right = right;
360             this.parent = parent;
361         }
362 
363         public boolean isColor() {
364             return color;
365         }
366 
367         public void setColor(boolean color) {
368             this.color = color;
369         }
370 
371         public T getKey() {
372             return key;
373         }
374 
375         public void setKey(T key) {
376             this.key = key;
377         }
378 
379         public RBTNode<T> getLeft() {
380             return left;
381         }
382 
383         public void setLeft(RBTNode<T> left) {
384             this.left = left;
385         }
386 
387         public RBTNode<T> getRight() {
388             return right;
389         }
390 
391         public void setRight(RBTNode<T> right) {
392             this.right = right;
393         }
394 
395         public RBTNode<T> getParent() {
396             return parent;
397         }
398 
399         public void setParent(RBTNode<T> parent) {
400             this.parent = parent;
401         }
402     }
403 }
View Code

(未完待续)

原文地址:https://www.cnblogs.com/magic-sea/p/11390892.html