Red Black Tree(红黑树)
(修改于 2018-05-06 15:53:22 还差删除维护操作、层序遍历没完成。维护操作没完成不想写层序遍历怎么办。。。)
今天下午完成了红黑树的插入的维护操作,但删除的维护操作还没有解决,删除的维护很麻烦...所有的删除会导致的情况我都理清楚了,但暂时没有想到好的算法来组合这些情况(orz...)。
实际上之前思维被《算法导论》中给的出算法思路固定了,导致一直想要在算导的红黑树算法基础上完成,后来觉得算导中给的伪码有些地方有问题,于是开始思考自己独立实现红黑树算法。当然没有算导给出的思路以及理解,我肯定也不能独立写出来。现在看来红黑树的维护情况确实比较复杂,个人觉得比AVL树的要复杂许多,但也或许是我想的算法过于复杂。总之,代码后面在说吧。
code:
/*
* RBT.c
*
* Created on: 2017年11月18日
* Author: darkchii
*/ #include <stdio.h>
#include <stdlib.h>
#include <conio.h> #define max(a, b) ((a) > (b) ? (a) : (b)) #define bool int
#define true 1
#define false 0; typedef int ElemType;
typedef enum {RED, BLACK} ColorTree;
const char ColorBoolean[] = "RB"; typedef struct BaseNode {
ElemType val;
ColorTree Color; struct BaseNode*Left;
struct BaseNode*Right;
struct BaseNode*Parent;
}rb_tree; rb_tree*Initialize();
bool IsBlack();
bool IsRed();
int CurrentBlackHeight();
int Depth();
int Degree();
rb_tree*parent();
rb_tree*uncle();
rb_tree*grandparent();
rb_tree*sibling();
rb_tree*SearchMinimum();
rb_tree*SearchMaximum();
rb_tree*SearchNode();
rb_tree*LeftRotate();
rb_tree*RightRotate();
void Insert_case_1();
void Insert_case_2();
void Insert_case_3();
void Insert_case_4_1();
void Insert_case_4_2();
rb_tree*InsertNode();
rb_tree*InsertFixup();
rb_tree*Insert();
rb_tree*Transpant();
rb_tree*DeleteNode();
void DeleteFixup(rb_tree*root, rb_tree*delNode);
rb_tree*Delete();
void InOrderTraversal();
void EraseTree(); rb_tree*Initialize(void) {
rb_tree*root = NULL; return root;
} bool IsBlack(rb_tree*node) {
return node != NULL && node->Color == BLACK;
} bool IsRed(rb_tree*node) {
return node != NULL && node->Color == RED;
} int CurrentBlackHeight(rb_tree*node) {
if (node == NULL)
return 0;
const int redness = IsRed(node);
if (redness && (IsRed(node->Left) || IsRed(node->Right)))
return -1;
int blackHeight;
if ((blackHeight = CurrentBlackHeight(node->Left)) < 0)
return -1;
if (blackHeight != CurrentBlackHeight(node->Right))
return -1; return blackHeight + !redness;
} int Depth(rb_tree*node) {
if(node == NULL)
return 0;
return max(Depth(node->Left), Depth(node->Right)) + 1;
} int Degree(rb_tree*node) {
if(node == NULL)
return 0;
return Degree(node->Left) + Degree(node->Right) + 1;
} rb_tree*parent(rb_tree*node) {
if(node == NULL)
return NULL;
return node->Parent;
} rb_tree*grandparent(rb_tree*node) {
rb_tree*p = parent(node); if(p == NULL)
return NULL;
return parent(p);
} rb_tree*sibling(rb_tree*node) {
rb_tree*p = parent(node); if(p == NULL)
return NULL;
else if(node == p->Left)
return p->Right;
else
return p->Left;
} rb_tree*uncle(rb_tree*node) {
rb_tree*p = parent(node);
rb_tree*g = grandparent(node); if(g == NULL)
return NULL;
if(sibling(p) == NULL)
return NULL;
return sibling(p);
} rb_tree*SearchMinimum(rb_tree*minNode) {
if(minNode == NULL)
return NULL;
else if(minNode->Left == NULL)
return minNode;
else
return SearchMinimum(minNode->Left);
} rb_tree*SearchMaximum(rb_tree*maxNode) {
if(maxNode == NULL)
return NULL;
else if(maxNode->Right == NULL)
return maxNode;
else
return SearchMaximum(maxNode->Right);
} rb_tree*SearchNode(rb_tree*root, ElemType Data) {
if(root == NULL)
return NULL;
else if(root->val > Data)
return SearchNode(root->Left, Data);
else if(root->val < Data)
return SearchNode(root->Right, Data);
else
return root;
} rb_tree*LeftRotate(rb_tree*root, rb_tree*node) {
rb_tree*x = node;
rb_tree*y = x->Right; x->Right = y->Left;
if(y->Left != NULL)
y->Left->Parent = x;
y->Parent = x->Parent;
if(x->Parent == NULL)
root = y;
else if(x == x->Parent->Left)
x->Parent->Left = y;
else
x->Parent->Right = y; y->Left = x;
x->Parent = y; return root;
} rb_tree*RightRotate(rb_tree*root, rb_tree*node) {
rb_tree*x = node;
rb_tree*y = x->Left; x->Left = y->Right;
if(y->Right != NULL)
y->Right->Parent = x;
y->Parent = x->Parent;
if(x->Parent == NULL)
root = y;
else if(x == x->Parent->Left)
x->Parent->Left = y;
else
x->Parent->Right = y; y->Right = x;
x->Parent = y; return root;
} rb_tree*LeftRightRotate(rb_tree*root, rb_tree*node)
{
rb_tree*x = node;
rb_tree*y = x->Left;
root = LeftRotate(root, y);
root = RightRotate(root, x);
return root;
} rb_tree*RightLeftRotate(rb_tree*root, rb_tree*node)
{
rb_tree*x = node;
rb_tree*y = x->Right;
root = RightRotate(root, y);
root = LeftRotate(root, x);
return root;
} /**
void Insert_case_1(rb_tree*root, rb_tree*node) {
if(parent(node) == NULL)
node->Color = BLACK;
} void Insert_case_2(rb_tree*root, rb_tree*node) {
return ;
} void Insert_case_3(rb_tree*root, rb_tree*node) {
parent(node)->Color = BLACK;
uncle(node)->Color = BLACK;
grandparent(node)->Color = RED;
InsertFixup(root, grandparent(node));
} void Insert_case_4_1(rb_tree*root, rb_tree*node) {
rb_tree*p = parent(node);
rb_tree*g = grandparent(node); if(node == g->Left->Right) {
LeftRotate(root, p);
node = node->Left;
} else if(node == g->Right->Left) {
RightRotate(root, p);
node = node->Right;
} Insert_case_4_2(root, node);
} void Insert_case_4_2(rb_tree*root, rb_tree*node) {
rb_tree*p = parent(node);
rb_tree*g = grandparent(node); if(node == p->Left)
RightRotate(root, g);
else
LeftRotate(root, g);
p->Color = BLACK;
g->Color = RED;
}
*/ rb_tree*InsertFixup(rb_tree*root, rb_tree*node) {
/**
if(parent(node) == NULL)
Insert_case_1(root, node);
else if(parent(node)->Color == BLACK)
Insert_case_2(root, node);
else if(uncle(node)->Color == RED)
Insert_case_3(root, node);
else
Insert_case_4_1(root, node);
*/ while(parent(node) != NULL && parent(node)->Color == RED) {
rb_tree*gptr = grandparent(node);
if(parent(node) == gptr->Left) {
if(uncle(node) != NULL && uncle(node)->Color == RED) {
parent(node)->Color = BLACK;
uncle(node)->Color = BLACK;
gptr->Color = RED;
node = gptr;
} else if(node == parent(node)->Right) {
node->Color = BLACK;
gptr->Color = RED;
root = LeftRightRotate(root, gptr);
node = parent(node);
}
else {
parent(node)->Color = BLACK;
gptr->Color = RED;
root = RightRotate(root, gptr);
}
} else {
if(uncle(node) != NULL && uncle(node)->Color == RED) {
parent(node)->Color = BLACK;
uncle(node)->Color = BLACK;
gptr->Color = RED;
node = gptr;
} else if(node == parent(node)->Left) {
node->Color = BLACK;
gptr->Color = RED;
root = RightLeftRotate(root, gptr);
node = parent(node);
}
else {
parent(node)->Color = BLACK;
gptr->Color = RED;
root = LeftRotate(root, gptr);
}
}
}
root->Color = BLACK;
return root;
} rb_tree*InsertNode(rb_tree*root, rb_tree*newNode) {
rb_tree*p = root;
rb_tree*q = NULL; while(p != NULL) {
q = p;
if(p->val > newNode->val)
p = p->Left;
else
p = p->Right;
}
newNode->Parent = q; if(q == NULL)
root = newNode;
else if(q->val > newNode->val)
q->Left = newNode;
else
q->Right = newNode;
newNode->Left = NULL;
newNode->Right = NULL;
newNode->Color = RED; root = InsertFixup(root, newNode); return root;
} rb_tree*Insert(rb_tree*root, ElemType Data) {
rb_tree*newNode = (rb_tree*)malloc(sizeof(rb_tree));
newNode->val = Data;
return InsertNode(root, newNode);
} rb_tree*Transpant(rb_tree*root, rb_tree*delNode, rb_tree*graftNode) {
if(delNode->Parent == NULL)
root = graftNode;
else if(delNode == delNode->Parent->Left)
delNode->Parent->Left = graftNode;
else
delNode->Parent->Right = graftNode; if(graftNode != NULL)
graftNode->Parent = delNode->Parent; return root;
} rb_tree*DeleteNode(rb_tree*root, rb_tree*delNode) {
rb_tree*minNode;
rb_tree*x; if (delNode->Left == NULL && delNode->Right == NULL) {
x = parent(delNode);
root = Transpant(root, delNode, delNode->Right);
} else if(delNode->Left == NULL) {
x = delNode->Right;
root = Transpant(root, delNode, delNode->Right);
} else if(delNode->Right == NULL) {
x = delNode->Left;
root = Transpant(root, delNode, delNode->Left);
} else {
minNode = SearchMinimum(delNode->Right);
if (parent(minNode) == delNode) {
if (minNode->Right != NULL)
x = minNode->Right;
else
x = minNode;
} else {
if (minNode->Right != NULL)
x = minNode->Right;
else
x = parent(minNode);
root = Transpant(root, minNode, minNode->Right);
minNode->Right = delNode->Right;
if(minNode->Right != NULL)
minNode->Right->Parent = minNode;
}
root = Transpant(root, delNode, minNode);
minNode->Left = delNode->Left;
if(minNode->Left != NULL)
minNode->Left->Parent = minNode;
minNode->Color = delNode->Color;
}
free(delNode);
delNode = NULL;
DeleteFixup(root, x); return root;
} void DeleteFixup(rb_tree*root, rb_tree*node) {
while(node != NULL) {
if (node->Left == NULL) { } else if (node->Right == NULL) {
if (node->Left != NULL) {
node->Left->Color = RED;
node->Color = BLACK;
if (parent(node) != NULL)
parent(node)->Color = RED;
if (node->Left->Left != NULL)
root = RightRotate(root, node);
else
root = LeftRightRotate(root, node);
if (sibling(node) != NULL)
sibling(node)->Color = node->Color;
}
} else if(node->Right != NULL) {
if (node->Color == RED) {
node->Color = BLACK;
node->Right->Color = RED;
if (parent(node) != NULL)
parent(node)->Color = RED;
}
} else { }
node = grandparent(node);
}
root->Color = BLACK;
} rb_tree*Delete(rb_tree*root, ElemType Data) {
rb_tree*delNode; if((delNode = SearchNode(root, Data)) == NULL) {
printf("该数据不存在!\n");
return root;
}
return DeleteNode(root, delNode);
} void InOrderTraversal(rb_tree*node) {
if(node != NULL) {
InOrderTraversal(node->Left);
printf("%d---%c\n", node->val, ColorBoolean[node->Color]);
InOrderTraversal(node->Right);
}
} void EraseTree(rb_tree*node) {
if(node != NULL) {
EraseTree(node->Left);
EraseTree(node->Right);
free(node);
node = NULL;
}
} int main() {
rb_tree*root, *x;
ElemType Data;
char ch; puts("1> 插入 2> 删除");
puts("3> 查找 4> 显示");
puts("5> 深度 6> 节点个数");
puts("7> 黑高 8> 退出"); x = root = Initialize(); while((ch = getch()) != '8') {
switch(ch) {
case '1' : printf("\n插入数据:");
scanf("%d", &Data);
root = Insert(root, Data);
break;
case '2' : printf("\n删除数据:");
scanf("%d", &Data);
root = Delete(root, Data);
break;
case '3' : printf("\n查找数据:");
scanf("%d", &Data);
if((x = SearchNode(root, Data)) != NULL) {
printf("%d %c\n", x->val, ColorBoolean[x->Color]);
}
else
printf("没有查找到数据!\n");
break;
case '4' : printf("\n显示数据:");
InOrderTraversal(root);
printf("\n");
break;
case '5' : printf("\n当前深度为:%d", Depth(root));
break;
case '6' : printf("\n当前节点个数为:%d", Degree(root));
break;
case '7' : printf("\n当前黑高为:%d", CurrentBlackHeight(root));
break;
}
}
EraseTree(root);
return 0;
}
插入的维护并不复杂,所以没什么好说的,改天加上配图再讨论一下,下面说说删除的维护。
实际上我在考虑要不要试试根据深度和节点颜色来实现删除的维护操作,emmm...但懒癌晚期,并不太想试。。。删除的维护操作想了一下午,但并没有找到很好的切入点。
进行删除的维护前,我们要弄清楚我们的目的:删除一个节点可能会破坏红黑树的性质,我们要维护被破坏的部分的性质。所以切入点很重要,删除掉的节点肯定没有了,我们不能通过它作为切入点,我们要找到一个比较合适的节点来检查红黑树的性质,这里检查实际上是隐式的,我们不会写明显的代码去检查红黑树的性质是否被破坏来进行维护。
合适的切入点一定选在删除节点的附近,或删除节点的父节点、或孩子节点,唯一的一种情况是删除节点的子树中的某个节点与其附近的节点。
我也是这样的思路,所以根据不同情况选择的有右孩子节点、父节点、右子树中恰比删除节点大的节点或其父节点、右孩子节点。
选择好这些节点后,我们才能开始进行维护,简单来说维护过程就是:检查该节点的位置、颜色情况根据可恢复性质的方法实现相对应的算法即可。但实际上要找到对几种情况通用的算法还是有点难的,不能把代码写死,还要能正确完成任务。
我被困住的部分是从如下图的基本情况:
假如要删除67或者88,我们会先得到这样的:
因为删除的是88,根据我的代码维护点为67,对于这种情况,我们只需要在67节点右旋一次即可,得到:
于是开始时设计的维护过程可能是这样:
if (node->Left != NULL) {
node->Left->Color = RED;
node->Color = BLACK;
if (parent(node) != NULL)
parent(node)->Color = RED;
if (node->Left->Left != NULL)
root = RightRotate(root, node);
else
root = LeftRightRotate(root, node);
if (sibling(node) != NULL)
sibling(node)->Color = node->Color;
}
但后面会发现还有许多坑...
突然懒得继续讨论了。。。
总之,慢慢来吧。。。
我发现自己越来越懒了 = =
然后说一说红黑树的平衡度,实际上红黑树不如AVL树平衡,假如一直往树的左(或右)子树中插入节点,到一定程度就会出现左边(或右边)更深的情况,但比普通的二叉树会好非常多,至少绝对不会出现左式树或右式树的情况,所以,整体的各种操作的时间复杂度为O(lg n)。为什么AVL树更平衡呢,因为AVL树可是依据各子树间的高度差来维护平衡的,且高度差不能超过2,条件很苛刻,所以比红黑树更平衡一点。
两张的红黑树:
图片来自网络
参考资料: wikipad-red black tree 、《算法导论》
Red Black Tree(红黑树)的更多相关文章
- Red Black Tree 红黑树 AVL trees 2-3 trees 2-3-4 trees B-trees Red-black trees Balanced search tree 平衡搜索树
小结: 1.红黑树:典型的用途是实现关联数组 2.旋转 当我们在对红黑树进行插入和删除等操作时,对树做了修改,那么可能会违背红黑树的性质.为了保持红黑树的性质,我们可以通过对树进行旋转,即修改树中某些 ...
- 吐血整理:二叉树、红黑树、B&B+树超齐全,快速搞定数据结构
前言 没有必要过度关注本文中二叉树的增删改导致的结构改变,规则操作什么的了解一下就好,看不下去就跳过,本文过多的XX树操作图片纯粹是为了作为规则记录,该文章主要目的是增强下个人对各种常用XX树的设计及 ...
- 红黑树(二)之 C语言的实现
概要 红黑树在日常的使用中比较常用,例如Java的TreeMap和TreeSet,C++的STL,以及Linux内核中都有用到.之前写过一篇文章专门介绍红黑树的理论知识,本文将给出红黑数的C语言的实现 ...
- 红黑树(四)之 C++的实现
概要 前面分别介绍红黑树的理论知识和红黑树的C语言实现.本章是红黑树的C++实现,若读者对红黑树的理论知识不熟悉,建立先学习红黑树的理论知识,再来学习本章. 目录1. 红黑树的介绍2. 红黑树的C++ ...
- 红黑树(五)之 Java的实现
概要 前面分别介绍红黑树的理论知识.红黑树的C语言和C++的实现.本章介绍红黑树的Java实现,若读者对红黑树的理论知识不熟悉,建立先学习红黑树的理论知识,再来学习本章.还是那句老话,红黑树的C/C+ ...
- 浅谈算法和数据结构: 七 二叉查找树 八 平衡查找树之2-3树 九 平衡查找树之红黑树 十 平衡查找树之B树
http://www.cnblogs.com/yangecnu/p/Introduce-Binary-Search-Tree.html 前文介绍了符号表的两种实现,无序链表和有序数组,无序链表在插入的 ...
- 第十四章 红黑树——C++代码实现
红黑树的介绍 红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉查找树.红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键 ...
- 红黑树的实现——c++
红黑树介绍参考上一篇. 1. 基本定义 enum RBTColor{RED, BLACK}; template <class T> class RBTNode{ public: RBTCo ...
- 红黑树 Java实现
概要 前面分别介绍红黑树的理论知识.红黑树的C语言和C++的实现.本章介绍红黑树的Java实现,若读者对红黑树的理论知识不熟悉,建立先学习红黑树的理论知识,再来学习本章.还是那句老话,红黑树的C/C+ ...
- 红黑树 - C++代码实现
红黑树的介绍 红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉查找树.红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键 ...
随机推荐
- codeforces 1198B - Welfare State
题目链接:http://codeforces.com/problemset/status 题目大意为有n个市民,每个市民有ai点数财富,以下有q次操作,操作类型为两类,1类:把第p个市民的财富改为x, ...
- Object.fromEntries
//数组转换成对象 const arr = [['foo', 1],['bar', 2]] const obj = Object.fromEntries(arr) console.log(obj.ba ...
- 对C#继承、多态的理解
11月3日 阴天 前两天看某位大牛写的程序,对于C#多态有困惑,今天一大早来查阅了不少资料,自认为有了一个基本的认知,记录下来,一扫今天这阴霾的天气 ------------------------- ...
- 2019新生赛 %%%xxh
笔记.md ...
- Mysql数据库内置功能之函数
一 函数 MySQL中提供了许多内置函数,例如: 一.数学函数 ROUND(x,y) 返回参数x的四舍五入的有y位小数的值 RAND() 返回0到1内的随机值,可以通过提供一个参数(种子)使RAND( ...
- libcurl库的简介(一)
一.Libcurl库简介 LibCurl是免费的客户端URL传输库,支持FTP,FTPS, HTTP, HTTPS, SCP, SFTP, TFTP, TELNET, DICT, FILE ,LDAP ...
- Python学习之字典集合篇
lambda表达式:起到一个函数速写的作用,允许在代码内嵌入一个函数的定义; filter()函数:1.用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表.2.接收两个参数,第一个为 ...
- MyBatis-Spring整合之方式3
通过注解整合 1.在UserDao同级目录下新建接口UserMapper,内容如下: public interface UserMapper { @Select("select * from ...
- vue项目用npm安装sass包遇到的问题及解决办法
IDEA启动vue程序,浏览器访问时出现如下情况的关于node-sass的错误: 错误1: Module build failed (from ./node_modules/sass-loader/d ...
- SQL - 各种joins