【红黑树】的详细实现(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). ...
随机推荐
- 关闭 APIPA
遇到的问题:我在网卡2上设置了静态ip,可是出现了一个奇怪的ip地址169.254.*.*,如下图. 解决方法:关闭APIPA功能 按照下述的做法,自己在win7企业版上尝试了下,有效.不再出现169 ...
- mac /windows
1.mac 和 windows 同样的浏览器展示的样式有偏差 考虑:字体的问题,mac/windows 的字体不一样 font-family 用法
- js前后端交互
1.前后端交互模式 2.promise用法 (1)异步调用 (2)ajax回顾 (3).promise 优点:可以解决回调地狱(多层异步调用嵌套问题)(解决代码可读性低的问题) 提供简洁的api (4 ...
- 假期学习【十一】Python切词,以及从百度爬取词典
今天主要对从CSDN爬取的标题利用jieba(结巴)进行分词,但在分词过程中发现,如大数据被分成了大/数据,云计算被分隔成了云/计算. 后来又从百度百科--->信息领域爬取了相关词语作为词典,预 ...
- K3/cloud执行计划插件示例
public class AutoCheckInventory : IScheduleService { /// <summary> /// 实际运行的Run 方法 ...
- Session方法
Session的save()和persist()方法Session的save()方法使一个临时对象转变为持久化对象.它完成以下操作:(1)将临时对象加入到Session缓存中,使其进入持久化状态.(2 ...
- JavaScript变量的传递方式
废话不多说,直接上案例: [案例] 1.访问变量 按值: function addM(num) { num += 5; return num; } var cnt = 10; var result = ...
- fnt文字
//fileName是fnt文件的path.在fnt文件的相同目录下有同名png文件.使用ShoeBox.exe生成fnt文件.在fnt文件中修改路径为类似number 及 number.png的格式 ...
- moveTo 与 moveBy的区别 (转贴)
MoveTo和MoveBy可以使精灵移动,区别在于MoveTo是移动到给定的坐标点:而MoveBy是从当前坐标点移动给定的坐标点这么多的距离.举个例子,假定精灵当前的坐标点是(x, y),分别给Mov ...
- centos6.8安装教程
特别详细的一个安装教程以及镜像下载等,用虚拟机不会安装或者安装失败的可以参考一下. https://blog.csdn.net/wu_zeqin/article/details/79833046