问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

算法与数据结构:红黑树详解

创作时间:
作者:
@小白创作中心

算法与数据结构:红黑树详解

引用
CSDN
1.
https://blog.csdn.net/qq_58240849/article/details/139350282

红黑树是一种自平衡二叉查找树,它在插入和删除操作后能够保持相对平衡,从而确保搜索、最小值、最大值、前驱和后继操作的时间复杂度为O(log n)。本文将详细介绍红黑树的性质、插入和删除操作的平衡调整方法,以及相应的代码实现。

红黑树的性质

红黑树有以下5条性质:

  1. 每个节点是红色或黑色。
  2. 根节点是黑色。
  3. 所有叶子节点都是黑色(这里的叶子节点是指树中所有的空节点)。
  4. 每个红色节点的两个子节点都是黑色(从每个红色节点到叶子节点的路径上不能有两个连续的红色节点)。
  5. 从任一节点到其每个叶子节点的所有路径都包含相同数目的黑色节点。

这些性质保证了红黑树的平衡性,使得树的高度保持在对数级别。

红黑树的难点

红黑树的难点主要在于插入和删除操作后的平衡调整。为了帮助理解,可以记住以下口诀:

  • 插入调整时站在祖父节点看:如果插入的节点违反了性质4,需要找到祖父节点进行调整。
  • 删除调整站在父节点看:通过父节点的视角进行调整操作。

插入操作

插入节点时,需要考虑以下问题:

  • 插入节点应该是什么颜色?

答:插入节点应该为红色。因为如果插入黑色节点,一定会违反性质5;而插入红色节点,如果父节点是黑色则不需要调整,如果父节点是红色则需要调整,平衡的概率是50%,而插入黑色是100%需要调整。

插入调整分为两种情况:

  1. 叔叔节点是红色的情况
  • 将祖父节点变为红色,叔叔节点和父节点变为黑色。

  • 通过调整,这颗子树已经变得平衡。

  1. 叔叔节点是黑色的情况
  • 这种情况类似于AVL树中的LL类型,需要进行旋转和颜色调整。

  • 先对祖父节点进行右旋,然后调整节点颜色,确保每条路径的黑色节点数量相同。



    对于LR类型,可以通过父节点的左旋将其转换为LL类型处理。

删除操作

删除操作需要考虑以下情况:

  1. 删除红色节点且度为0:直接删除,不会影响平衡。
  2. 删除黑色节点且度为0:用NIL虚拟空节点代替,并将NIL节点的颜色变为双重黑。
  3. 删除红色节点且度为1:这种情况不存在,因为红色节点只能接两个黑色节点或不接节点。
  4. 删除黑色节点且度为1:将唯一子节点(必为红色)提升到当前位置,并将其颜色改为黑色。
  5. 删除度为2的节点:通过二叉排序树的删除操作,将其转换为删除度为0或1的节点。

对于双重黑的处理,有以下几种情况:

  1. 兄弟节点及其子节点都是黑色
  • 将双重黑节点减去一重黑,父节点增加一重黑,兄弟节点变为红色。


  1. RR类型
  • 父节点左旋,调整节点颜色,保持每条路径的黑色节点数量相同。





  1. RL类型
  • 先对兄弟节点右旋,再按照RR类型处理。



  1. 兄弟节点为红色
  • 兄弟节点变黑,父节点变红,然后左旋,转化为其他情况处理。



代码实现

以下是红黑树的C语言实现代码:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define RED    0
#define BLACK  1
#define DBLACK 2
#define NIL (&__NIL)
#define K(n) (n->key)
#define L(n) (n->lchild)
#define R(n) (n->rchild)
#define C(n) (n->color)

typedef struct Node {
    int key, color; // 0 red, 1 black, 2 double black
    struct Node *lchild, *rchild;
} Node;

Node __NIL;

__attribute__((constructor))
void init_NIL() {
    NIL->key   = -1;
    NIL->color = BLACK;
    NIL->lchild = NIL->rchild = NIL;
    return ;
}

Node *getNewNode(int key) {
    Node *p = (Node *)malloc(sizeof(Node));
    p->key   = key;
    p->color = RED;
    p->lchild = p->rchild = NIL;
    return p;
}

bool has_red_node(Node *root) {
    return root->lchild->color == RED || root->rchild->color == RED;
}

Node *left_rotate(Node *root) {
    Node *new_root = root->rchild;
    root->rchild = new_root->lchild;
    new_root->lchild = root;
    return new_root;
}

Node *right_rotate(Node *root) {
    Node *new_root = root->lchild;
    root->lchild = new_root->rchild;
    new_root->rchild = root;
    return new_root;
}

Node *insert_maintain(Node *root) {
    int flag = 0;
    if (C(L(root)) == RED && has_red_node(L(root))) flag = 1;
    if (C(R(root)) == RED && has_red_node(R(root))) flag = 2;
    if (flag == 0) return root;
    if (C(L(root)) == RED && C(R(root)) == RED) goto red_up_maintain;
    if (flag == 1) {
        if (C(R(L(root))) == RED) {
            L(root) = left_rotate(L(root));
        }
        root = right_rotate(root);
    } 
    else {
        if (C(L(R(root))) == RED) {
            R(root) = right_rotate(R(root));
        }
        root = left_rotate(root);
    }
red_up_maintain:
    C(root) = RED;
    C(L(root)) = C(R(root)) = BLACK;
    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);
    root->color = BLACK;
    return root;
}

Node *predecessor(Node *root) {
    Node *temp = root->lchild;
    while (temp->rchild != NIL) temp = temp->rchild;
    return temp;
}

Node *erase_maintain(Node *root) {
    if (C(L(root)) != DBLACK && C(R(root)) != DBLACK) return root;
    if (has_red_node(root)) {
        root->color = RED;
        if (root->lchild->color == RED) {
            root = right_rotate(root);
            root->rchild = erase_maintain(root->rchild);
        } else {
            root = left_rotate(root);
            root->lchild = erase_maintain(root->lchild);
        }
        root->color = BLACK;
        return root;
    }
    if ((root->lchild->color == DBLACK && !has_red_node(root->rchild)) 
        || (root->rchild->color == DBLACK && !has_red_node(root->lchild))) {
        root->color += 1;
        root->lchild->color -= 1;
        root->rchild->color -= 1;
        return root;
    }
    if (root->rchild->color == DBLACK) {
        root->rchild->color = BLACK;
        if (root->lchild->lchild->color != RED) {
            root->lchild = left_rotate(root->lchild);
        }
        root->lchild->color = root->color;
        root = right_rotate(root);
    } else {
        root->lchild->color = BLACK;
        if (root->rchild->rchild->color != RED) {
            root->rchild = right_rotate(root->rchild);
        }
        root->rchild->color = root->color;
        root = left_rotate(root);
    }
    root->lchild->color = root->rchild->color = BLACK;
    return root;
}

Node *__erase(Node *root, int key) {
    if (root == NIL) return root;
    if (key < root->key) {
        root->lchild = __erase(root->lchild, key);
    } else if (key > root->key) {
        root->rchild = __erase(root->rchild, key);
    } else {
        if (root->lchild == NIL || root->rchild == NIL) {
            Node *temp = root->lchild == NIL ? root->rchild : root->lchild;
            temp->color += root->color;
            free(root);
            return temp;
        }
        Node *temp = predecessor(root);
        root->key = temp->key;
        root->lchild = __erase(root->lchild, temp->key);
    }
    return erase_maintain(root);
}

Node *erase(Node *root, int key) {
    root = __erase(root, key);
    root->color = BLACK;
    return root;
}

void clear(Node *root) {
    if (root == NIL) return ;
    clear(root->lchild);
    clear(root->rchild);
    free(root);
    return ;
}

void output(Node *root) {
    if (root == NIL) return ;
    printf("(%d| %d; %d, %d)\n", 
        C(root), K(root),
        K(L(root)), K(R(root))
    );
    output(root->lchild);
    output(root->rchild);
    return ;
}

int main() {
    srand(time(0));
    #define MAX_N 10
    Node *root = NIL;
    for (int i = 0; i < MAX_N; i++) {
        int x = rand() % 100;
        printf("\ninsert %d to red black tree : \n", x);
        root = insert(root, x);
        output(root);
    }
    int x;
    while (~scanf("%d", &x)) {
        printf("\nerase %d from red black tree\n", x);
        root = erase(root, x);
        output(root);
    }
    return 0;
}

这段代码实现了红黑树的基本操作,包括插入、删除和平衡调整。通过这些操作,可以确保红黑树在动态变化中保持良好的平衡性。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号