红黑树(上)
(C语言实现)\
文章目录
一、平衡条件
- 节点非黑既红
- 根节点是黑色
- 叶子(NIL)结点是黑色
- 红色节点下面接两个黑色节点
- 从根节点到叶子结点路径上,黑色节点数量相同
平衡条件的认识
第4条和第5条条件,注定了,红黑树中最长路径是最短路径的长度的 2 倍。
本质上,红黑树也是通过树高来控制平衡的。
红黑树比 AVL 树树高控制条件要更松散,红黑树在发生节点插入和删除以后,发生调整的概率,比 AVL 树要更小。
二、学习诀窍
- 理解红黑树的插入调整,要站在祖父节点向下进行调整
- 理解红黑树的删除调整,要站在父节点向下进行调整
- 插入调整,主要就是为了解决双红情况
- 新插入的节点一定是红色,插入黑色节点一定会产生冲突,违反条件5,插入红色节点,不一定产生冲突
- 把每一种情况,想象成一棵大的红黑树中的局部子树
- 局部调整的时候,为了不影响全局,调整前后的路径上黑色节点数量相同
三、插入策略
- 叔叔节点为红色的时候,修改三元组小帽子,改成红黑黑
- 叔叔节点为黑色的时候,参考 AVL 树的失衡情况,分成 L L , L R , R L , R R LL,LR,RL,RR LL,LR,RL,RR, 先参考 AVL 树的旋转调整策略,然后再修改三元组的颜色,有两种调整策略:红色上浮,红色下沉。
- 两大类情况,包含 8 种小情况
四、代码演示
- 插入调整,发正在递归的回溯阶段
- 插入调整代码中,使用 goto 语句,8行代码,变成了4行
- 处理根节点一定是黑色,通过代码封装, i n s e r t − > _ _ i n s e r t insert->\_\_insert insert−>__insert
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef struct Node {
int key;
int color; // 0 red, 1 black, 2 double black
struct Node *lchild, *rchild;
} Node;
Node __NIL;
#define NIL (&__NIL)
__attribute__((constructor))
void init_NIL() {
NIL->key = -1;
NIL->color = 1;
NIL->lchild = NIL->rchild = NIL;
return ;
}
Node *getNewNode(int key) {
Node *p = (Node *)malloc(sizeof(Node));
p->key = key;
p->color = 0;
p->lchild = p->rchild = NIL;
return p;
}
int has_red_child(Node *root) {
return root->lchild->color == 0 || root->rchild->color == 0;
}
Node *left_rotate(Node *root) {
printf("left rotate : %d\n", root->key);
Node *temp = root->rchild;
root->rchild = temp->lchild;
temp->lchild = root;
return temp;
}
Node *right_rotate(Node *root) {
printf("right rotate : %d\n", root->key);
Node *temp = root->lchild;
root->lchild = temp->rchild;
temp->rchild = root;
return temp;
}
const char *insert_maintain_str[] = {"", "LL", "LR", "RL", "RR"};
Node *insert_maintain(Node *root) {
if (!has_red_child(root)) return root;
if (root->lchild->color == 0 && root->rchild->color == 0) {
if (!has_red_child(root->lchild) && !has_red_child(root->rchild)) return root;
// situation 1
printf("Situation 1 : change color\n");
root->color = 0;
root->lchild->color = root->rchild->color = 1;
return root;
}
if (root->lchild->color == 0 && !has_red_child(root->lchild)) return root;
if (root->rchild->color == 0 && !has_red_child(root->rchild)) return root;
// situation 2
int flag = 0;
printf("Situation 2 : \n");
if (root->lchild->color == 0) {
if (root->lchild->rchild->color == 0) {
root->lchild = left_rotate(root->lchild);
flag = 2;
} else {
flag = 1;
}
root = right_rotate(root);
} else {
if (root->rchild->lchild->color == 0) {
root->rchild = right_rotate(root->rchild);
flag = 3;
} else {
flag = 4;
}
root = left_rotate(root);
}
printf("Maintain Type : %s\n", insert_maintain_str[flag]);
root->color = 0;
root->lchild->color = root->rchild->color = 1;
return root;
}
Node *__insert(Node *root, int key) {
if (root == NIL) return getNewNode(key);
if (root->key == key) return root;
if (key < root->key) {
root->lchild = __insert(root->lchild, key);
} else {
root->rchild = __insert(root->rchild, key);
}
return insert_maintain(root);
}
Node *insert(Node *root, int key) {
root = __insert(root, key); // insert ans maintain
root->color = 1;
return root;
}
void clear(Node *root) {
if (root == NIL) return ;
clear(root->lchild);
clear(root->rchild);
free(root);
return ;
}
void print(Node *root) {
printf("(%d | %d, %d, %d)\n",
root->color, root->key,
root->lchild->key, root->rchild->key
);
return ;
}
void output(Node *root) {
if (root == NIL) return ;
print(root);
output(root->lchild);
output(root->rchild);
return ;
}
Node *rand_insert(Node *root) {
int val = rand() % 100;
printf("\ninsert %d to red black tree: \n", val);
root = insert(root, val);
output(root);
return root;
}
int main() {
int n;
scanf("%d", &n);
Node *root = NIL;
for (int i = 0; i < n; i++) {
root = rand_insert(root);
}
clear(root);
return 0;
}
五、Leetcode 刷题过程演示
106. 从中序与后序遍历序列构造二叉树
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
struct TreeNode *getNewNode(int key) {
struct TreeNode *p = (struct TreeNode *)malloc(sizeof(struct TreeNode));
p->val = key;
p->left = p->right = NULL;
return p;
}
struct TreeNode* buildTree(int* inorder, int inorderSize, int* postorder, int postorderSize){
if (inorderSize == 0) return NULL;
int key = postorder[postorderSize - 1];
struct TreeNode *root = getNewNode(key);
int ind = 0, n = inorderSize;
while (inorder[ind] != key) ++ind;
root->left = buildTree(inorder, ind, postorder, ind);
root->right = buildTree(inorder + ind + 1, n - ind - 1, postorder + ind, n - ind - 1);
return root;
}
99. 恢复二叉搜索树
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
struct TreeNode *l = NULL, *r = NULL, *pre = NULL;
void inorder(struct TreeNode *root) {
if (root == NULL) return ;
inorder(root->left);
if (pre && root->val < pre->val) {
r = root;
if (l == NULL) l = pre;
}
pre = root;
inorder(root->right);
return ;
}
void recoverTree(struct TreeNode* root){
l = r = pre = NULL;
inorder(root);
int temp = l->val;
l->val = r->val;
r->val = temp;
return ;
}
29. 两数相除
long long mul(long long a, long long b) {
long long temp = a, ans = 0;
while (b) {
if (b & 1) ans += temp;
temp = temp + temp;
b >>= 1;
}
return ans;
}
int divide(long long a, long long b) {
if (a == 0) return 0;
if (b == 0) return INT_MAX;
int flag = 0;
if (a < 0 && b > 0) flag = 1;
if (a > 0 && b < 0) flag = 1;
if (a < 0) a = -a;
if (b < 0) b = -b;
long long l = 0, r = a, mid;
while (l < r) {
mid = (l + r + 1) >> 1;
printf("mul(%lld, %lld) = %lld, %lld\n", mid, b, mul(mid, b), a);
if (mul(mid, b) <= a) l = mid;
else r = mid - 1;
printf("l = %lld, r = %lld\n", l, r);
}
if (flag) l = -l;
if (l < INT_MIN || l > INT_MAX) return INT_MAX;
return l;
}