红黑树讲解(超短红黑树c代码150行)

红黑树

说明:

图片信息来源于计蒜客数据结构红黑树一讲

注意自己动手多画

介绍

红黑树是一种自平衡二查查找树,是由Rudolf Bayer发明的,当时被称为平衡二叉B树(symmetric binary B-trees)。后来,在1978年被 Leo J. Guibas 和 Robert Sedgewick 修改为如今的“红黑树”。

红黑树与AVL相比,他牺牲了部分平衡性以在插入和删除操作时,减少旋转操作.

红黑树与其他平衡二叉树相比,他增加了一个颜色属性,用来标识节点是红色还是黑色,如果一个节点没有子节点,则该节点的子节点对应的指针为NIL,也就是说红黑树的所有叶子节点都是NIL

它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。

C++STL中的map,set就是用红黑树实现的

五个条件

  1. 每个节点非黑即红
  2. 根节点是黑色
  3. 叶节点(NIL)是黑色
  4. 如果一个节点是红色,则他的两个子节点都是黑色
  5. 从根节点出发到所有叶节点路径上,黑色节点数量相同

调整策略

  1. 插入调整站在祖父节点看,调整两红
  2. 删除调整站在父节点看,调整双重黑
  3. 插入和删除的情况处理一共五种

学习前提:

需要有AVL树的基础.

插入调整

分析:

插入的时候,我们初始化把节点初始化为红色.这样插入在违反规则上只用考虑双红的情况,并且只从祖父节点忘下看,父亲节点和孙子节点节点时候违反规则.

提问一:为什么要插入的新节点为红色?

如果我们要插入的新节点为黑色,某条支路上会多个红色,会使得第五条规则被破坏,很难通过旋转来调整.而通过旋转可以比较容易的使得红色节点分开

提问二:为什么要从祖父节点忘下看?而不能从父亲节点往下看?

这个问题其实是由于调整引起的.我们需要三层节点,而两层节点无法完成调整.

试想一下,我们从父亲节点忘下看,父亲节点与儿子节点发生双红,另一个儿子一定为黑色,我们无论是左旋,还是右旋都无法满足要求,所以只考虑两层是无法满足要求的,所以要考虑三层,我们接下来的分析就是考虑三层的情况下,完成调整的

情况一:叔父节点是红色

在这里插入图片描述

如果叔父节点是红色那么祖父节点一定是黑色,并且这个节点的父亲节点和本节点都是红色,我们只需要将祖父节点变成红色,祖父节点的两个儿子节点都变成黑色即可(所谓的红色上顶)

情况二:叔父节点是黑色

在这里插入图片描述

如果叔父节点是黑色,因为父亲节点为红色所以祖父节点一定是黑色,这时有两种情况,一种是本节点和父亲节点属于同一侧的孩子节点,另一中是属于不同侧的孩子节点.

不同侧

如果父亲是左孩子,本节点是右孩子.来一个小左旋,现在红色节点都是左孩子了,就进行同侧的处理方案,反之同样分析

同侧

如果都是父亲节点与本节点左孩子,直接一个大右旋将,这时右支路少一个黑色因为祖父节点原来为黑,现在变成黑色了,其他的没有改变,有两种调整方案,一种是红色上顶(祖父左右孩子编程黑色,祖父变成红色),一种是红色下沉(祖父变成黑色,左右孩子变成红色)

删除调整

分析:

删除调整,如果我们删除的是一个红色节点,则不会影响红黑树的性质,直接删除即可

如果我们删除的是一个黑色节点,则会影响红黑树的性质,则需要分析.

  1. 如果我们要删除的时是有两个孩子的节点,我们会去找到他的前驱节点,就变成了有一个孩子节点的节点
  2. 如果我们删除的是没有孩子节点的节点,相应的他的左右孩子都是NIL我们就利用NIL当成一个孩子节点,也变成了只有一个孩子节点的情况
  3. 如果我们删除的是有一个孩子节点的,可以分为一下几种情况,我们具体分析

且我们在删除黑色节点时,为了不影响红黑树的性质,我们是将响应的节点加上一重黑,如果响应节点已经是黑色节点就变成了双重黑,如果相应节点是红色节点,则不违反红黑树的规则

注意:经过上面的分析,我们下面处理的情况都是需删除节点是黑色,且有一条支路的情况,我们删除这个节点且让其子孩子加上一重黑,这时有两种情况,一种是出现双重黑,进行调整,另一种是孩子节点是红色,没有出现双重黑,不用进行调整,我们下面都是出现双重黑的结果

提问-:为什么可以将NIL当成一个孩子处理?

其实NIL可以当成一个孩子处理的必要条件是,我们在后续处理中其他支路没有用到NIL,后面的具体分析中可以慢慢体悟

提问二:为什么删除的节点只需要两层?

因为我们删除的情况如果两层无法解决,就是一直向上递归返回,并进行处理,所以两层就可以解决

情况一

在这里插入图片描述

兄弟节点为红色,父亲节点一定是黑色,将父亲节点进行一次旋转(旋转之后兄弟节点变成跟节点),但是原兄弟节点支路少了一个黑丝节点,所以将原兄弟节点与原跟节点颜色互换即可.

情况二

在这里插入图片描述

如果兄弟节点的两个孩子节点都是黑色,兄弟节点变成红色,x减少一重黑,父亲节点增加一重黑,向上返回

情况三

在这里插入图片描述

这时分为兄弟节点有一个红色节点,或者有两个红色节点,但是有两个红色节点不影响我们后续的操作,所以我们只讨论有一个红色节点的情况

不同侧有红色节点

在这里插入图片描述

交换父兄弟节点与红色节点的颜色,并将原红色节点旋转为成为brother节点,这样就变成同侧是红色了,进行同侧处理

同侧有红色节点(两红归为这一类)

在这里插入图片描述

如果是RR型就进行,首先双重黑节点减去一重黑,然后进行大左旋,交换原父亲节点与兄弟节点的颜色即可

超级超级简短的代码150行(红色上顶)

/*************************************************************************
	> File Name: 5.rbt.cpp
	> Author: ldc
	> Mail: litesla
	> Created Time: 2019年02月22日 星期五 20时49分32秒
 ************************************************************************/
//0 red 1 black 2 double_dlack
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct Node{
    int key,color;
    struct Node *lchild, *rchild;
}Node;

Node *NIL;
__attribute__((constructor))
void init_NIL(){
    NIL = (Node*)malloc(sizeof(Node));
    NIL->key =-1;
    NIL->color = 1;
    NIL->lchild = NIL->rchild = NIL;
}

Node *init(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 *node) {
    return (node->lchild->color == 0 || node->rchild->color == 0);
}

Node *left_rotate(Node *node) {
    Node *temp = node->rchild;
    node->rchild = temp->lchild;
    temp->lchild = node;
    return temp;
}

Node *right_rotate(Node *node) {
    Node *temp = node->lchild;
    node->lchild = temp->rchild;
    temp->rchild = node;
    return temp;
}
Node *insert_maintain(Node *node) {
    if (!has_red_child(node)) return node;
    else if (node->lchild->color == 0 && node->rchild->color == 0) {
        if (!has_red_child(node->lchild) && !has_red_child(node->rchild)) return node;
    }else if (node->lchild->color == 0 && has_red_child(node->lchild)){
        if (node->lchild->rchild->color == 0) {
            node->lchild = left_rotate(node->lchild);
        }
        node = right_rotate(node);
    }else if (node->rchild->color == 0 && has_red_child(node->rchild)) {
        if (node->rchild->lchild->color == 0) {
            node->rchild = right_rotate(node->rchild);
        }
        node = left_rotate(node);
    } else {
        return node;
    }
    node->color = 0;
    node->lchild->color = node->rchild->color = 1;
    return node;
}

Node *__insert(Node *node, int key) {
    if (node == NIL) return init(key);
    else if (node->key == key) return node;
    else if (node->key > key) node->lchild = __insert(node->lchild, key);
    else node->rchild = __insert(node->rchild, key);
    return insert_maintain(node);
}

Node *insert(Node *node, int key){
    node = __insert(node, key);
    node->color = 1;
    return node;
}

Node *erase_maintain(Node *node) {
        #define maintain(a,b,c,d) ({\
        if (node->b->color == 0) {\
            node = c(node);\
            node->color = 1;\
            node->a->color = 0;\
            node->a = erase_maintain(node->a);\
            return node;\
        }\
        node->a->color = 1;\
        if (!has_red_child(node->b)) {\
            node->color += 1;\
            node->b->color -= 1;\
            return node;\
        }else if (node->b->b->color != 0) {\
            node->b = d(node->b);\
            node->b->color = 1;\
            node->b->b->color = 0;\
        }\
        node = c(node);\
        node->color = node->a->color;\
        node->lchild->color = node->rchild->color = 1;\
    })
    if (node->lchild->color != 2 && node->rchild->color != 2) return node;
    else if (node->lchild->color == 2){
        maintain(lchild, rchild, left_rotate, right_rotate);
    }else {
        maintain(rchild, lchild, right_rotate, left_rotate);
    }
    return node;
}

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

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

Node *erase(Node *node, int key) {
    node = __erase(node,key);
    node->color = 1;
    return node;
}
void clear(Node *node) {
    if (node == NIL) return ;
    clear(node->lchild);
    clear(node->rchild);
    free(node);
    return ;
}

int main() {
    return 0;
}

相关引用

https://www.jisuanke.com/course/792/41228

https://baike.baidu.com/item/红黑树/2413209?fr=aladdin

猜你喜欢

转载自blog.csdn.net/weixin_38331384/article/details/88105469
今日推荐