typedef int KEY_TYPE; // 未来修改节点内容数据类型时可以方便修改
#define BSTREE_ENTRY(name, type) \
struct name { \
struct type* left; \
struct type* right; \
} \
/* 二叉排序树的节点 */
struct bstree_node{
KEY_TYPE key; // 用于判断大小的Key
void* value; // 存储值
BSTREE_ENTRY(ready, bstee_node) bst;
}
/* 二叉排序树 */
struct bstree{
struct bstree_node* root;
}
使用 BSTREE_ENTRY 的优点是可以方便地为二叉树节点添加多个不同的子树指针,而不需要重复地写 struct type* left; struct type* right; 这样的代码
这样可以节省代码量,提高可读性,也方便修改和扩展
/* 创建节点 */
bstree_node* bstree_create_node(KEY_TYPE key) {
bstree_node* node = (bstree_node*)malloc(sizeof(bstree_node));
if(node == nullptr) assert(0);
node->key = key;
node->left = node->right = nullptr;
return node;
}
/* 删除节点 */
void bstree_destroy_node(bstree_node* node) {
if(node == nullptr) return;
node->left = node->right = nullptr;
free(node);
}
上面创建的是一个普通的二叉排序树,在最坏的情况下二叉排序树会退化成一个链表
平衡二叉树为每个节点添加了左右子树的高度;红黑树为每个节点增加了一个颜色(红或黑)
红黑树的5个规则
红黑树的叶子节点是指空节点,也就是没有实际数据的节点,所有叶子节点都隐藏,并且为黑色
#define RED 1
#define BLACK 2
typedef int KEY_TYPE;
/* 红黑树的节点 */
typedef struct _rbtree_node {
unsigned char color;
struct _rbtree_node *right;
struct _rbtree_node *left;
struct _rbtree_node *parent;
KEY_TYPE key; // Key值 用于排序和查找
void *value; // Value 用于存储值
} rbtree_node;
/* 红黑树 */
typedef struct _rbtree {
rbtree_node *root;
rbtree_node *nil;
} rbtree;
还记得前面说的吗? 红黑树的叶子节点是指空节点,也就是没有实际数据的节点,也就是说所有 _rbtree_node 的叶子节点其实都是指向 _rbtree.nil 空节点的
通过上面的代码可以定义出一个红黑树和红黑树节点,但是红黑树麻烦的点在于节点的添加与删除,在添加节点与删除节点时需要保证依然符合上面的五个规则,因此在添加/删除节点时会旋转红黑树来保证符合规则
什么时候需要旋转?
因为红黑树插入节点的颜色是红色的,所以当新红黑树不符合规则时需要通过旋转来符合规则
插入了新红色节点的红黑树应该如何变色呢?
不同的情况,有以下几种可能的变色方法:
旋转分为两种情况:左旋转、右旋转
void rbtree_left_rotate(rbtree *T, rbtree_node *x) {
rbtree_node *y = x->right;
x->right = y->left; //1 1
if (y->left != T->nil) { //1 2
y->left->parent = x;
}
y->parent = x->parent; //1 3
if (x->parent == T->nil) { //1 4
T->root = y;
} else if (x == x->parent->left) {
x->parent->left = y;
} else {
x->parent->right = y;
}
y->left = x; //1 5
x->parent = y; //1 6
}
void rbtree_right_rotate(rbtree *T, rbtree_node *y) {
rbtree_node *x = y->left;
y->left = x->right;
if (x->right != T->nil) {
x->right->parent = y;
}
x->parent = y->parent;
if (y->parent == T->nil) {
T->root = x;
} else if (y == y->parent->right) {
y->parent->right = x;
} else {
y->parent->left = x;
}
x->right = y;
y->parent = x;
}