红黑树

95 阅读4分钟

红黑树(上)

(C语言实现)\

文章目录

一、平衡条件

  1. 节点非黑既红
  2. 根节点是黑色
  3. 叶子(NIL)结点是黑色
  4. 红色节点下面接两个黑色节点
  5. 从根节点到叶子结点路径上,黑色节点数量相同

平衡条件的认识

第4条和第5条条件,注定了,红黑树中最长路径是最短路径的长度的 2 倍。

本质上,红黑树也是通过树高来控制平衡的。

红黑树比 AVL 树树高控制条件要更松散,红黑树在发生节点插入和删除以后,发生调整的概率,比 AVL 树要更小。

二、学习诀窍

  1. 理解红黑树的插入调整,要站在祖父节点向下进行调整
  2. 理解红黑树的删除调整,要站在父节点向下进行调整
  3. 插入调整,主要就是为了解决双红情况
  4. 新插入的节点一定是红色,插入黑色节点一定会产生冲突,违反条件5,插入红色节点,不一定产生冲突
  5. 把每一种情况,想象成一棵大的红黑树中的局部子树
  6. 局部调整的时候,为了不影响全局,调整前后的路径上黑色节点数量相同

三、插入策略

  1. 叔叔节点为红色的时候,修改三元组小帽子,改成红黑黑
  2. 叔叔节点为黑色的时候,参考 AVL 树的失衡情况,分成 L L , L R , R L , R R LL,LR,RL,RR LL,LR,RL,RR, 先参考 AVL 树的旋转调整策略,然后再修改三元组的颜色,有两种调整策略:红色上浮,红色下沉。
  3. 两大类情况,包含 8 种小情况

四、代码演示

  1. 插入调整,发正在递归的回溯阶段
  2. 插入调整代码中,使用 goto 语句,8行代码,变成了4行
  3. 处理根节点一定是黑色,通过代码封装, 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;
}