forked from bangoc/cgen
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrb.h
395 lines (371 loc) · 14.8 KB
/
rb.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
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
/*
(C) 2021 Nguyen Ba Ngoc (bangoc)
Cài đặt khái quát của cây đỏ-đen,
tương thích với các hàm cho cây nhị phân và cây nhị phân tìm kiếm
*/
#ifndef RBI_H_
#define RBI_H_
#include "bn.h"
#include "bns.h"
#include <stdbool.h>
/*
* Các tính chất của cây đỏ đen:
* 1) Mỗi nút chỉ có thể là đỏ hoặc đen
* 2) Nút gốc là nút đen
* 3) Tất cả các nút lá (NULL) là các nút đen
* 4) Cả hai con của nút đỏ là các nút đen
* 5) Tất cả các đường đi đơn giản từ nút gốc tới các nút lá đều có
* cùng số lượng nút đen.
*/
// đỏ = 0, đen = 1 như vậy chúng ta có tổng giá trị mầu = số lượng nút đen
typedef enum {
RB_RED = 0,
RB_BLACK = 1
} rb_node_color_t;
static const char * color_names[] = {"Đỏ", "Đen"};
typedef struct rb_node {
struct bn_node bn_node;
rb_node_color_t color;
} *rb_node_t;
/*
Trong triển khai này NULL được sử dụng thay vì lính canh để tương
thích tốt hơn với các hàm ngoại.*
Nút NULL được quy ước là nút đen
*/
// ========== Khai báo hàm ===============
static rb_node_t rb_create_node();
static bn_node_t rb_insert(bn_tree_t t,
bn_node_t node, bn_node_t *loc, bn_node_t parent);
static int rb_delete(bn_tree_t t, bn_node_t z);
// ========== Macro viết nhanh ===========
#define to_rb(n) ((rb_node_t)n)
#define rb_color(n) (n? to_rb(n)->color: RB_BLACK)
#define rb_color_str(n) color_names[(int)rb_color(n)]
#define rb_set_color(n, new_color) to_rb(n)->color = new_color
#define rb_is_red(node) (rb_color(node) == RB_RED)
#define rb_is_black(node) (rb_color(node) == RB_BLACK)
#define rb_set_black(node) rb_set_color(node, RB_BLACK)
#define rb_set_red(node) rb_set_color(node, RB_RED)
// ========== Định nghĩa hàm =============
static rb_node_t rb_create_node() {
// Mặc định giá trị 0 là đỏ
return calloc(1, sizeof(struct rb_node));
}
static void rb_insert_fixup(bn_tree_t t, bn_node_t n, bn_node_t p) {
/*
* Các biến:
* t - con trỏ tới cây (tree)
* n - ban đầu là nút mới được thêm vào (node)
* p - là đỉnh của n (parent, n->top)
* u - nút đối xứng của p trong cây, chú bác của n (uncle)
* gp - ông của n, là đỉnh của p (grandparent, p->top)
*
* Trong các ví dụ minh họa thì nút có tên được viết hoa là nút đen,
* nút có tên viết thường là nút đỏ, nút có thể là đen hoặc đỏ
* (không ảnh hưởng đển tính đúng đắn) thì được đặt trong dấu ()
*/
while (true) {
/* Các bất biến của vòng lặp:
* + p là đỉnh của n, tính chất cây đỏ đen chỉ bị vi phạm ở đoạn
* p-n: n và p là các nút đỏ (tính chất 4). Vấn đề này được
* khắc phục trong quá trình n được đẩy lên phía gốc.
* Ban đầu n là nút mới được thêm vào, sau mỗi vòng lặp n tiến
* gần hơn về phía gốc của cây. Vòng lặp dừng lại khi p ==
* NULL_PTR (n là gốc của cây) hoặc p được tô mầu đen.
*
* Trong vòng lặp chúng ta có
* + n->top != NULL_PTR và p->top != NULL_PTR (bởi vì n và p
* là các nút đỏ)
*/
if (p == p->top->left) {
#define IMPL_INSERT_FIXUP(left, right) \
bn_node_t u = p->top->right; \
if (rb_is_red(u)) { \
/* GP gp <- n mới \
p u thành>>> P U \
->n <- có thể vi phạm tính chất 4 nếu gp->top là đỏ,\
n có thể là con trái hoặc con phải của p \
*/ \
rb_set_black(p); \
rb_set_black(u); \
rb_set_red(p->top); \
n = p->top; \
p = n->top; \
if (p == NULL_PTR) { \
/* n là gốc của cây */ \
rb_set_black(n); \
break; \
} \
if (rb_is_black(p)) { \
/* Các tính chất đã được thỏa mãn */ \
break; \
} \
} else { \
if (n == n->top->right) { \
/* GP GP \
p U thành>>> n <-p U \
n p <-n mới \
*/ \
bn_ ##left ##_rotate(t, p); \
n = p; \
p = n->top; \
} \
/* \
+ n là con trái của p \
GP gp \
p U lật mầu >> P U \
n n \
>>> & sau khi xoay phải ở GP thành =>>> \
P \
n gp \
U \
Thỏa mãn các tính chất của cây đỏ đen \
*/ \
rb_set_color(p, RB_BLACK); \
rb_set_color(p->top, RB_RED); \
bn_ ##right ##_rotate(t, p->top); \
break; \
}
IMPL_INSERT_FIXUP(left, right)
} else {
IMPL_INSERT_FIXUP(right, left)
}
}
}
#undef IMPL_INSERT_FIXUP
static bn_node_t rb_insert(bn_tree_t t,
bn_node_t node, bn_node_t *loc, bn_node_t parent) {
*loc = node;
if (parent == NULL_PTR) {
// loc là gốc của cây
rb_set_black(node);
} else {
node->top = parent;
// Hàm tạo cần đảm bảo node là nút đỏ
if (rb_is_red(parent)) {
// vi phạm tính chất 4 (sau thao tác thêm vào chỉ có tính chất 4)
// có thể bị vi phạm.
rb_insert_fixup(t, node, parent);
}
}
return node;
}
static void rb_delete_fix_color(bn_tree_t t, bn_node_t parent) {
bn_node_t node = NULL, sibling,
cn, // Con của sibling ở phía gần node (close nephew)
dn; // Con của sibling ở phía xa node (distant nephew)
while (true) {
/*
* Các tính chất bất biến trong vòng lặp:
* - node là nút đen (hoặc NULL trong lần lặp đầu tiên)
* - node không phải là nút gốc (top của nó khác NULL_PTR)
* - Tất cả các đường dẫn tới lá đi qua parent va node có số
* lượng nút đen ít hơn 1 so với các đường dẫn khác.
*/
sibling = parent->right;
if (node != sibling) { // node == parent->left
#define ERASE_COLOR_SYMMETRY(left, right) \
/* Trong các ký hiệu cây chữ cái đầu viết thường là nút đỏ, \
* chữ cái đầu viết hoa là nút đen, \
* nút được để trong ngoặc có thể là đỏ hoặc đen. \
*/ \
if (rb_is_red(sibling)) { \
/* \
* Trường hợp 1 - Xoay trái ở parent \
* \
* P S \
* / \ / \ \
* N s --> p Dn \
* / \ / \ \
* Cn Dn N Cn <- sibling mới \
*/ \
bn_ ##left ##_rotate(t, parent); \
rb_set_red(parent); \
rb_set_black(sibling); \
sibling = parent->right; \
} \
dn = sibling->right; \
if (rb_is_black(dn)) { \
cn = sibling->left; \
if (rb_is_black(cn)) { \
/* \
* Trường hợp 2 - Đảo mầu sibling, p có thể có mầu bất kỳ \
* \
* (p) (p) \
* / \ / \ \
* N S --> N s \
* / \ / \ \
* Cn Dn Cn Dn \
* \
* Điều này dẫn tới vi phạm ràng buộc 5), vi phạm này có \
* thể được khắc phục bằng cách đảo mầu p thành đen nếu nó \
* là nút đỏ, hoặc đệ quy tại p nếu ngược lại. Nút p có \
* mầu đỏ sau khi xử lý trường hợp 1. \
*/ \
rb_set_color(sibling, RB_RED); \
if (rb_is_red(parent)) { \
rb_set_black(parent); \
} else { \
node = parent; \
parent = node->top; \
if (parent) { \
continue; \
} \
} \
break; \
} \
/* \
* Trường hợp 3 - Xoay phải tại sibling (p có thể có mầu bất \
* kỳ) \
* \
* (p) (p) \
* / \ / \ \
* N S --> N cn \
* / \ \ \
* cn Dn S \
* \ \
* Dn \
* Lưu ý: + p có thể là nút đỏ, và nếu như vậy thì cả p và \
* Cn đều là các nút đỏ sau khi xoay (vi phạm ràng buộc 4). \
* \
* + Đường đi từ p qua cn sau đó rẽ về phía N bị giảm một \
* nút đen (S, vi phạm tính chất 5). \
* \
* Các vấn đề này được xử lý trong trường hợp 4: Sau khi \
* xoay trái tại parent, cn được tô bằng mầu của p, dn và p \
* được tô mầu đen) \
* \
* (p) (cn) \
* / \ / \ \
* N cn --> P S \
* \ / \ \
* S N Dn \
* \ \
* Dn \
*/ \
bn_ ##right ##_rotate(t, sibling); \
sibling = parent->right; \
} \
/* Trường hợp 4 - Xoay trái ở parent + đảo mầu các nút \
* (p và cn có thể có mầu bất kỳ trước khi xoay. Sau khi xoay \
* p và dn được tô mầu đen, s có mầu của p, \
* còn cn giữ nguyênmầu của nó) \
* \
* (p) (s) \
* / \ / \ \
* N S --> P Dn \
* / \ / \ \
* (cn) dn N (cn) \
*/ \
dn = sibling->right; \
bn_ ##left ##_rotate(t, parent); \
rb_set_color(sibling, rb_color(parent)); \
rb_set_black(parent); \
rb_set_black(dn); \
break
ERASE_COLOR_SYMMETRY(left, right);
} else {
sibling = parent->left;
ERASE_COLOR_SYMMETRY(right, left);
#undef ERASE_COLOR_SYMMETRY
}
}
}
#define rb_set_parent_color(n, parent, color) \
n->top = parent; \
rb_set_color(n, color)
static int rb_delete(bn_tree_t t, bn_node_t node) {
bn_node_t child = node->right,
tmp = node->left,
parent, rebalance;
bn_node_t p;
rb_node_color_t c;
if (!tmp) {
/* Trường hợp 1: Nếu nút đang xóa có không quá 1 nút con (dễ)
*
* Nếu có một con thì nút con phải là nút đỏ do tính chất 5,
* và nó phải là nút đen theo tính chất 4. Chúng ta điều chỉnh
* mầu trong lân cận để tránh gọi hàm sửa mầu sau này.
*/
p = node->top;
c = rb_color(node);
parent = p;
bn_change_child(node, child, parent, t);
if (child) {
rb_set_parent_color(child, p, c);
rebalance = NULL_PTR;
} else {
rebalance = c == RB_BLACK? parent: NULL_PTR;
}
tmp = parent;
} else if (!child) {
// Vẫn trường hợp 1 nhưng nút con là node->left
p = node->top;
c = rb_color(node);
rb_set_parent_color(tmp, p, c);
parent = p;
bn_change_child(node, tmp, parent, t);
rebalance = NULL_PTR;
tmp = parent;
} else {
bn_node_t successor = child, child2;
tmp = child->left;
if (!tmp) {
/* Trường hợp 2: Nút liền sau node là con phải của node.
*
* (n) (s)
* / \ / \
* (x) (s) -> (x) (c)
* \
* (c)
*/
parent = successor;
child2 = successor->right;
} else {
/* Trường hợp 3: Nút liền sau node là nút trái nhất trong
* cây con phải của node
*
* (n) (s)
* / \ / \
* (x) (y) -> (x) (y)
* / /
* (p) (p)
* / /
* (s) (c)
* \
* (c)
*/
do {
parent = successor;
successor = tmp;
tmp = tmp->left;
} while (tmp);
child2 = successor->right;
parent->left = child2;
successor->right = child;
child->top = successor;
}
tmp = node->left;
successor->left = tmp;
tmp->top = successor;
p = node->top;
c = rb_color(node);
tmp = p;
bn_change_child(node, successor, tmp, t);
if (child2) {
rb_set_parent_color(child2, parent, RB_BLACK);
rebalance = NULL_PTR;
} else {
rb_node_color_t c2 = rb_color(successor);
rebalance = c2 == RB_BLACK? parent: NULL_PTR;
}
rb_set_parent_color(successor, p, c);
tmp = successor;
}
if (rebalance) {
rb_delete_fix_color(t, rebalance);
}
return 1;
}
#undef rb_set_parent_color
#endif // RBI_H_