红黑树

一、红黑树概述

  红黑树不仅是一个二叉搜索树,并且满足以下规则:

    1>每个节点不是红的就是黑的,

    2>根结点为黑色,

    3>如果节点为红色,其子节点必须为黑色,

    4>任一节点至NULL(树尾端)的任何路径,所含的黑节点的树木必须相同

二、红黑树上结点的插入

    下面分6种情况介绍红黑树的插入操作:
    1 插入点的父亲为红,父亲的兄弟节点为黑,插入点在外侧   ///把父亲染红 祖父染黑 后右旋
    2 插入点的父亲为红,父亲的兄弟节点为黑,插入点在内侧  ///先左旋后把父亲染红 祖父染黑 后右旋
    3 插入点的父亲为红,父亲的兄弟节点为红,插入点在外侧  ///把父亲及父亲的兄弟染成黑色
    4 插入点的父亲为红,父亲的兄弟节点为红,插入点在外侧  ///(父亲的祖父节点为红)
    5 插入点的父亲为红,父亲的兄弟节点为红,插入点在内侧  ///先右旋父亲改为黑祖父为红后左旋(两次旋转)
    6 插入点的父亲为黑,直接插入
    这里判断父节点是否为祖先节点的左节点和 取得伯父节点,判断伯父节点的颜色的目的是为了识别以上者6种情况

    针对双黑结点的兄弟做一次右旋转,结果使双黑结点的近侄子成为双黑结点新的兄弟;
    将新兄弟结点着为双黑结点的父结点的颜色,父结点着为黑色,再针对父做一次左旋转,

 

三、红黑树删除操作

    

一、普通二叉查找树删除一个结点:

(1)待删除结点没有子结点,即它是一个叶子结点,此时直接删除

(2)待删除结点只有一个子结点,则可以直接删除;如果待删除结点是根结点,则它的子结点变为根结点;如果待删除结点不是根结点,则用它的子结点替代它的位置。

(3)待删除结点有两个子结点,首先找出该结点的后继结点(即右子树中数值最小的那个结点),然后将两个结点进行值交换(即:只交换两个结点的数值,不改变结点的颜色)并将待删除结点删除,由于后继结点不可能有左子结点,对调后的待删除结点也不会有左子结点,因此要把它删除的操作会落入情况(1)或情况(2)中。

二、.红黑树的删除结点算法

1.待删除结点有两个外部结点,操作如下:

(1)直接把该结点调整为叶结点

(2)若该结点是红色,则可直接删除,不影响红黑树的性质,算法结束

(3)若该结点是黑色,则删除后红黑树不平衡。此时要进行“双黑”操作

记该结点为V,则删除了V后,从根结点到V的所有子孙叶结点的路径将会比树中其他的从根结点到叶结点的路径拥有更少的黑色结点, 破坏了红黑树性质4。此时,用“双黑”结点来表示从根结点到这个“双黑”结点的所有子孙叶结点的路径上都缺少一个黑色结点。  
双黑含义:该结点需要代表两个黑色结点,才能维持树的平衡

图1

如图1,要删除结点90,则删除后从根结点到结点90的所有子树结点的路径上的黑色结点比从根点到叶结点的路径上的黑结点少。因而,删除结点90后,用子结点NULL代替90结点,并置为“双黑”结点。

2.  待删除结点有一个外部结点,操作为: 
    该节点是黑色,其非空子节点为红色 ;则将其子节点提升到该结点位置,颜色变黑

3.“双黑”结点的处理

分三种情况:(1)双黑色结点的兄弟结点是黑色,且子结点有红色结点

(2)双黑结点的兄弟结点是黑色,且有两个黑色结点

                       (3)双黑结点的兄弟结点是红色结点
 
(1)双黑结点的兄弟结点是黑色,且子结点有红色结点
A种情况:双黑结点远侄子结点(双黑结点若为左孩子,则双黑结点的兄弟结点的右孩子为远侄子结点;同理,处理双黑结点为右孩子)为红色,如图2
处理方法:把兄弟结点染为双黑结点的父亲结点的颜色,把兄弟结点的右孩子染为黑色,再把父结点染为黑色;然后针对父结点进行一次左旋转,如科3。
                    
                        图2                                                                              图3
B种情况:双黑结点近侄子结点(双黑结点若为左孩子,则双黑结点的兄弟结点的左孩子为近侄子结点;同理,处理双黑结点为右孩子)为红,如图4

处理方法:针对双黑结点的兄弟做一次右旋转,结果使双黑结点的近侄子成为双黑结点新的兄弟;将新兄弟结点着为双黑结点的父结点的颜色,父结点着为黑色,再针对父做一次左旋转,如图5

 
                                图4                                                               图5

 (2)双黑结点的兄弟结点是黑色,且有两个黑色结点,如图6

 处理方法:把双黑结点的兄弟结点着为红色,双黑结点的父结点着为黑色;若父结点原来为红色,则算法结束;若父结点原来黑色,则将父结点作为双黑结点,继续调整,如图7
 
                        图6                                                                      图7
  (3)双黑结点的兄弟结点是红色结点,如图8
处理方法:单旋转为情况1或情况2,并改变双黑结点的兄弟结点的颜色及父结点的颜色(?????可能不完善),如图9
 
 
代码实现如下:
#include <iostream>
#include <algorithm>
#include <string.h>
#include <stdio.h>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
#include <stack>
#include <stdlib.h>
#include <windows.h> using namespace std; typedef int Elemtype;
#define RED 0
#define BLACK 1 typedef struct Node
{
Elemtype node;
struct Node* left;
struct Node* right;
struct Node* prev;
int color;
} Node, * PNode; typedef struct RBTree
{
PNode root;
PNode data;
} RBTree, * PRBTree; int init(PRBTree tree)
{ //红黑树的初始化
if(tree == NULL) {
return ;
}
tree->data = (PNode)malloc(sizeof(Node));
tree->data->color = BLACK;
tree->root = tree->data;
return ;
}
void rotate_left(PRBTree tree, PNode x)
{
PNode y = x->right;
x->right = y->left;
if(y->left != tree->data)
{
y->left->prev = x;
}
y->prev = x->prev;
if(x == tree->root) {
tree->root = y;
}
else if(x == x->prev->left) {
x->prev->left = y;
}
else {
x->prev->right = y;
}
y->left = x;
x->prev = y;
} ///右旋
void rotate_right(PRBTree tree, PNode x)
{
PNode y = x->left;
x ->left = y->right;
if(y->right != tree->data)
{
y->right->prev = x;
}
y->prev = x->prev;
if(x == tree->root) {
tree->root = y;
}
else if(x == x->prev->left) {
x->prev->left = y;
}
else {
x->prev->right = y;
}
y->right = x;
x->prev = y;
} int insert_fixup(PRBTree tree, PNode e)
{
while(e->prev->color == RED)
{
if(e->prev->prev->left == e->prev) ///该节点的父亲节点是 左节点
{
if(e->prev->prev->right == RED)
{
e = e->prev->prev;
e->left->color = e->right->color = BLACK;
e->color = RED;
}
else
{
if(e->prev->right == e)
{
e = e->prev;
rotate_left(tree, e);
}
e->prev->color = BLACK;
e->prev->prev->color = RED;
rotate_right(tree, e->prev->prev);
}
}
else ///该节点的父亲节点是 右节点
{
if(e->prev->prev->left->color == RED)
{
e = e->prev->prev;
e->left->color = e->right ->color = BLACK;
e->color = RED;
}
else
{
if(e->prev->left == e)
{
e = e->prev;
rotate_right(tree, e);
}
e->prev->color = BLACK;
e->prev->prev->color = RED;
rotate_left(tree, e->prev->prev);
}
}
}
tree->root->color = BLACK;
return ;
} PNode insertNode(PRBTree tree, Elemtype e)
{
PNode t = NULL;
PNode p = NULL;
t = tree->root;
int flag = ;
if(tree->root == tree->data)
{
tree->root = (PNode)malloc(sizeof(Node));
tree->root->node = e;
tree->root->color = BLACK;
tree->root->prev = tree->root->left = tree->root->right = tree->data;
return tree->root;
}
while(t != tree->data)
{
p = t;
if(e < t->node)
{
flag = ;
t = t->left;
}
else
{
if(e > t->node)
{
flag = ;
t = t ->right;
}
else
{
if((flag = rand() % ) == )
{
t = t->left;
}
else
{
t = t->right;
}
}
}
}
t = (PNode)malloc(sizeof(Node));
t->node = e;
t->color = RED;
t->prev = p;
t->left = t->right = tree->data;
if(!flag)
{
p->left = t;
}
else
{
p->right = t;
}
insert_fixup(tree, t);
return t;
} ///左旋 ///找后继结点中-->右子树中数值最小的节点
PNode next(PRBTree tree, PNode t)
{
if(t == tree->data)
{
return NULL;
}
while(t->right != tree->data);
{
t = t->right;
}
return t;
} int delete_fixup(PRBTree tree, PNode c)
{
PNode b;
while(c != tree->root && c->color == BLACK)
{
if(c == c -> prev->left)
{
b = c->prev->right;
if(b->color == RED)
{
b->color = BLACK;
c->prev->color = RED;
rotate_left(tree, c->prev);
b = c->prev->right;
}
if(b->right->color == BLACK && b->left->color == BLACK)
{
b->color = RED;
c = c->prev;
}
else
{
if(b->right->color == BLACK)
{
b->color = RED;
b->left->color = BLACK;
rotate_right(tree, b);
b = c->prev->right;
}
b->color = b->prev->color;
b->prev->color = BLACK;
b->right->color = BLACK;
rotate_left(tree, c->prev);
c = tree->root;
}
}
else
{
b = c->prev->left;
if(b->color == RED)
{
b->color = BLACK;
c->prev->color = RED;
rotate_right(tree, c->prev);
b = c->prev->left;
}
if(b->right->color == BLACK && b->left->color == BLACK)
{
b->color = RED;
c = c->prev;
}
else
{
if(b->left->color == BLACK)
{
b->color = RED;
b->right->color = BLACK;
rotate_left(tree, b);
b = c->prev->left;
}
b->color = b->prev->color;
b->prev->color = BLACK;
b->left->color = BLACK;
rotate_right(tree, c->prev);
c = tree->root;
}
}
}
c->color = BLACK;
return ;
} ///删除节点
PNode destory_node(PRBTree tree, PNode t)
{
PNode c = NULL;
PNode d = NULL;
Elemtype tmp;
if(t == tree->data)
{
return NULL;
}
if(t->left != tree->data && t->right != tree->data) ///该节点有两个子节点 则找到该节点右子树中最小的值
{
d = next(tree, t);
tmp = d->node;
d->node = t->node;
t->node = tmp;
}
else
{
d = t;
}
if(d->left == tree->data)
{
c = d->right;
}
else
{
c = d->left;
}
c->prev = d->prev;
if(d->prev != tree->data)
{
if(d->prev->left == d)
{
d->prev->left = c;
}
else
{
d -> prev->right = c;
}
}
else
{
tree->root = c;
}
if(d->color == BLACK)
{
delete_fixup(tree, c);
}
return d;
} ///最小值
int minnum(PRBTree tree)
{
PNode x = tree->root;
while(x->left != tree->data)
{
x = x->left;
}
return x->node;
}
int get_root(PRBTree tree)
{
PNode x = tree->root;
return x->node;
}
///最大值
int maxnum(PRBTree tree)
{
PNode x = tree->root;
while(x->right != tree->data)
{
x = x->right;
}
return x->node;
} PNode mmap[];
void print(PRBTree tree)
{
PNode x = tree->root;
int front = ;
int rear = ;
if(tree == NULL)
{
printf("NULL\n");
return;
}
PNode tmp;
mmap[rear] = x;
int count = ;
int temp = ;
while(front <= rear)
{
tmp = mmap[front++];
count--;
if(tmp->left != tree->data)
{
mmap[++rear] = tmp->left;
temp++;
}
if(tmp->right != tree->data)
{
mmap[++rear] = tmp->right;
temp++;
}
printf("%d color = -->", tmp->node);
if(tmp->color == BLACK)
{
printf("BLACK |");
}
else
{
printf("RED |");
}
if(count == )
{
count = temp;
temp = ;
printf("\n");
}
}
return ;
}
PNode get_root1(PRBTree tree)
{
return tree->root;
}
int main()
{
PNode p ;
RBTree tree ;
init(&tree);
int i;
PNode tt , ttt;
tt = insertNode(&tree, );
insertNode(&tree, );
insertNode(&tree, );
insertNode(&tree, );
insertNode(&tree, );
insertNode(&tree, );
insertNode(&tree, );
insertNode(&tree, );
insertNode(&tree, );
insertNode(&tree, );
insertNode(&tree, );
insertNode(&tree, );
p = get_root1(&tree);
printf("根是------>%d\n", get_root(&tree));
printf("根是------>%d\n\n", p->node);
int mmin = minnum(&tree);
int mmax = maxnum(&tree);
printf("mmin = %d\n", mmin);
printf("mmax = %d\n\n", mmax);
print(&tree);
printf("\n删除 %d 之后树的结构是:\n\n", tt->node);
ttt = destory_node(&tree, tt);
print(&tree);
return ;
}

输出结果是:

根是------>
根是------> mmin =
mmax = color = -->BLACK |
color = -->BLACK | color = -->BLACK |
color = -->BLACK | color = -->BLACK | color = -->RED | color = -->RED |
color = -->BLACK | color = -->BLACK | color = -->BLACK | color = -->BLACK |
color = -->RED | 删除 之后树的结构是: color = -->BLACK |
color = -->BLACK | color = -->BLACK |
color = -->BLACK | color = -->RED | color = -->BLACK | color = -->BLACK |
color = -->RED | color = -->BLACK | color = -->BLACK | color = -->RED |

RBTree 红黑树的更多相关文章

  1. 红黑树(二)之 C语言的实现

    概要 红黑树在日常的使用中比较常用,例如Java的TreeMap和TreeSet,C++的STL,以及Linux内核中都有用到.之前写过一篇文章专门介绍红黑树的理论知识,本文将给出红黑数的C语言的实现 ...

  2. 红黑树的C语言实现

    rbtree.h #ifndef _RED_BLACK_TREE_H_ #define _RED_BLACK_TREE_H_ #define RED 0 // 红色节点 #define BLACK 1 ...

  3. 平衡搜索树--红黑树 RBTree

    红黑树是一棵二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是Red或Black. 通过对任何一条从根到叶子节点简单路径上的颜色来约束树的高度,红黑树保证最长路径不超过最短路径的两倍, ...

  4. java——红黑树 RBTree

    对于完全随机的数据,普通的二分搜索树就很好用,只是在极端情况下会退化成链表. 对于查询较多的情况,avl树很好用. 红黑树牺牲了平衡性,但是它的统计性能更优(综合增删改查所有的操作). 红黑树java ...

  5. 红黑树(RBTREE)之上-------构造红黑树

    该怎么说呢,现在写代码的速度还是很快的,很高兴,o(^▽^)o. 光棍节到了,早上没忍住,手贱了一般,看到*D的优惠,买了个机械键盘,晚上就到了,敲着还是很舒服的,和老婆炫耀了一把哈哈. 光棍节再去* ...

  6. 红黑树(RB-tree)比AVL树的优势在哪?

    1. 如果插入一个node引起了树的不平衡,AVL和RB-Tree都是最多只需要2次旋转操作,即两者都是O(1):但是在删除node引起树的不平衡时,最坏情况下,AVL需要维护从被删node到root ...

  7. 高级搜索树-红黑树(RBTree)代码实现

    代码实现 代码参考了<数据结构(c++语言版)>--清华大学邓俊辉 "RBTree.h" #pragma once //#include"pch.h" ...

  8. 高级搜索树-红黑树(RBTree)解析

    目录 红黑树的定义 节点与树的定义 旋转操作 插入操作 情况1:p的兄弟u为黑色 情况2: p的兄弟u为红色 插入操作性能分析 代码实现 删除操作 情况1:x的接替者succ为红色 情况2:x的接替者 ...

  9. RB-tree (红黑树)相关问题

    今天被问到了红黑树的规则,简述总结一下: 1.每个节点不是红色就是黑色. 2.根节点为黑色. 3.如果节点为红,其子节点必须为黑. 4.任一节点至NULL(树尾端)的任何路径,所含之黑节点数必须相同. ...

随机推荐

  1. 福大软工1816:Alpha(3/10)

    Alpha 冲刺 (3/10) 队名:第三视角 组长博客链接 本次作业链接 团队部分 团队燃尽图 工作情况汇报 张扬(组长) 过去两天完成了哪些任务: 文字/口头描述: 1.学习qqbot库: 2.实 ...

  2. STL中list的erase()方法

    http://www.cnblogs.com/gshlsh17/ rase()方法是删除iterator指定的节点  但是要注意的是在执行完此函数的时候iterator也被销毁了   这样的话关于it ...

  3. PAT 甲级 1015 Reversible Primes

    https://pintia.cn/problem-sets/994805342720868352/problems/994805495863296000 A reversible prime in ...

  4. 【UML】类图介绍

    1.类图是面向对象系统建模中最常用和最重要的图,是定义其它图的基础.类图主要是用来显示系统中的类.接口以及它们之间的静态结构和关系的一种静态模型. 2.类的关系有泛化(Generalization). ...

  5. 【bzoj2809】[Apio2012]dispatching 贪心+可并堆

    题目描述 在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿.在这个帮派里,有一名忍者被称之为 Master.除了 Master以外,每名忍者都有且仅有一个上级.为保密,同时增 ...

  6. ASP.NET页面之间传值QueryString(1)

    QueryString是一种非常简单的传值方式,他可以将传送的值显示在浏览器的地址栏中.如果是传递一个或多个安全性要求不高或是结构简单的数值时,可以使用这个方法.但是对于传递数组或对象的话,就不能用这 ...

  7. hdu4035 Maze 【期望dp + 数学】

    题目链接 BZOJ4035 题解 神题啊...orz 不过网上题解好难看,数学推导不写\(Latex\)怎么看..[Latex中毒晚期] 我们由题当然能很快写出\(dp\)方程 设\(f[i]\)表示 ...

  8. 如何使用Navicat恢复数据库脚本

    Navicat 可以做数据库备份,当然也可以做数据库脚本恢复了.操作很简单. 1.连接需要恢复的数据库.鼠标右键点击,选择[运行SQL文件] 2.在弹出的窗口中选择sql文件,继续下一步即可. 余不赘 ...

  9. NOIP2017宝藏 [搜索/状压dp]

    NOIP2017 宝藏 题目描述 参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 n 个深埋在地下的宝藏屋, 也给出了这 n 个宝藏屋之间可供开发的 m 条道路和它们的长度. 小明决心亲自前往挖掘 ...

  10. Windows Socket 编程_ 简单的服务器/客户端程序

    转载自:http://blog.csdn.net/neicole/article/details/7459021 一.程序运行效果图 二.程序源代码 三.程序设计相关基础知识 1.计算机网络    2 ...