返回 LeetCode 刷题

Markdown File

红黑树自底向上插入示例代码

红黑树自底向上插入示例代码.md

11. 结点结构
2#include <stdio.h>
3#include <stdlib.h>
4
5#define RED 1
6#define BLACK 0
7
8struct RBNode {
9 int key;
10 int color;
11 struct RBNode* left;
12 struct RBNode* right;
13 struct RBNode* parent;
14};
15
16这里比普通 BST 多了两个东西:
17
18color:颜色
19parent:父指针,方便往上找父亲、爷爷、叔叔
20
212. 创建新结点
22struct RBNode* createNode(int key)
23{
24 struct RBNode* node =
25 malloc(sizeof(struct RBNode));
26
27 node->key = key;
28 node->color = RED; // 新插入结点默认红色
29 node->left = NULL;
30 node->right = NULL;
31 node->parent = NULL;
32
33 return node;
34}
35
36注意:
37
38node->color = RED;
39
40因为新节点染红不增加黑高。
41
423. 左旋
43
44左旋处理 RR 或 RL 变形之后的情况。
45
46void leftRotate(struct RBNode** root, struct RBNode* x)
47{
48 struct RBNode* y = x->right;
49
50 x->right = y->left;
51
52 if(y->left != NULL)
53 {
54 y->left->parent = x;
55 }
56
57 y->parent = x->parent;
58
59 if(x->parent == NULL)
60 {
61 *root = y;
62 }
63 else if(x == x->parent->left)
64 {
65 x->parent->left = y;
66 }
67 else
68 {
69 x->parent->right = y;
70 }
71
72 y->left = x;
73 x->parent = y;
74}
75
76可以理解成:
77
78x 往左下沉
79y 上来当新的子树根
80
81形状:
82
83 x
84 \
85 y
86
87左旋后:
88
89 y
90 /
91 x
92
934. 右旋
94void rightRotate(struct RBNode** root, struct RBNode* y)
95{
96 struct RBNode* x = y->left;
97
98 y->left = x->right;
99
100 if(x->right != NULL)
101 {
102 x->right->parent = y;
103 }
104
105 x->parent = y->parent;
106
107 if(y->parent == NULL)
108 {
109 *root = x;
110 }
111 else if(y == y->parent->left)
112 {
113 y->parent->left = x;
114 }
115 else
116 {
117 y->parent->right = x;
118 }
119
120 x->right = y;
121 y->parent = x;
122}
123
124形状:
125
126 y
127 /
128 x
129
130右旋后:
131
132 x
133 \
134 y
135
1365. BST 插入
137
138先按普通 BST 插进去。
139
140void bstInsert(struct RBNode** root, struct RBNode* node)
141{
142 struct RBNode* parent = NULL;
143 struct RBNode* cur = *root;
144
145 while(cur != NULL)
146 {
147 parent = cur;
148
149 if(node->key < cur->key)
150 {
151 cur = cur->left;
152 }
153 else
154 {
155 cur = cur->right;
156 }
157 }
158
159 node->parent = parent;
160
161 if(parent == NULL)
162 {
163 *root = node;
164 }
165 else if(node->key < parent->key)
166 {
167 parent->left = node;
168 }
169 else
170 {
171 parent->right = node;
172 }
173}
174
175这一步只保证:
176
177左 < 根 < 右
178
179颜色还没修。
180
1816. 插入修复 fixInsert
182
183这是红黑树插入最核心。
184
185void fixInsert(struct RBNode** root, struct RBNode* node)
186{
187 while(node != *root &&
188 node->parent->color == RED)
189 {
190 struct RBNode* parent = node->parent;
191 struct RBNode* grand = parent->parent;
192
193 if(parent == grand->left)
194 {
195 struct RBNode* uncle = grand->right;
196
197 if(uncle != NULL && uncle->color == RED)
198 {
199 parent->color = BLACK;
200 uncle->color = BLACK;
201 grand->color = RED;
202
203 node = grand;
204 }
205 else
206 {
207 if(node == parent->right)
208 {
209 node = parent;
210 leftRotate(root, node);
211
212 parent = node->parent;
213 grand = parent->parent;
214 }
215
216 parent->color = BLACK;
217 grand->color = RED;
218 rightRotate(root, grand);
219 }
220 }
221 else
222 {
223 struct RBNode* uncle = grand->left;
224
225 if(uncle != NULL && uncle->color == RED)
226 {
227 parent->color = BLACK;
228 uncle->color = BLACK;
229 grand->color = RED;
230
231 node = grand;
232 }
233 else
234 {
235 if(node == parent->left)
236 {
237 node = parent;
238 rightRotate(root, node);
239
240 parent = node->parent;
241 grand = parent->parent;
242 }
243
244 parent->color = BLACK;
245 grand->color = RED;
246 leftRotate(root, grand);
247 }
248 }
249 }
250
251 (*root)->color = BLACK;
252}
2537. 这段代码怎么对应你刚才的理解
254情况一:父亲在爷爷左边
255 G
256 /
257 P
258 /
259 X
260
261代码进入:
262
263if(parent == grand->left)
264
265叔叔是:
266
267struct RBNode* uncle = grand->right;
268叔叔红
269if(uncle != NULL && uncle->color == RED)
270{
271 parent->color = BLACK;
272 uncle->color = BLACK;
273 grand->color = RED;
274
275 node = grand;
276}
277
278就是
279父黑
280叔黑
281爷红
282当前 = 爷爷
283继续往上修
284叔叔黑:先处理 LR
285
286如果:
287
288 G
289 /
290 P
291 \
292 X
293
294也就是 LR。
295
296代码:
297
298if(node == parent->right)
299{
300 node = parent;
301 leftRotate(root, node);
302
303 parent = node->parent;
304 grand = parent->parent;
305}
306
307先对 P 左旋,把 LR 变成 LL。
308
309然后统一按 LL 处理:
310
311parent->color = BLACK;
312grand->color = RED;
313rightRotate(root, grand);
314
315就是:
316
317父亲变黑
318爷爷变红
319对爷爷右旋
3208. 父亲在爷爷右边
321
322这个是完全对称的。
323
324 G
325 \
326 P
327 \
328 X
329
330代码进入:
331
332else
333{
334 struct RBNode* uncle = grand->left;
335
336如果叔叔红:
337
338父黑
339叔黑
340爷红
341当前=爷爷
342
343如果叔叔黑:
344
345先处理 RL,再统一 RR。
346
3479. 外部插入函数
348void insertRB(struct RBNode** root, int key)
349{
350 struct RBNode* node = createNode(key);
351
352 bstInsert(root, node);
353
354 fixInsert(root, node);
355}
356
357完整流程就是:
358
359创建红节点
360BST 插入
361红黑树修复
362
36310. 简单测试打印中序
364void inorder(struct RBNode* root)
365{
366 if(root == NULL)
367 {
368 return;
369 }
370
371 inorder(root->left);
372
373 printf("%d(%s) ",
374 root->key,
375 root->color == RED ? "R" : "B");
376
377 inorder(root->right);
378}
37911. main 示例
380int main()
381{
382 struct RBNode* root = NULL;
383
384 insertRB(&root, 10);
385 insertRB(&root, 20);
386 insertRB(&root, 30);
387 insertRB(&root, 15);
388 insertRB(&root, 5);
389
390 inorder(root);
391
392 return 0;
393}
394
395可能输出类似:
396
3975(R) 10(B) 15(R) 20(B) 30(B)
398
399具体颜色可能因为插入序列和实现细节有差异,但必须满足红黑树性质。
400
40112. 核心代码
402
403
404
405while(node != *root &&
406 node->parent->color == RED)
407{
408 if(parent 是 grand 的左孩子)
409 {
410 uncle = grand->right;
411
412 if(uncle 是红)
413 {
414 parent黑;
415 uncle黑;
416 grand红;
417 node = grand;
418 }
419 else
420 {
421 if(node 是 parent 的右孩子)
422 {
423 对 parent 左旋;
424 }
425
426 parent黑;
427 grand红;
428 对 grand 右旋;
429 }
430 }
431 else
432 {
433 完全对称;
434 }
435}
436
437root黑;
438
439
440叔叔红:变色并向上继续。
441叔叔黑:先把 LR/RL 转成 LL/RR,再旋转,最后父黑爷红。
Rendered Preview
  1. 结点结构
    #include <stdio.h>
    #include <stdlib.h>

#define RED 1
#define BLACK 0

struct RBNode {
int key;
int color;
struct RBNode* left;
struct RBNode* right;
struct RBNode* parent;
};

这里比普通 BST 多了两个东西:

color:颜色
parent:父指针,方便往上找父亲、爷爷、叔叔

  1. 创建新结点
    struct RBNode* createNode(int key)
    {
    struct RBNode* node =
    malloc(sizeof(struct RBNode));

    node->key = key;
    node->color = RED; // 新插入结点默认红色
    node->left = NULL;
    node->right = NULL;
    node->parent = NULL;

    return node;
    }

注意:

node->color = RED;

因为新节点染红不增加黑高。

  1. 左旋

左旋处理 RR 或 RL 变形之后的情况。

void leftRotate(struct RBNode** root, struct RBNode* x)
{
struct RBNode* y = x->right;

x->right = y->left;

if(y->left != NULL)
{
    y->left->parent = x;
}

y->parent = x->parent;

if(x->parent == NULL)
{
    *root = y;
}
else if(x == x->parent->left)
{
    x->parent->left = y;
}
else
{
    x->parent->right = y;
}

y->left = x;
x->parent = y;

}

可以理解成:

x 往左下沉
y 上来当新的子树根

形状:

x
 \
  y

左旋后:

  y
 /
x

4. 右旋
void rightRotate(struct RBNode** root, struct RBNode* y)
{
struct RBNode* x = y->left;

y->left = x->right;

if(x->right != NULL)
{
    x->right->parent = y;
}

x->parent = y->parent;

if(y->parent == NULL)
{
    *root = x;
}
else if(y == y->parent->left)
{
    y->parent->left = x;
}
else
{
    y->parent->right = x;
}

x->right = y;
y->parent = x;

}

形状:

  y
 /
x

右旋后:

x
 \
  y

5. BST 插入

先按普通 BST 插进去。

void bstInsert(struct RBNode** root, struct RBNode* node)
{
struct RBNode* parent = NULL;
struct RBNode* cur = *root;

while(cur != NULL)
{
    parent = cur;

    if(node->key < cur->key)
    {
        cur = cur->left;
    }
    else
    {
        cur = cur->right;
    }
}

node->parent = parent;

if(parent == NULL)
{
    *root = node;
}
else if(node->key < parent->key)
{
    parent->left = node;
}
else
{
    parent->right = node;
}

}

这一步只保证:

左 < 根 < 右

颜色还没修。

  1. 插入修复 fixInsert

这是红黑树插入最核心。

void fixInsert(struct RBNode** root, struct RBNode* node)
{
while(node != root &&
node->parent->color == RED)
{
struct RBNode
parent = node->parent;
struct RBNode* grand = parent->parent;

    if(parent == grand->left)
    {
        struct RBNode* uncle = grand->right;

        if(uncle != NULL && uncle->color == RED)
        {
            parent->color = BLACK;
            uncle->color = BLACK;
            grand->color = RED;

            node = grand;
        }
        else
        {
            if(node == parent->right)
            {
                node = parent;
                leftRotate(root, node);

                parent = node->parent;
                grand = parent->parent;
            }

            parent->color = BLACK;
            grand->color = RED;
            rightRotate(root, grand);
        }
    }
    else
    {
        struct RBNode* uncle = grand->left;

        if(uncle != NULL && uncle->color == RED)
        {
            parent->color = BLACK;
            uncle->color = BLACK;
            grand->color = RED;

            node = grand;
        }
        else
        {
            if(node == parent->left)
            {
                node = parent;
                rightRotate(root, node);

                parent = node->parent;
                grand = parent->parent;
            }

            parent->color = BLACK;
            grand->color = RED;
            leftRotate(root, grand);
        }
    }
}

(*root)->color = BLACK;

}
7. 这段代码怎么对应你刚才的理解
情况一:父亲在爷爷左边
G
/
P
/
X

代码进入:

if(parent == grand->left)

叔叔是:

struct RBNode* uncle = grand->right;
叔叔红
if(uncle != NULL && uncle->color == RED)
{
parent->color = BLACK;
uncle->color = BLACK;
grand->color = RED;

node = grand;

}

就是
父黑
叔黑
爷红
当前 = 爷爷
继续往上修
叔叔黑:先处理 LR

如果:

    G
   /
  P
   \
    X

也就是 LR。

代码:

if(node == parent->right)
{
node = parent;
leftRotate(root, node);

parent = node->parent;
grand = parent->parent;

}

先对 P 左旋,把 LR 变成 LL。

然后统一按 LL 处理:

parent->color = BLACK;
grand->color = RED;
rightRotate(root, grand);

就是:

父亲变黑
爷爷变红
对爷爷右旋
8. 父亲在爷爷右边

这个是完全对称的。

G
 \
  P
   \
    X

代码进入:

else
{
struct RBNode* uncle = grand->left;

如果叔叔红:

父黑
叔黑
爷红
当前=爷爷

如果叔叔黑:

先处理 RL,再统一 RR。

  1. 外部插入函数
    void insertRB(struct RBNode** root, int key)
    {
    struct RBNode* node = createNode(key);

    bstInsert(root, node);

    fixInsert(root, node);
    }

完整流程就是:

创建红节点
BST 插入
红黑树修复

  1. 简单测试打印中序
    void inorder(struct RBNode* root)
    {
    if(root == NULL)
    {
    return;
    }

    inorder(root->left);

    printf("%d(%s) ",
    root->key,
    root->color == RED ? "R" : "B");

    inorder(root->right);
    }

  2. main 示例
    int main()
    {
    struct RBNode* root = NULL;

    insertRB(&root, 10);
    insertRB(&root, 20);
    insertRB(&root, 30);
    insertRB(&root, 15);
    insertRB(&root, 5);

    inorder(root);

    return 0;
    }

可能输出类似:

5(R) 10(B) 15(R) 20(B) 30(B)

具体颜色可能因为插入序列和实现细节有差异,但必须满足红黑树性质。

  1. 核心代码

while(node != *root &&
node->parent->color == RED)
{
if(parent 是 grand 的左孩子)
{
uncle = grand->right;

    if(uncle 是红)
    {
        parent黑;
        uncle黑;
        grand红;
        node = grand;
    }
    else
    {
        if(node 是 parent 的右孩子)
        {
            对 parent 左旋;
        }

        parent黑;
        grand红;
        对 grand 右旋;
    }
}
else
{
    完全对称;
}

}

root黑;

叔叔红:变色并向上继续。
叔叔黑:先把 LR/RL 转成 LL/RR,再旋转,最后父黑爷红。