可以参考的学习资料:
30张图带你彻底理解红黑树–这个可以讲解思想来源
红黑树(一)之 原理和算法详细介绍–总结了算法的规则
【数据结构】带你轻松学习红黑树,菜鸟学习红黑树经验分享!
–做这个视频的大佬动画分析得不错
#include<iostream>
#include<ctime>
using namespace std;
//R - B Tree,全称是Red - Black Tree,又称为“红黑树”,它一种特殊的二叉查找树。红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black)。
//红黑树的特性 :
//(1)每个节点或者是黑色,或者是红色。
//(2)根节点是黑色。
//(3)每个叶子节点(NIL)是黑色。[注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
//(4)如果一个节点是红色的,则它的子节点必须是黑色的。
//(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
//
//注意:
//(01) 特性(3)中的叶子节点,是只为空(NIL或null)的节点。
//(02) 特性(5),确保没有一条路径会比其他路径长出俩倍。因而,红黑树是相对是接近平衡的二叉树。
//定理:一棵含有n个节点的红黑树的高度至多为2log(n+1).
//处理红黑树的核心思想:将红色的节点移到根节点;然后,将根节点设为黑色。
//既然是“将红色的节点移到根节点”,那就是说要不断的将破坏红黑树特性的红色节点上移(即向根方向移动)。
//基本定义
#define
#define
typedef int Type;
//================================//
//1.定义结构
//红黑树的结点类
typedef struct RBTreeNode {
unsigned int color=-1;
Type key;
struct RBTreeNode *left;
struct RBTreeNode *right;
struct RBTreeNode *parent;
}Node,*RBTree;
//红黑树的根
typedef struct rb_root {
Node *node;
}RBRoot;
RBTree rb_parent(RBTree node) {
if (node && node->parent)
return node->parent;
else return NULL;
}
bool isRedNode(RBTree node) {
return node->color == RED;
}
bool isBlackNode(RBTree node) {
return node->color == BLACK;
}
RBRoot * createRoot() {
RBRoot *root = (RBRoot *)malloc(sizeof(RBRoot));
root->node = NULL;
return root;
}
//===============================关键函数区域============================================//
//2.左旋
/*
* 对红黑树的节点(x)进行左旋转
*
* 左旋示意图(对节点x进行左旋):
* px px
* / /
* x y
* / \ --(左旋)--> / \ #
* lx y x ry
* / \ / \
* ly ry lx ly
*
*
*/
static void rbtree_left_rotate(RBRoot *root, RBTree x) {
RBTree y = x->right;
x->right = y->left;
if(y->left!=NULL) y->left->parent = x;
y->parent = x->parent;
if (x->parent == NULL) {
root->node = y;
}
else {
if (x->parent->left == x)
x->parent->left = y;
else
x->parent->right = y;
}
x->parent = y;
y->left = x;
}
//3.右旋
/*
* 对红黑树的节点(y)进行右旋转
*
* 右旋示意图(对节点y进行左旋):
* py py
* / /
* y x
* / \ --(右旋)--> / \ #
* x ry lx y
* / \ / \ #
* lx rx rx ry
*
*/
static void rbtree_right_rotate(RBRoot *root, RBTree x) {
RBTree y = x->left;
x->left = y->right;
if (y->right != NULL) y->right->parent = x;
y->parent = x->parent;
if (x->parent == NULL) {
root->node = y;
}
else {
if (x->parent->left == x) {
x->parent->left = y;
}
else {
x->parent->right = y;
}
}
y->right = x;
x->parent = y;
}
//4.添加
//先将红黑树当作一颗二叉查找树,将节点插入;然后,将节点着色为红色;
//最后,通过"旋转和重新着色"等一系列操作来修正该树,使之重新成为一颗红黑树。
void rebtree_insert_fixup(RBRoot*, RBTree);
static void rbtree_insert(RBRoot * root, RBTree newNode) {
Node*y = NULL;
Node*x = root->node;
//1.当作是一棵二叉查找树,将点加入二叉排序树中
while (x != NULL) {
y = x;
if (newNode->key < x->key) { x = x->left; }
else x = x->right;
}
newNode->parent = y;
if (y != NULL) {
if (newNode->key < y->key)
y->left = newNode;
else
y->right = newNode;
}
else {//y是空结点,把newNode作为根节点
root->node = newNode;
}
//2.设置位红色
newNode->color = RED;
//3.修正为一棵二叉排序树
rebtree_insert_fixup(root, newNode);
}
//4.1修正insert
//根据被插入节点的父节点的情况,可以将"当节点z被着色为红色节点,并插入二叉树"划分为三种情况来处理。
//① 情况说明:被插入的节点是根节点。
//处理方法:直接把此节点涂为黑色。
//② 情况说明:被插入的节点的父节点是黑色。
//处理方法:什么也不需要做。节点被插入后,仍然是红黑树。
//③ 情况说明:被插入的节点的父节点是红色,且父节点是爷结点的左孩子
//
//Case 1 当前节点的父节点是红色,且当前节点的祖父节点的另一个子节点(叔叔节点)也是红色。
//(01) 将“父节点”设为黑色。
//(02) 将“叔叔节点”设为黑色。
//(03) 将“祖父节点”设为“红色”。
//(04) 将“祖父节点”设为“当前节点”(红色节点);即,之后继续对“当前节点”进行操作。
//
//Case 2 当前节点的父节点是红色,叔叔节点是黑色,且当前节点是其父节点的右孩子
//(01) 将“父节点”作为“新的当前节点”。
//(02) 以“新的当前节点”为支点进行左旋。
//
//Case 3 当前节点的父节点是红色,叔叔节点是黑色,且当前节点是其父节点的左孩子
//(01) 将“父节点”设为“黑色”。
//(02) 将“祖父节点”设为“红色”。
//(03) 以“祖父节点”为支点进行右旋。
void rebtree_insert_fixup(RBRoot *root, RBTree node) {
Node *parent, *grandparent;
//若父结点存在且父结点是红色
while ((parent=node->parent) &&isRedNode(parent) ) {
grandparent = parent->parent;
if (parent == grandparent->left) {
//Case1:uncle存在且他的颜色是红色的
Node *uncle = grandparent->right;
if (uncle&&isRedNode(uncle)) {
//情况1:父亲和叔叔都是红色
uncle->color = parent->color = BLACK;
grandparent->color = RED;
node = grandparent;
continue;
}
else{ //等效于else if ((uncle && !isRedNode(uncle))||!uncle) {
if (parent->right == node) {
//Case2:父亲是红色,叔叔是黑色,node是右孩子==》父亲为左旋中心,把孩子变成和父亲同线,和Case3一样
//需要转为直线操作
rbtree_left_rotate(root, parent);
Node * temp = parent;
parent = node;
node = temp;
}
else {//Case 3:当前为左孩子,直线操作
//不用转为直线操作
}
parent->color = BLACK;
grandparent->color = RED;
rbtree_right_rotate(root, grandparent);
}
}
else {
Node *uncle = grandparent->left;
if (uncle && isRedNode(uncle)) {
//Case1 1:both red
uncle->color = parent->color = BLACK;
grandparent->color = RED;
node = grandparent;
continue;
}
else {
if (parent->left == node) {//Case 2:需要右旋
rbtree_right_rotate(root, parent);
Node * temp = parent;
parent = node;
node = temp;
}
//Case 3:需要左旋
parent->color = BLACK;
grandparent->color = RED;
rbtree_left_rotate(root, grandparent);
}
}//else
}//while
// 将根节点设为黑色
root->node->color = BLACK;
}
//5.删除
/*
第一步:将红黑树当作一颗二叉查找树,将节点删除。
这和"删除常规二叉查找树中删除节点的方法是一样的"。分3种情况:
① 被删除节点没有儿子,即为叶节点。那么,直接将该节点删除就OK了。
② 被删除节点只有一个儿子。那么,直接删除该节点,并用该节点的唯一子节点顶替它的位置。
③ 被删除节点有两个儿子。那么,先找出它的后继节点;然后把“它的后继节点的内容”复制给“该节点的内容”;
之后,删除“它的后继节点”。在这里,后继节点相当于替身,在将后继节点的内容复制给"被删除节点"之后,再将后继节点删除。
这样就巧妙的将问题转换为"删除后继节点"的情况了,下面就考虑后继节点。
在"被删除节点"有两个非空子节点的情况下,它的后继节点不可能是双子非空。
既然"的后继节点"不可能双子都非空,就意味着"该节点的后继节点"要么没有儿子,要么只有一个儿子。
若没有儿子,则按"情况① "进行处理;
若只有一个儿子,则按"情况② "进行处理。
第二步:通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。
因为"第一步"中删除节点之后,可能会违背红黑树的特性。所以需要通过"旋转和重新着色"来修正该树,使之重新成为一棵红黑树。
*/
void rebtree_delete_fixup(RBRoot*, RBTree,RBTree);
static void rbtree_delete(RBRoot *root, RBTree node) {
RBTree child, parent;
int color=-1;
//左右孩子全有时
if ((node->right != NULL) && (node->left != NULL)) {
RBTree rChild;
RBTree replace = node->right;
while (replace->left != NULL) {
replace = replace->left;
}//replace 是node的中序遍历后继点
//==(1)==动node的父母=====//
if (node->parent != NULL) {
if (node->parent->left == node) {
node->parent->left = replace;
}else
node->parent->right = replace;
}
else root->node = replace;//情况① "node节点"是根节点,更新根节点。
int color = replace->color;
rChild = replace->right; // child是"取代节点"的右孩子,也是需要"调整的节点"。
// "取代节点"肯定不存在左孩子!因为它是一个后继节点。
parent = replace->parent;//先是replace的父母,后来是replace的孩子的父母
//=(2)==动replace的原、现右分支关系====//
//分2种情况,1:replace是node的右结点;2:node和replace中还有很多结点
if (parent == node) {
parent = replace;//不用改动右孩子关系
}
else {
//先连接replace的父子
if (rChild) {
rChild->parent = parent;
}
parent->left = rChild;
//再连接replace的新右孩子
replace->right = node->right;
node->right->parent = replace;
}
//==(3)===动replace左分支现关系===//
replace->color = node->color;
replace->parent = node->parent;
replace->left = node->left;
node->left->parent = replace;
if (color == BLACK) {//双黑情况
rebtree_delete_fixup(root, rChild, parent);
}
free(node);
return;
}
color = node->color;
parent = node->parent;
if (node->right != NULL)
child = node->right;
else
child = node->left;
if (child) {
child->parent = parent;
}
if (parent) {//node不是root
if (parent->left == node) {
parent->left = child;
}
else {
parent->right = child;
}
}
else {//node是root
root->node = child;
}
if (color == BLACK) {
rebtree_delete_fixup(root, child, parent);
}
free(node);
}
//5.1 修正delete
//RB - DELETE - FIXUP的思想是:将x所包含的额外的黑色不断沿树上移(向根方向移动),直到出现下面的姿态:
//a) x指向一个"红+黑"节点。此时,将x设为一个"黑"节点即可。
//b) x指向根。此时,将x设为一个"黑"节点即可。
//c) 非前面两种姿态。
//将上面的姿态,可以概括为3种情况。
//① 情况说明:x是“红 + 黑”节点。
//处理方法:直接把x设为黑色,结束。此时红黑树性质全部恢复。
//② 情况说明:x是“黑 + 黑”节点,且x是根。
//处理方法:什么都不做,结束。此时红黑树性质全部恢复。
//③ 情况说明:x是“黑 + 黑”节点,且x不是根。
//Case 1 x是"黑+黑"节点,x的兄弟节点是红色。(此时x的父节点和x的兄弟节点的子节点都是黑节点)。
//(01) 将x的兄弟节点设为“黑色”。
//(02) 将x的父节点设为“红色”。
//(03) 对x的父节点进行左旋。
//(04) 左旋后,重新设置x的兄弟节点。
//Case 2 x是“黑 + 黑”节点,x的兄弟节点是黑色,x的兄弟节点的两个孩子都是黑色。
//(01) 将x的兄弟节点设为“红色”。
//(02) 设置“x的父节点”为“新的x节点”。
//Case 3 x是“黑 + 黑”节点,x的兄弟节点是黑色;x的兄弟节点的左孩子是红色,右孩子是黑色的。
//(01) 将x兄弟节点的左孩子设为“黑色”。
//(02) 将x兄弟节点设为“红色”。
//(03) 对x的兄弟节点进行右旋。
//(04) 右旋后,重新设置x的兄弟节点。
//Case 4 x是“黑 + 黑”节点,x的兄弟节点是黑色;x的兄弟节点的右孩子是红色的,x的兄弟节点的左孩子任意颜色。
//(01) 将x父节点颜色 赋值给 x的兄弟节点。
//(02) 将x父节点设为“黑色”。
//(03) 将x兄弟节点的右子节设为“黑色”。
//(04) 对x的父节点进行左旋。
//(05) 设置“x”为“根节点”。
void rebtree_delete_fixup(RBRoot*root, RBTree node, RBTree parent) {
//思想:将x所包含的额外的黑色不断沿树上移(向根方向移动)
//node不存在或node是黑色且非根节点
RBTree broNode;
while ((!node || isBlackNode(node)) && node != root->node) {
if (node == parent->left) {
broNode = parent->right;
//Case 1:x是"黑+黑"节点,x的兄弟节点是红色
if (isRedNode(broNode)) {
broNode->color = BLACK;
parent->color = RED;
rbtree_left_rotate(root, parent);
broNode = parent->right;
}//目的是将“Case 1”转换为“Case 2”、“Case 3”或“Case 4”,从而进行进一步的处理。
//Case 2 x是“黑 + 黑”节点,x的兄弟节点是黑色,x的兄弟节点的两个孩子都是黑色(NULL也是黑色)
if ((!broNode->right || isBlackNode(broNode->right)) &&
(!broNode->left|| isBlackNode(broNode->left))) {
broNode->color = RED;
node = parent;
parent = node->parent;
}//目的:所有经过x的兄弟节点的分支中黑色节点的个数减1
else {
if (!broNode->right || isBlackNode(broNode->right)) {
//Case 3 x是“黑 + 黑”节点,x的兄弟节点是黑色;x的兄弟节点的左孩子是红色,右孩子是黑色的。
broNode->left->color = BLACK;
broNode->color = RED;
rbtree_right_rotate(root, broNode);
broNode = parent->right;
}// 使得parent-broNode-(broNode->right) :红-黑-红直线
// Case 4: x的兄弟w是黑色的;并且w的右孩子是红色的,左孩子任意颜色。
broNode->color=node->parent->color;
parent->color = BLACK;
broNode->right->color = BLACK;
rbtree_left_rotate(root, parent);
node = root->node;
break;
}
}
else {
broNode = parent->left;
//Case 1:x是"黑+黑"节点,x的兄弟节点是红色
if (isRedNode(broNode)) {
broNode->color = BLACK;
parent->color = RED;
rbtree_right_rotate(root, parent);
broNode = parent->left;
}//目的是将“Case 1”转换为“Case 2”、“Case 3”或“Case 4”,从而进行进一步的处理。
//Case 2 x是“黑 + 黑”节点,x的兄弟节点是黑色,x的兄弟节点的两个孩子都是黑色(NULL也是黑色)
if ((!broNode->right || isBlackNode(broNode->right)) &&
(!broNode->left || isBlackNode(broNode->left))) {
broNode->color = RED;
node = parent;
parent = node->parent;
}//目的:所有经过x的兄弟节点的分支中黑色节点的个数减1
else {
if (!broNode->left || isBlackNode(broNode->left)) {
//Case 3 x是“黑 + 黑”节点,x的兄弟节点是黑色;x的兄弟节点的右孩子是红色,左孩子是黑色的。
broNode->right->color = BLACK;
broNode->color = RED;
rbtree_left_rotate(root, broNode);
broNode = parent->left;
}// 使得parent-broNode-(broNode->right) :红-黑-红直线
// Case 4: x的兄弟w是黑色的;并且w的左孩子是红色的,右孩子任意颜色。
broNode->color = node->parent->color;
parent->color = BLACK;
broNode->left->color = BLACK;
rbtree_right_rotate(root, parent);
node = root->node;
break;
}
}
}
//node根节点
if (node) node->color = BLACK;
}
//===================================启动和终止===========================================//
static Node* create_rbtree_node(Type key, Node *parent, Node *left, Node* right)
{
Node* p = (Node *)malloc(sizeof(Node));
if (p == NULL)
return NULL;
p->key = key;
p->left = left;
p->right = right;
p->parent = parent;
p->color = BLACK; // 默认为黑色
return p;
}
static Node* search(RBTree x, Type key)
{
if (x == NULL || x->key == key)
return x;
if (key < x->key)
return search(x->left, key);
else
return search(x->right, key);
}
bool rbtree_search(RBRoot *root, Type key)
{
if (root)
return search(root->node, key) ? true : false;
else
cout <<key<< "不存在" << endl;
return false;
}
void print_rbtree(RBRoot *);
/*
* 新建结点(节点键值为key),并将其插入到红黑树中
*
* 参数说明:
* root 红黑树的根
* key 插入结点的键值
* 返回值:
* 0,插入成功
* -1,插入失败
*/
bool insert_rbtree(RBRoot * & root, Type key)
{
Node *node= (Node *)malloc(sizeof(Node)); // 新建结点
// 不允许插入相同键值的节点。
// (若想允许插入相同键值的节点,注释掉下面两句话即可!)
if (search(root->node, key) )
return false;
// 如果新建结点失败,则返回。
if ((node = create_rbtree_node(key, NULL, NULL, NULL)) == NULL)
return false;
rbtree_insert(root, node);
cout << endl;
print_rbtree(root);
cout << endl;
return true;
}
/*
* 删除键值为key的结点
*
* 参数说明:
* tree 红黑树的根结点
* key 键值
*/
void delete_rbtree(RBRoot *root, Type key)
{
Node *z;
if ((z = search(root->node, key)) != NULL)
rbtree_delete(root, z);
else cout <<key<< "不存在" << endl;
}
//6.销毁
static void destroyTree(RBTree tree) {
if (!tree) return;
if (tree->left)destroyTree(tree->left);
if (tree->right)destroyTree(tree->right);
free(tree);
}
void rbtree_destroy(RBRoot * & root) {
if (root != NULL) {
destroyTree(root->node);
cout << "delete finished." << endl;
}
free(root);
}
//===============================拓展================================================//
//7.前序遍历
void preOrder(RBTree tree) {
if (tree) {
cout << tree->key<<" ";// << "--" << (tree->color == 0 ? "red" : "black") << endl;
preOrder(tree->left);
preOrder(tree->right);
}
}
static void preorder_rbtree(RBRoot * root) {
if (root) {
preOrder(root->node);
}
}
//8.中序
void InOrder(RBTree tree) {
if (tree) {
InOrder(tree->left);
cout << tree->key << " ";
InOrder(tree->right);
}
}
static void Inorder_rbtree(RBRoot * root) {
if (root) {
InOrder(root->node);
}
}
/*
* 9 .打印"红黑树"
*
* tree -- 红黑树的节点
* key -- 节点的键值
* direction -- 0,表示该节点是根节点;
* -1,表示该节点是它的父结点的左孩子;
* 1,表示该节点是它的父结点的右孩子。
*/
static void rbtree_print(RBTree tree, Type key, int direction)
{
if (tree != NULL)
{
if (direction == 0) // tree是根节点
printf("%2d(B) is root\n", tree->key);
else // tree是分支节点
printf("%2d(%s) is %2d's %6s child\n", tree->key, isRedNode(tree) ? "R" : "B", key, direction == 1 ? "right" : "left");
rbtree_print(tree->left, tree->key, -1);
rbtree_print(tree->right, tree->key, 1);
}
}
void print_rbtree(RBRoot *root)
{
if (root != NULL && root->node != NULL)
rbtree_print(root->node, root->node->key, 0);
else
cout << "为空树" << endl;
}
//9 .根结点中序遍历的前驱
//9.1最大节点
static RBTree rbtree_max(RBTree tree) {
if (tree == NULL) return NULL;
else if (tree->right == NULL) return tree;
else return rbtree_max(tree->right);
}
static RBTree rbtree_predecessor(RBTree treeNode) {
// 如果x存在左孩子,则"x的前驱结点"为 "以其左孩子为根的子树的最大结点"。
if (treeNode->left != NULL) return rbtree_max(treeNode->left);
else {
RBTree parent = treeNode->parent;
// 如果x没有左孩子。则x有以下两种可能:
// (01) x是"一个右孩子",则"x的前驱结点"为 "它的父结点"。
// (01) x是"一个左孩子",则查找"x的最低的父结点,并且该父结点要具有右孩子",找到的这个"最低的父结点"就是"x的前驱结点"。
while ((parent != NULL) && (parent->left == treeNode)) {
treeNode = parent;
parent = parent->parent;
}
return parent;
}
}
//10.根结点中序遍历后继
static RBTree rbtree_min(RBTree tree) {
if (tree == NULL) return NULL;
else if (tree->left == NULL) return tree;
else return rbtree_min(tree->left);
}
static RBTree rbtree_successor(RBTree treeNode) {
// 如果x存在右孩子,则"x的后驱结点"为 "以其右孩子为根的子树的最小结点"。
if (treeNode->right != NULL) return rbtree_min(treeNode->right);
else {
RBTree parent = treeNode->parent;
// 如果x没右孩子。则x有以下两种可能:
// (01) x是"一个左孩子",则"x的前驱结点"为 "它的父结点"。
// (01) x是"一个右孩子",则查找"x的最低的父结点,并且该父结点要具有孩子",找到的这个"最低的父结点"就是"x的前驱结点"。
while ((parent != NULL) && (parent->right == treeNode)) {
treeNode = parent;
parent = parent->parent;
}
return parent;
}
}
int main() {
RBRoot * root= createRoot();
Type key;
srand(unsigned(time(NULL)));
for (int i = 0; i < 9; i++) {
//cin >> key;
key = rand()%100;
insert_rbtree(root, key);
}
cout << "先序遍历"; preorder_rbtree(root);
cout << "中序遍历"; Inorder_rbtree(root);
cout << endl;
print_rbtree(root);
cout<<"root的前驱"<<rbtree_predecessor(root->node)->key;//测试前驱结点
cout << "root的后继" << rbtree_successor(root->node)->key;//测试后继结点
cout << "Input a number to be deleted:" << endl; cin >> key;
delete_rbtree(root, key);
cout << endl;
print_rbtree(root);
cout << endl;
cout << "Input a number to be checked whether it does exit in the chart? :" << endl; cin >> key;
cout << rbtree_search(root, key) << endl;
rbtree_destroy(root);
}