【红黑树】的详细实现(C++)
红黑树的介绍
红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉查找树。
红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键值,小于等于右孩子的键值。
除了具备该特性之外,红黑树还包括许多额外的信息。红黑树的每个节点上都有存储位表示节点的颜色,颜色是红(Red)或黑(Black)。
红黑树一棵在每个结点上增加了一个存储位来表示结点颜色(红色RED或黑色BLACK)的二叉搜索树。
二叉搜索树简单的说就是:对树中任何结点x,其左子树中的关键字最大不超过x.key,即对左子树中任一结点y,有y.key<x.key;其右子树中的关键字最小不低于x.key,即对右子树中任一结点y,有y.key>x.key。
红黑树中每个结点包含5个属性:color、key、left、right和p。如果一个结点没有子结点或父结点,则该结点相应属性值为NIL。如图1所示:

图1 红黑树的叶结点
这些NIL被视为指向二叉搜索树的叶结点(外部结点)的指针,而把带关键字的结点视为树的内部结点。
为了便于处理红黑树代码中的边界条件,使用一个哨兵T.nil来代表所有的NIL:所有的叶结点和根结点的父结点。如图2所示:

图2 红黑树的T.nil属性
红黑树的特性:
(1) 每个节点或者是黑色,或者是红色。
(2) 根节点是黑色。
(3) 每个叶子节点是黑色(为空的结点)。
(4) 不能出现两个连续的红色结点(如果一个节点是红色的,那么它的两个子节点都是黑色的)。
(5) 从一个节点开始所有路径上包含相同数目的黑节点。
关于它的特性,需要注意的是:
第一,特性(3)中的叶子节点,是只为空(NIL或null)的节点。
第二,特性(5),确保没有一条路径会比其他路径长出俩倍。因而,红黑树是相对是接近平衡的二叉树。
红黑树示意图如下:
从红黑树中任一结点x出发(不包括结点x),到达一个外部结点的任一路径上的黑结点个数叫做结点x的黑高度,亦称为结点的阶(rank),记作bh(x)。红黑树的黑高度定义为其根结点的黑高度。下图,数字表示该结点的黑高度。
红黑树的搜索
红黑树的插入


(2)u是pu的右子女,pu是gu的左子女。在这种情况下对pu做先左后右的双旋转,再交换u与gu的颜色,就可恢复红黑树的特性,结束重新平衡过程。

红黑树的删除





②若结点t为黑色结点,以v为旋转轴,做一次右单旋转,并改变υ和r的颜色,即可消除结点u的双重黑色,恢复红黑树的特色。

>当结点u是结点g的左子女的情况与上面讨论的情况是镜像的,只要左、右指针互换就可以了。
红黑树的实现代码
typedef enum { RED = , BLACK } Color;
//红黑树结点类型
template <typename Type>
struct RBTNode
{
Color color; //颜色
Type key; //关键字
RBTNode* left; //左孩子
RBTNode* right; //右孩子
RBTNode* parent; //父结点
};
//红黑树类型
template<typename Type>
class RBTree
{
public:
//构造函数
RBTree()
{
Nil = BuyNode();
root = Nil;
Nil->color = BLACK;
}
//析构函数
~RBTree()
{
destroy(root); //销毁创建的非Nil结点
delete Nil; //最后删除Nil结点
Nil = NULL;
}
//中序遍历
void InOrder() { InOrder(root); }
//插入
//1.BST方式插入
//2.调整平衡
bool Insert(const Type &value)
{
RBTNode<Type>* pr = Nil; //pr用来记住父节点
RBTNode<Type>* s = root; //定义变量s指向根
while (s != Nil)
{
if (value == s->key)
{
return false;
}
pr = s; //每次记住s的父节点
if (value < s->key)
{
s = s->left;
}
else
{
s = s->right;
}
}
//循环后s==Nil
s = BuyNode(value); //申请结点
if (pr == Nil) //如果父节点pr是根节点,第一次root指向Nil,所以pr==Nil
{
root = s;
root->parent = pr;
}
else //如果父节点不是根节点
{
if (value < pr->key)
{
pr->left = s;
}
else
{
pr->right = s;
}
s->parent = pr; //设置新结点s的父节点
}
//调整平衡
Insert_Fixup(s);
return true;
}
//删除key结点(先查找,再调用内部删除)
void Remove(Type key)
{
RBTNode<Type>* t;
if ((t = Search(root, key)) != Nil)
{
Remove(t);
}
else
{
cout << "Key is not exist." << endl;
}
}
//中序遍历打印结点详细的结点颜色
void InOrderPrint() { InOrderPrint(root); }
protected:
//申请结点结点,将结点的颜色初始化为红色,初始化结点的关键字,其他的初始化为空
RBTNode<Type>* BuyNode(const Type &x = Type())
{
RBTNode<Type>* s = new RBTNode<Type>();
assert(s != NULL);
s->color = RED;
s->left = s->right = s->parent = Nil;
s->key = x;
return s;
}
//中序遍历
void InOrder(RBTNode<Type>* root)
{
if (root != Nil)
{
InOrder(root->left);
cout << root->key << " ";
InOrder(root->right);
}
}
//左转,对z结点左转
// zp zp
// / \
// z z
// / \ / \
// lz y lz y
// / \ / \
// ly ry ly ry
void LeftRotate(RBTNode<Type>* z)
{
RBTNode<Type>* y = z->right; //用y指向要转动的z结点
z->right = y->left;
if (y->left != Nil) //y所指结点的左结点不为空
{
y->left->parent = z;
}
y->parent = z->parent;
if (root == z) //z就是根节点
{
root = y;
}
else if (z == z->parent->left) //z在左结点
{
z->parent->left = y;
}
else //z在右结点
{
z->parent->right = y;
}
y->left = z;
z->parent = y;
}
//右转,对z结点进行右转
// zp zp
// / \
// z z
// / \ / \
// y rz y rz
// / \ / \
// ly ry ly ry
void RightRotate(RBTNode<Type>* z)
{
RBTNode<Type>* y = z->left;
z->left = y->right;
if (y->right != Nil)
{
y->right->parent = z;
}
y->parent = z->parent;
if (root == z) //如果z是根结点
{
root = y;
}
else if (z == z->parent->left) //z在左结点
{
z->parent->left = y;
}
else //z在右结点
{
z->parent->right = y;
}
y->right = z;
z->parent = y;
}
//插入后的调整函数
void Insert_Fixup(RBTNode<Type>* s)
{
RBTNode<Type>* uncle; //叔结点(父结点的兄弟结点)
while (s->parent->color == RED) //父节点的颜色也为红色
{
if (s->parent == s->parent->parent->left) //父节点是左结点
{
uncle = s->parent->parent->right;
if (uncle->color == RED) //叔结点为红色
{
//父节点和叔结点都变为黑色
s->parent->color = BLACK;
uncle->color = BLACK;
//祖父结点变为红色
s->parent->parent->color = RED;
//将s指针指向祖父结点,下一次循环继续判断祖父的父节点是否为红色
s = s->parent->parent;
}
else //没有叔结点,或叔结点为黑色(经过多次循环转换,叔结点可能为黑)
{
if (s == s->parent->right) //如果调整的结点在右结点
{
s = s->parent; //先将s指向s的父结点
LeftRotate(s); //再左转
}
//如果调整的结点在左结点,将s的父节点变为黑色,将祖父的结点变为红色,将s的祖父结点右转
s->parent->color = BLACK;
s->parent->parent->color = RED;
RightRotate(s->parent->parent);
}
}
else
{
if (s->parent == s->parent->parent->right) //父节点是右结点
{
uncle = s->parent->parent->left;
if (uncle->color == RED) //叔结点为红色
{
//父节点和叔结点都变为黑色
s->parent->color = BLACK;
uncle->color = BLACK;
//祖父结点变为红色
s->parent->parent->color = RED;
//将s指针指向祖父结点,下一次循环继续判断祖父的父节点是否为红色
s = s->parent->parent;
}
else //没有叔结点,或叔结点为黑色(经过多次循环转换,叔结点可能为黑)
{
if (s == s->parent->left) //如果调整的结点在左结点
{
s = s->parent; //先将s指向s的父结点
RightRotate(s); //再右转
}
//如果调整的结点在右结点,将s的父节点变为黑色,将祖父的结点变为红色,将s的祖父结点右转
s->parent->color = BLACK;
s->parent->parent->color = RED;
LeftRotate(s->parent->parent);
}
}
}
}
root->color = BLACK; //最后始终将根节点置为黑色
}
//查找key结点
RBTNode<Type>* Search(RBTNode<Type>* root, Type key) const
{
if (root == Nil) //root为空,或key和根的key相同
{
return Nil;
}
if (root->key == key)
{
return root;
}
if (key<root->key)
{
return Search(root->left, key);
}
else
{
return Search(root->right, key);
}
}
//将u的子节点指针指向u改变指向v,将v的父节点改变为指向u的父节点
// up
// \
// u
// / \
// ul ur
// / \
// v ulr
// \
// rv
void Transplant(RBTNode<Type>* u, RBTNode<Type>* v)
{
if (u->parent == Nil) //u的父节点为空
{
root = v; //直接令根root为v
}
else if (u == u->parent->left) //u父节点不为空,且u在左子树
{
u->parent->left = v;
}
else //u在左子树
{
u->parent->right = v;
}
v->parent = u->parent;
}
//找到最左结点(最小)
// xp
// \
// x
// / \
// xl xr
// / \
// xll xlr
RBTNode<Type>* Minimum(RBTNode<Type>* x)
{
if (x->left == Nil)
{
return x;
}
return Minimum(x->left);
}
//删除红黑树结点z
void Remove(RBTNode<Type>* z)
{
RBTNode<Type>* x = Nil;
RBTNode<Type>* y = z; //y记住传进来的z结点
Color ycolor = y->color; //
if (z->left == Nil) //z只有右孩子
{
x = z->right;
Transplant(z, z->right);
}
else if (z->right == Nil) //z只有右孩子
{
x = z->left;
Transplant(z, z->left);
}
else //右左孩子和右孩子
{
y = Minimum(z->right); //y是z右子树的的最左子树
ycolor = y->color;
x = y->right;
if (y->parent == z) //z的右子结点没有左节点或为Nil
{
x->parent = y;
}
else //z的右子结点有左节点或为Nil
{
Transplant(y, y->right);
y->right = z->right;
y->right->parent = y;
}
Transplant(z, y);
//改变指向
y->left = z->left;
z->left->parent = y;
y->color = z->color;
}
if (ycolor == BLACK)
{
Remove_Fixup(x);
}
}
//红黑树删除调整
void Remove_Fixup(RBTNode<Type>* x)
{
while (x != root&&x->color == BLACK) //当结点x不为根并且它的颜色不是黑色
{
if (x == x->parent->left) //x在左子树
{
RBTNode<Type>* w = x->parent->right; //w是x的兄结点
if (w->color == RED) //情况1
{
w->color = BLACK;
x->parent->color = RED;
LeftRotate(x->parent);
w = x->parent->right;
}
if (w->left->color == BLACK&&w->right->color == BLACK) //情况2
{
w->color = RED;
x = x->parent;
}
else
{
if (w->right->color == BLACK) //情况3
{
w->color = RED;
w->left->color = BLACK;
RightRotate(w);
w = x->parent->right;
}
//情况4
w->color = w->parent->color;
w->parent->color = BLACK;
w->right->color = BLACK;
LeftRotate(x->parent);
x = root; //结束循环
}
}
else //x在右子树
{
RBTNode<Type>* w = x->parent->left;
if (w->color == RED) //情况1
{
w->parent->color = RED;
w->color = BLACK;
RightRotate(x->parent);
w = x->parent->left;
}
if (w->right->color == BLACK&&w->right->color == BLACK) //情况2
{
w->color = RED;
x = x->parent;
}
else
{
if (w->left->color == BLACK) //情况3
{
w->right->color = BLACK;
w->color = RED;
LeftRotate(w);
w = x->parent->left;
}
//情况4
w->color = x->parent->color;
x->parent->color = BLACK;
w->left->color = BLACK;
RightRotate(x->parent);
x = root; //结束循环
}
}
}
x->color = BLACK;
}
//销毁红黑树
void destroy(RBTNode<Type>* &root)
{
if (root == Nil)
{
return;
}
if (root->left != Nil)
{
destroy(root->left);
}
if (root->right != Nil)
{
destroy(root->right);
}
delete root;
root = NULL;
}
//中序遍历打印结点详细的结点颜色
void InOrderPrint(RBTNode<Type>* node)
{
if (node == Nil)
{
return;
}
if (node->left != NULL)
{
InOrderPrint(node->left);
}
cout << node->key << "(" << ((node->color == BLACK) ? "BLACK" : "RED") << ")" << " ";
if (node->right != Nil)
{
InOrderPrint(node->right);
}
}
private:
RBTNode<Type>* root; //根指针
RBTNode<Type>* Nil; //外部结点,表示空结点,黑色的
};
测试代码
int main(int argc, char* argv[])
{
RBTree<int> rb;
// rb.InitTree();
int arr[] = { ,,,,,,,, };
int n = sizeof(arr) / sizeof(int);
for (int i = ; i < n; i++)
{
rb.Insert(arr[i]);
} rb.InOrder();
cout << endl;
rb.InOrderPrint();
cout << endl;
rb.Remove();
rb.InOrder();
cout << endl;
rb.Remove();
return ;
}
主函数
【红黑树】的详细实现(C++)的更多相关文章
- 红黑树之 原理和算法详细介绍(阿里面试-treemap使用了红黑树) 红黑树的时间复杂度是O(lgn) 高度<=2log(n+1)1、X节点左旋-将X右边的子节点变成 父节点 2、X节点右旋-将X左边的子节点变成父节点
红黑树插入删除 具体参考:红黑树原理以及插入.删除算法 附图例说明 (阿里的高德一直追着问) 或者插入的情况参考:红黑树原理以及插入.删除算法 附图例说明 红黑树与AVL树 红黑树 的时间复杂度 ...
- 红黑树(二)之 C语言的实现
概要 红黑树在日常的使用中比较常用,例如Java的TreeMap和TreeSet,C++的STL,以及Linux内核中都有用到.之前写过一篇文章专门介绍红黑树的理论知识,本文将给出红黑数的C语言的实现 ...
- 红黑树(四)之 C++的实现
概要 前面分别介绍红黑树的理论知识和红黑树的C语言实现.本章是红黑树的C++实现,若读者对红黑树的理论知识不熟悉,建立先学习红黑树的理论知识,再来学习本章. 目录1. 红黑树的介绍2. 红黑树的C++ ...
- 红黑树(五)之 Java的实现
概要 前面分别介绍红黑树的理论知识.红黑树的C语言和C++的实现.本章介绍红黑树的Java实现,若读者对红黑树的理论知识不熟悉,建立先学习红黑树的理论知识,再来学习本章.还是那句老话,红黑树的C/C+ ...
- 第十四章 红黑树——C++代码实现
红黑树的介绍 红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉查找树.红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键 ...
- 【Java深入研究】10、红黑树
一.红黑树介绍 红黑树是二叉查找树,红黑树的时间复杂度为: O(lgn) 红黑树的特性:(1)每个节点或者是黑色,或者是红色.(2)根节点是黑色.(3)每个叶子节点(NIL)是黑色. [注意:这里叶子 ...
- 红黑树 Java实现
概要 前面分别介绍红黑树的理论知识.红黑树的C语言和C++的实现.本章介绍红黑树的Java实现,若读者对红黑树的理论知识不熟悉,建立先学习红黑树的理论知识,再来学习本章.还是那句老话,红黑树的C/C+ ...
- 红黑树 - C++代码实现
红黑树的介绍 红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉查找树.红黑树是特殊的二叉查找树,意味着它满足二叉查找树的特征:任意一个节点所包含的键值,大于等于左孩子的键 ...
- Java集合(4)一 红黑树、TreeMap与TreeSet(下)
目录 Java集合(1)一 集合框架 Java集合(2)一 ArrayList 与 LinkList Java集合(3)一 红黑树.TreeMap与TreeSet(上) Java集合(4)一 红黑树. ...
- 红黑树(R-B Tree)
R-B Tree简介 R-B Tree,全称是Red-Black Tree,又称为“红黑树”,它一种特殊的二叉查找树.红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black). ...
随机推荐
- python3爬取淘宝商品(失效)
最近有人反映淘宝的搜索功能要登录才能用,原先的直接爬取的方法挂了.稍微把之前的代码修改了一下,登录采用最简单的复制cookie来解决. 顺便说一下,这只是根据搜索的的索引界面获取的信息,并未深入的获取 ...
- PP: Sequence to sequence learning with neural networks
From google institution; 1. Before this, DNN cannot be used to map sequences to sequences. In this p ...
- nginx 启动报错找不到nginx.pid文件
这个问题的出现应该是系统找不到nginx的配置文件nginx.conf,所以,我们要告诉系统配置文件的位置:' --- 使用nginx -c /usr/local/nginx/conf/nginx.c ...
- DVR
DVR,全称为Digital Video Recorder(硬盘录像机),即数字视频录像机,相对于传统的模拟视频录像机,采用硬盘录像,故常常被称为硬盘录像机.它是一套进行图像计算存储处理的计算机系统, ...
- Copy Paste DWG to older 3ds Max
Hi, This is quick tutorial: how to install Auto Cad scripts to be able to copy from newer Auto Cad t ...
- 与大神聊天1h
与大神聊天1h 啊,与大神聊天1h真的是干货满满 解bug问题 之所以老出bug是因为我老是调用别人的包啊,在调参数的时候,并不知道内部机制 其实就自己写一个函数,然后能把功能实现就好了. 问题是,出 ...
- IIS添加网站
打开IIS 在网站上面点击右键进行添加网站 进入添加网站配置
- 理解Login函数
_LoginPartial.cshtml文件 其中 <li>@Html.ActionLink("Log in", "Login", "Ac ...
- 转载:android audio policy
Audio policy basic:https://www.cnblogs.com/CoderTian/p/5705742.html Set volume flow:https://blog.csd ...
- 基于PO和单例设计模式用python+selenium进行ui自动化框架设计
一)框架目录的结构 二)config包当中的config.ini文件主要是用来存项目的绝对路径,是为了后续跑用例和生成测试报告做准备然后目前的配置文件大都会用yaml,ini,excel,还有.py也 ...