红黑树学习笔记
红黑树的5个性质
- 每个结点要么是红的,要么是黑的。
- 根结点是黑的。
- 每个叶结点(叶结点即指树尾端NIL指针或NULL结点)是黑的。
- 如果一个结点是红的,那么它的俩个儿子都是黑的。
- 对于任一结点而言,其到叶结点树尾端NIL指针的每一条路径都包含相同数目的黑结点。
注意黑色的nil结点,对于红黑树的删除的理解很重要。
红黑树的插入
插入一个初始为红色的节点,有以下3种情况。
1.空树,使其颜色转为黑,成为根。
2.父为黑,无须调整。
3.父为红,根据以下图片进行调整。
流程图如下所示
根据图片就可以写出代码了~~~
红黑树的删除
红黑树的删除有点难以理解。
如果删除一个节点,他有后继节点,我们就用它的后继节点(或者前继节点)转变成它的颜色顶替它的位置。
所以我们不用考虑删除的节点是什么颜色,而是应该考虑顶替它的后继节点的什么颜色。
因为是后继节点,所以最终删除的要么只有一个右孩子,要么就是叶子节点(有两个nil节点)。
基于以上的情况,如果删除的该节点是红色,那么直接删除就可以了。
如果是黑色,那么就有以下两种情况
1.删除的节点(5)只有一个红色的右孩子(对于用后继节点顶替来考虑),则将儿子-》黑色-》结束。
2.删除的节点(5)的儿子是黑色(包括nil节点),将以儿子为当前点,进行调整。**
下面只分析将删除的节点的儿子为当前点,并且是左儿子的情况(也就是删除的最终节点是父节点的左儿子的情况…好难说清楚…)
比如下面删除2节点,用5节点(2的后继)顶替,那么5才是认为的删除的节点。然后5就是5的父节点的左儿子,下面处理的就是这种情况。
删除节点2后
注意此时6一定有兄弟节点,不然就不是红黑树了!!
然后将6做为当前点,按下图进行操作即可~(编程时要加上是右儿子的情况)
感觉删除那里很难理解~这是对之前算法复习时候笔记的整理,有点久了,可能记错了,请不要怪罪 = =
// RBTree.cpp : Defines the entry point for the console application.
//
/* 性质1. 节点是红色或黑色。 性质2. 根节点是黑色。 性质3 每个叶节点(NIL节点,空节点)是黑色的。 性质4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点) 性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。 */
#include "stdafx.h"
#include <iostream>
#include <queue>
using namespace std;
struct Node
{
int key;//结点的值
char color;
Node *left, *right, *parent;
// Constructor
Node(int key)
{
this->key = key;
left = right = parent = NULL;
color = 'R';
}
};
static Node* Tnil = new Node(-1);
// Class to represent Red-Black Tree
class RBTree
{
public:
Node *root;
protected:
void rotateLeft(Node *&, Node *&);//左旋操作,传入根结点和旋转的结点,成为自己右孩子的左孩子
void rotateRight(Node *&, Node *&); //右旋操作,传入根结点和旋转的结点,成为自己左孩子的右孩子
void transplant(Node*&u, Node*&v);//v子树代替u子树
Node* minimum(Node*);//某个子树的最小值
Node* find(Node*root,int data);//某值
void RBTInsertFixup(Node *&, Node *&);
void RBTDeleteFixup(Node *&, Node *&);
Node* RBTInsert(Node*&root, Node *z);
public:
// Constructor
RBTree() {
root = Tnil;
Tnil->color = 'B';
}
void insert(const int &n);
void deleteN(const int &n);
void clear(Node *&);
Node* RBTDelete(Node*&root, Node *z);//删除z结点
};
void RBTree::clear(Node*&d)
{
//判断二叉树是否为空
if (d != Tnil)
{
//1.清空左子树
clear(d->left);
//2.清空右子树
clear(d->right);
//3.清空根节点
// cout << d->key << endl;
delete d;
d = NULL;
}
d = Tnil;
}
// 对违反红黑树的地方进行调整
void RBTree::RBTInsertFixup(Node *&root, Node *&x)
{
Node *parent_x = Tnil;
Node *grand_parent_x = Tnil;
while ((x != root) && (x->parent->color == 'R') && (x->color != 'B'))//当x的父结点为红色时,才需要调整,循环到当前点的父结点为黑色为至
{
parent_x = x->parent;//当前点的父节点
grand_parent_x = x->parent->parent;//当前点的祖父节点
//父为祖父左孩子
if (parent_x == grand_parent_x->left)
{
Node *uncle_x = grand_parent_x->right;//叔结点
if (uncle_x != Tnil &&uncle_x->color == 'R')//叔结点为红色
{
parent_x->color = 'B';
uncle_x->color = 'B';
grand_parent_x->color = 'R';
x = grand_parent_x;
}
else//叔节点为黑色
{
if (x == parent_x->right)
{
x = parent_x;
rotateLeft(root, x);
}
x->parent->color = 'B';
x->parent->parent->color = 'R';
rotateRight(root, grand_parent_x);
x = x->parent;
}
}
else//父为祖父右孩子
{
Node *uncle_x = grand_parent_x->left;//叔结点
if (uncle_x != Tnil &&uncle_x->color == 'R')//叔结点为红色
{
parent_x->color = 'B';
uncle_x->color = 'B';
grand_parent_x->color = 'R';
x = grand_parent_x;
}
else//叔节点为黑色
{
if (x == parent_x->left)
{
x = parent_x;
rotateRight(root, x);
}
x->parent->color = 'B';
x->parent->parent->color = 'R';
rotateLeft(root, grand_parent_x);
x = x->parent;
}
}
}
root->color = 'B';
}
void RBTree::RBTDeleteFixup(Node *&root, Node *&x)
{
Node* sibling = Tnil;
while (x != root&&x->color == 'B')
{
if (x==x->parent->left)//x是其父亲的左孩子
{
sibling = x->parent->right;
if (sibling->color=='R')//第一种情况,兄弟颜色为红
{
sibling->color = 'B';
x->parent->color = 'R';
rotateLeft(root, x->parent);
sibling = x->parent->right;//兄弟节点变了
}
else//兄弟颜色为黑
{
if (sibling->left->color=='B'&&sibling->right->color=='B')//情况二,兄弟的两个孩子都为黑色
{
sibling->color = 'R';
x = x->parent;
}
else
{
if (sibling->right->color=='B')//情况三,兄弟右子为黑,左子为红
{
sibling->color = 'R';
sibling->left->color = 'B';
rotateRight(root, sibling);
sibling = x->parent->right;//兄弟又变了
}
sibling->color = x->parent->color;//情况四,兄弟右子为红,左子颜色任意。
x->parent->color = 'B';
sibling->right->color = 'B';
rotateLeft(root, x->parent);
x = root;//结束
}
}
}
else//x是其父亲的右孩子
{
sibling = x->parent->left;
if (sibling->color == 'R')//第一种情况,兄弟颜色为红
{
sibling->color = 'B';
x->parent->color = 'R';
rotateRight(root, x->parent);
sibling = x->parent->left;//兄弟节点变了
}
else//兄弟颜色为黑
{
if (sibling->left->color == 'B'&&sibling->right->color == 'B')//情况二,兄弟的两个孩子都为黑色
{
sibling->color = 'R';
x = x->parent;
}
else
{
if (sibling->left->color =='B')//情况三,兄弟左子为黑,右子为红
{
sibling->color = 'R';
sibling->right->color = 'B';
rotateLeft(root, sibling);
sibling = x->parent->left;//兄弟又变了
}
sibling->color = x->parent->color;//情况四,兄弟左子为红,右子颜色任意。
x->parent->color = 'B';
sibling->left->color = 'B';
rotateRight(root, x->parent);
x = root;//结束
}
}
}
}
x->color = 'B';
}
/* 像普通二叉树一样插入,返回根结点*/
Node* RBTree::RBTInsert(Node* &root, Node *z)
{
Node*y = Tnil;//记录当前x的父结点
Node*x = root;
while (x != Tnil)
{
y = x;
if (z->key < x->key)
x = x->left;
else
x = x->right;
}
z->parent = y;
if (y == Tnil)
{
z->color = 'B';
root = z;
}
else if (z->key < y->key)
{
y->left = z;
}
else
{
y->right = z;
}
if (y->color=='R')
RBTInsertFixup(root, z);
return root;
}
//左旋操作,传入根结点和旋转的结点,成为自己右孩子的左孩子
void RBTree::rotateLeft(Node *&root, Node *&x)
{
Node *y = x->right;//y是x的右孩子
x->right = y->left;
if (x->right != Tnil)//原来y没有左子树的就不需要改变y子树的父结点指向
x->right->parent = x;
y->parent = x->parent;//修改y的父结点
//x的父结点有以下三种情况
if (x->parent == Tnil)//如果原来x是根结点,则y变成新的根结点
root = y;
else if (x == x->parent->left)//x原来是x父结点的左孩子
x->parent->left = y;
else
x->parent->right = y;//x原来是x父结点的右孩子
y->left = x;
x->parent = y;
}
//右旋操作,传入根结点和旋转的结点,成为自己左孩子的右孩子
void RBTree::rotateRight(Node*&root, Node*&x)
{
Node*y = x->left;
x->left = y->right;
if (y->right != Tnil)
x->left->parent = x;
y->parent = x->parent;
if (x->parent == Tnil)
root = y;
else if (x == x->parent->left)
x->parent->left = y;
else
x->parent->right = y;
y->right = x;
x->parent = y;
}
// Function to insert a new node with given key
void RBTree::insert(const int &key)
{
Node *x = new Node(key);
x->parent = x->left = x->right = Tnil;
// Do a normal BST insert
root = RBTInsert(root, x);
}
void RBTree::deleteN(const int &key)
{
Node *x = find(root,key);
if(x!=Tnil)
// Do a normal BST insert
root = RBTDelete(root, x);
}
//v子树代替u子树
void RBTree::transplant(Node*&u, Node*&v)
{
if (u->parent == Tnil)//u为根节点
root = v;
else if (u == u->parent->left)//u为其父的左节点
u->parent->left = v;
else
u->parent->right = v;
/* if (v != Tnil)*/
v->parent = u->parent;
}
Node*RBTree::minimum(Node*x)//x子树的最小值
{
while (x->left != Tnil)
{
x = x->left;
}
return x;
}
Node * RBTree::find(Node *root,int key)
{
//int value = root->key;
while (root->key!=key)
{
if (key > root->key)
root = root->right;
else
root = root->left;
if (root == Tnil)
{
cout << "树中没有 " << key << "这个值" << endl;
return Tnil;
}
}
return root;
}
Node* RBTree::RBTDelete(Node*&root, Node *z)//删除z结点
{
Node*y = z;
Node*x = Tnil;
char y_original_color = y->color;
// if (z->left==Tnil&&z->right==Tnil)
// {
// if (z->parent->left == z)
// z->parent->left = Tnil;
// else
// z->parent->right = Tnil;
// return root;
// }
if (z->left == Tnil)
{
x = z->right;
transplant(z, x);
}
else if (z->right == Tnil)
{
x = z->left;
transplant(z, x);
}
else
{
y = minimum(z->right);//y为z的后继
y_original_color = y->color;
x = y->right;
if (y->parent == z)
x->parent = y;
else
{
transplant(y, y->right);
y->right = z->right;
y->right->parent = y;
}
transplant(z, y);
y->left = z->left;
y->left->parent = y;
y->color = z->color;
}
if (y_original_color == 'B')
RBTDeleteFixup(root, x);//x可能引起红黑树的性质被破坏
return root;
}
/* 树形打印作用域 root:子树根 blk:缩进次数 */
void printTree(Node*root, int blk)
{
if (root == Tnil)
return;
printTree(root->right, blk + 1);
for (int i = 0; i < blk; i++)printf("| ");//缩进时输出"|"符号
//for (int i = 0; i<blk; i++)printf(" ");//缩进
printf("|—<%d %c>\n", root->key, root->color);//打印"|—<id>"形式
printTree(root->left, blk + 1);
}
int main()
{
//FILE *stream;
//freopen_s(&stream,"D:\\xx.txt", "w", stdout);
RBTree tree;
int data,sum;
cout << "请输入树的节点总数,输入0结束" << endl;
while (scanf_s("%d", &sum) != EOF) {
RBTree tree;
if (sum == 0 )
break;
cout << "请输入节点数值" << endl;
for (int i=0;i<sum; i++)
{
cin >> data;
tree.insert(data);
}
cout << "======================" << endl;
printTree(tree.root, 0);
cout << endl << "输入要删除的节点,输入-1表示结束" << endl;
cin >> data;
while (data!=-1)
{
tree.deleteN(data);
cout << "删除" << data << "后:" << endl;
cout << "======================" << endl;
printTree(tree.root, 0);
cout << endl << "输入要删除的节点,输入-1表示结束" << endl;
cin >> data;
}
tree.clear(tree.root);
printTree(tree.root, 0);
cout << endl << "输入要删除的节点,输入-1表示结束" << endl;
cout << "请输入树的节点总数,输入0结束" << endl;
}
return 0;
}
借鉴了几个博客(并偷了两张图片)和算法导论。没有记下博客。。。就不贴出来了。谢谢在网上分享的各位大神。