红黑树学习路线
Author:yangzhichao passion_yang_008.163.com
网上有很多博客讲红黑树,有的讲的很清晰,有的不清晰,就算你找到一个有各种插图,讲的很清晰的博客,但对于首次接触红黑树的你来说,只是看文字说明和插图来书,还是有点吃力,所以本人亲自总结了一套学习计划,保证你在四天之内学完红黑树(如果你看本人的博客,还学不会,本人挥刀自宫),并能写出实用的c语言代码。
- 首先建议观看关于如何构建红黑树,了解什么是平衡二叉树,对于不平衡的二叉树,如何将它调整为平衡二叉树,构建红黑树等视频,视频链接地址如下:
https://edu.csdn.net/course/detail/26202
- 在旧金山大学的算法学习网站上模拟红黑树的插入,删除,旋转等操作,这个对你后面编写红黑树来说非常重要,链接地址如下:
https://www.cs.usfca.edu/~galles/visualization/RedBlack.html
- 当学习玩如何构建红黑树之后,请查看博客系列:
https://www.cnblogs.com/skywang12345/p/3624177.html#a1
- 认真查看下面的代码,保证你学会红黑树,你会感谢我的,嘿嘿。
红黑树的性质
-
节点是红色或者黑色。
-
根节点是黑色
-
每个叶子节点都是黑色的空节点
-
每个红色节点的两个叶子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)。
-
从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
红黑树和平衡二叉树的区别
-
红黑树放弃了追求完全的平衡,只只求大致的平衡,在与平衡二叉树的时间复杂度相差不大的情况下,保证每次插入最多只需要三次旋转就能达到平衡,实现起来比较简单。
-
平衡二叉树追求绝对的平衡,条件苛刻,实现起来比较麻烦,每次插入新结点之后需要旋转的次数不能预知。
红黑树的实现
- rb_tree.h 头文件
#pragma once
#ifndef _RB_TREE_H
#define _RB_TREE_H
#define RB_RED 0 /* 红色节点 */
#define RB_BLACK 1 /* 黑色节点 */
typedef struct RBTreeNode
{
unsigned char color;
int key;
struct RBTreeNode * left;
struct RBTreeNode * right;
struct RBTreeNode * parent;
}rb_node,*rb_node_t;
typedef struct rb_root
{
rb_node * node;
}rb_root;
rb_root * create_rbtree();
void print_rbtree(rb_root * root);
void insert_rbtree(rb_root *root,rb_node * node);
void rbtree_delete(rb_root *root,rb_node * node);
void rbtree_inorder(rb_node * node);
rb_node * rbtree_search(rb_root *root, int key);
#endif /* _RB_TREE_H */
- 红黑树插入后着色和调整思想
现象说明 | 处理策略 | |
---|---|---|
Case 1 | 当前节点的父节点是红色,且当前节点的祖父节点的另一个子节点(叔叔节点)也是红色。 | (01) 将“父节点”设为黑色。 (02) 将“叔叔节点”设为黑色。 (03) 将“祖父节点”设为“红色”。 (04) 将“祖父节点”设为“当前节点”(红色节点);即,之后继续对“当前节点”进行操作。 |
Case 2 | 当前节点的父节点是红色,叔叔节点是黑色,且当前节点是其父节点的右孩子 | (01) 将“父节点”作为“新的当前节点”。 (02) 以“新的当前节点”为支点进行左旋。 |
Case 3 | 当前节点的父节点是红色,叔叔节点是黑色,且当前节点是其父节点的左孩子 | (01) 将“父节点”设为“黑色”。 (02) 将“祖父节点”设为“红色”。 (03) 以“祖父节点”为支点进行右旋。 |
- 红黑树删除节点思想
现象说明 | 处理策略 | |
---|---|---|
Case 1 | x是"黑+黑"节点,x的兄弟节点是红色。(此时x的父节点和x的兄弟节点的子节点都是黑节点)。 | (01) 将x的兄弟节点设为“黑色”。 (02) 将x的父节点设为“红色”。 (03) 对x的父节点进行左旋。 (04) 左旋后,重新设置x的兄弟节点。 |
Case 2 | x是“黑+黑”节点,x的兄弟节点是黑色,x的兄弟节点的两个孩子都是黑色。 | (01) 将x的兄弟节点设为“红色”。 (02) 设置“x的父节点”为“新的x节点”。 |
Case 3 | x是“黑+黑”节点,x的兄弟节点是黑色;x的兄弟节点的左孩子是红色,右孩子是黑色的。 | (01) 将x兄弟节点的左孩子设为“黑色”。 (02) 将x兄弟节点设为“红色”。 (03) 对x的兄弟节点进行右旋。 (04) 右旋后,重新设置x的兄弟节点。 |
Case 4 | x是“黑+黑”节点,x的兄弟节点是黑色;x的兄弟节点的右孩子是红色的,x的兄弟节点的左孩子任意颜色。 | (01) 将x父节点颜色 赋值给 x的兄弟节点。 (02) 将x父节点设为“黑色”。 (03) 将x兄弟节点的右子节设为“黑色”。 (04) 对x的父节点进行左旋。 (05) 设置“x”为“根节点”。 |
- 红黑树实现 rb_tree.c
#include "rb_tree.h"
#include <stdio.h>
#define MAXSIZE 1024
#define rb_is_red(x) ((x)->color == RB_RED)
#define rb_is_black(x) ((x)->color == RB_BLACK)
#define rb_set_black(x) do{(x)->color = RB_BLACK;}while(0)
#define rb_set_red(x) do{(x)->color = RB_RED;}while(0)
rb_root * create_rbtree()
{
rb_root * root = (rb_root *)malloc(sizeof(rb_root));
root->node = NULL;
return root;
}
/* 中序非递归遍历算法 */
void rbtree_inorder(rb_node * node)
{
rb_node *buf[MAXSIZE];
rb_node * p = NULL;
int top = -1;
p = node;
while (-1 != top || NULL != p) {
/*判断左子树是否为空,将所有的左子树入栈 */
while (NULL != p) {
buf[++top] = p;
p = p->left;
}
if(-1 != top){ /* 判断栈中是否有元素 */
p = buf[top--];/* 出栈打印元素 */
printf("\t %d\t %s \n", p->key, (p->color == 1 ? "black" : "red"));
p = p->right;
}
}
}
void print_rbtree(rb_root * root)
{
if (root->node)
inorder(root->node);
}
void rbtree_right_rotate(rb_root *root, rb_node * node)
{
rb_node * x = node->left;
/* 先将x 的右孩子给node */
node->left = x->right;
/* 让node给 x的右子树当爸爸 */
if(NULL != x->right)
x->right->parent = node;
/*将 node的爸爸给 x当爸爸 */
x->parent = node->parent;
/* 判断node是不是没有爸爸 */
if (NULL == node->parent) {
root->node = x;
}
else {
/* 查看 node是自己父亲的左儿子还是右儿子*/
if (node == node->parent->left)
node->parent->left = x;
else
node->parent->right = x;
}
/*由于是右旋,所以node现在变成了自己左儿子的有儿子 */
x->right = node;
/* node当初自己的右儿子变成了自己的爸爸 乱伦呀 */
node->parent = x;
}
void rbtree_left_rotate(rb_root *root, rb_node * node)
{
rb_node * x = node->right;
/*先拿出被旋转的两个元素 */
/* 先将可能发生冲突的两个元素处理,x的左子树旋转过后会变为 node的右子树 */
node->right = x->left;
/* 判断存在 */
if (NULL != x->left)
x->left->parent = node;
/* 将 node的父亲交给 x */
x->parent = node->parent;
/*判断父亲是都存在 */
if (NULL == node->parent) {
root->node = x;
}
else {
/*判断 node是父亲的左子树还是父亲的右子树 */
if (node == node->parent->right)
node->parent->right = x;
else
node->parent->left = x;
}
/* node 变成了自己有孩子的 左孩子 */
x->left = node;
/* 现在 node自己的右孩子x变成了自己的爸爸 */
node->parent = x;
}
void rbtree_insert_fixup(rb_root *root, rb_node * node)
{
rb_node *parent = NULL;
rb_node *gparent = NULL;
while ((parent = node->parent) && rb_is_red(parent)) {
gparent = parent->parent;
/* 如果 parent 是 gparent的左子树 */
if (parent == gparent->left) {
/* 第一种情况,当叔叔节点是红色时 */
{
rb_node * uncle = gparent->right;
if (uncle && rb_is_red(uncle)) {
rb_set_black(parent);
rb_set_black(uncle);
rb_set_red(gparent);
node = gparent;
continue;
}
}
/*
* px px
* / \ / \
* x ub y ub
* / \ --(左旋)--> / \
* lx y x ry
* / \ / \
* ly ry lx ly
* 恰好是第三种情况
*/
/* 当前节点是父亲节点的右子树时,先将这种情况化简为第三种情况 */
if (node == parent->right) {
rb_node * tmp;
rbtree_left_rotate(root, parent);
tmp = parent;
parent = node;
node = tmp;
}
rb_set_black(parent);
rb_set_red(gparent);
rbtree_right_rotate(root, gparent);
}
/* parent 是gparent 的右孩子 */
else {
/*第一种情况 */
{
rb_node * uncle = gparent->left;
if (uncle && rb_is_red(uncle)) {
rb_set_black(uncle);
rb_set_black(parent);
rb_set_red(gparent);
node = gparent;
continue;
}
}
/*处理第二种情况,方法是:旋转为第三种情况 */
if (node == parent->left) {
rb_node * tmp;
rbtree_right_rotate(root, parent);
tmp = parent;
parent = node;
node = tmp;
}
/* 处理第三种情况 */
rb_set_black(parent);
rb_set_red(gparent);
rbtree_left_rotate(root,gparent);
}
}
/* 将根节点染成黑色 */
root->node->color = RB_BLACK;
}
void insert_rbtree(rb_root *root, rb_node * node)
{
rb_node * y = NULL;
rb_node * x = root->node;
/* 在哪里插入 */
while(x != NULL){
y = x;
if (node->key < x->key)
x = x->left;
else
x = x->right;
}
/* 插入操作 */
node->parent = y;
/*判断是否是头结点 */
if (NULL == y)
root->node = node;
else {
/* 判断是大于当前值还是小于当前值 */
if (node->key < y->key)
y->left = node;
else
y->right = node;
}
/* 默认插入为红色 */
node->color = RB_RED;
/* 插入调整颜色和位置 */
rbtree_insert_fixup(root,node);
}
static void
rbtree_delete_fixup(rb_root *root, rb_node * node, rb_node * parent)
{
rb_node * other = NULL;
/*分为两种情况,node不是头结点 */
while((!node || rb_is_black(node)) && (node != root->node)){
/*node是parent的左子树*/
if (node == parent->left) {
/* 先判断兄弟节点 */
other = parent->right;
/* 兄弟节点是红色 case 1: */
if (rb_is_red(other)) {
rb_set_black(other);
rb_set_red(parent);
rbtree_left_rotate(root,parent);
other = parent->right;
}
/* 兄弟节点是黑色,并且兄弟节点的两个孩子也是黑色 case 2:*/
if (((!other->left) || rb_is_black(other->left)) &&
(!other->right || rb_is_black(other->right))) {
rb_set_red(other);
node = parent;
parent = node->parent;
}
else {
if (!other->right || rb_is_black(other->right)) {
/*兄弟节点是黑色 左孩子是红色右孩纸是黑色 case 3:*/
rb_set_black(other->left);
rb_set_red(other);
rbtree_right_rotate(root,other);
other = parent->right;
}
/*兄弟节点是黑色 左孩子是黑色右孩纸是红色 case 4*/
other->color = parent->color;
rb_set_black(parent);
rb_set_black(other->right);
rbtree_left_rotate(root,parent);
node = root->node;
}
}
/* node 是parent 的右子树 */
else {
other = parent->left;
/* 第一种情况 */
if (rb_is_red(other)) {
rb_set_black(other);
rb_set_red(parent);
rbtree_right_rotate(root,parent);
other = parent->left;
}
/* case 2*/
if (((!other->left) || rb_is_black(other->left)) &&
(!other->right || rb_is_black(other->right))) {
rb_set_red(other);
node = parent;
parent = node->parent;
}
else {
if (!other->left || rb_is_black(other->left)) {
/*兄弟节点是黑色 右孩子是红色左孩纸是黑色 case 3:*/
rb_set_black(other->right);
rb_set_red(other);
rbtree_left_rotate(root, other);
other = parent->left;
}
/* case 4*/
other->color = parent->color;
rb_set_black(parent);
rb_set_black(other->left);
rbtree_right_rotate(root, parent);
node = root->node;
}
}
}
/*node 是头结点 */
if (node)
rb_set_black(node);
}
void rbtree_delete(rb_root *root, rb_node * node)
{
rb_node * child, *parent;
unsigned char color;
/* 如果删除的左子树和右子树都不为空 */
if ((NULL != node->left) && (NULL != node->right)) {
/* 查找替换元素 */
rb_node * replace = node;/*记录下node节点 */
replace = replace->left;
while (NULL != replace->right)
replace = replace->right;
/* 查找到replace 后 */
parent = node->parent;//记录node父节点
if (parent) {
/*父节点存在,判断node是左子树还是右子树 */
if (node == parent->right)
parent->right = replace;
else
parent->left = replace;
}
else
root->node = replace;
/*记录replace的父节点 */
parent = replace->parent;
/*记录replace的左孩子*/
child = replace->left;
/* 记录replace的颜色 */
color = replace->color;
/*判断 parent 是不是和 node 相等 */
if (parent == node)
parent = replace;
else {
/*替代节点的 孩子存在,也就是替代节点的 左子树存在 */
if (child) {
child->parent = parent;
}
/*replace的孩子(左子树)成为了replace父节点的右孩子*/
parent->right = child;
/*将 replace和 parent链接起来,replace成为了 parent的父节点 */
replace->left = node->left;
node->left->parent = replace;
}
/*将node中的信息copy到replace中*/
replace->color = node->color;
replace->parent = node->parent;
replace->right = node->right;
node->right->parent = replace;
if (color == RB_BLACK) {
/* 对删除node后,颜色调整 */
rbtree_delete_fixup(root,child,parent);
}
free(node);
return;
}
/* 如果左子树为空 */
if (NULL != node->left)
child = node->left;
else
child = node->right;
/* 记录当前节点的父节点 */
parent = node->parent;
/* 记录当前节点的颜色 */
color = node->color;
/*判断儿子节点是否为空 */
if(child)
child->parent = parent;
/* 判断 node 是不是头结点 */
if (NULL != node->parent) {
/* 查看 node 是父节点的左子树还是右子树 */
if (node == parent->left)
parent->left = child;
else
parent->right = child;
}
else
/* 当前孩子变为头结点 */
root->node = child;
if (color == RB_BLACK) {
/* 对删除node后,颜色调整 */
rbtree_delete_fixup(root, child, parent);
}
free(node);
}
rb_node * rbtree_search(rb_root *root, int key)
{
rb_node * node = root->node;
while(NULL != node){
if (key < node->key)
node = node->left;
else if (key > node->key)
node = node->right;
else
return node;
}
return NULL;
}
测试程序
#include <stdio.h>
#include "rb_tree.h"
void insert(rb_root * root,int key)
{
rb_node * node = (rb_node *)malloc(sizeof(rb_node));
memset(node, 0x00, sizeof(node));
node->key = key;
node->left = NULL;
node->parent = NULL;
node->right = NULL;
insert_rbtree(root, node);
}
rb_root * create(rb_root * root, int * array,int n)
{
int i = 0;
for (i = 0; i < n; ++i) {
insert(root, array[i]);
}
return root;
}
void delete(rb_root * root,int key)
{
rb_node * node = rbtree_search(root,key);
if (node)
rbtree_delete(root,node);
}
int main(int argc, char * argv[])
{
int array[] = {40,10,5,50,45,60,25,74,36,7,3,1,4,8,9};
rb_root *root = create_rbtree();
create(root,array,sizeof(array)/sizeof(array[0]));
print_rbtree(root);
printf("\n-----------------------------------\n");
delete(root, 45);
print_rbtree(root);
printf("\n-----------------------------------\n");
delete(root, 8);
print_rbtree(root);
printf("\n-----------------------------------\n");
delete(root, 5);
print_rbtree(root);
printf("\n-----------------------------------\n");
delete(root, 10);
print_rbtree(root);
printf("\n-----------------------------------\n");
delete(root, 9);
print_rbtree(root);
printf("\n-----------------------------------\n");
delete(root, 36);
print_rbtree(root);
printf("\n-----------------------------------\n");
delete(root, 74);
print_rbtree(root);
return 0;
}