红黑树插入算法的C++实现
参考教材:算法导论第三版
1. 首先建立红黑树节点和红黑树(按照教材上红黑树定义):
enum Color{ red, black };
struct TNode{
Color color;
int key;
TNode* left;
TNode* right;
TNode* p; //父节点指针
};
class RbTree{
public:
TNode* nil = new TNode{black, 0, NULL, NULL, NULL};
TNode* root = nil;
int size = 0; //节点数量
};
2. 左旋、右旋实现
左旋:当在某个节点x上做左旋是,假设它的右孩子为y而不是T.nil; 左旋以x到y的链为“支轴”进行。它使y成为该子树的新的根节点,x成为y的左孩子,y的左孩子成为x的右孩子;
左旋、右旋的示意图如下:
算法步骤示意图:
相关代码及注释:
void left_rotate(RbTree &T, TNode* x){
//将y置为x的右孩子
TNode* y = x->right;
//将x的右孩子置为y的左孩子
x->right = y->left;
// 将y左孩子(非空)的父节点置为x
if(y->left != T.nil){
y->left->p = x;
}
// 将y的父节点置为x的父节点
y->p = x->p;
// 若x父节点为nil,则说明原x为根节点,设置y为新根
if(x->p == T.nil){
T.root = y;
}
// 若原来x是x父节点((非空) 的左(右)节点,则将x的父节点左(右)指针设置为y
else if(x == x->p->left){
x->p->left = y;
}
else x->p->right = y;
// 将y的左孩子设为x,将x的父节点设为y
y->left = x;
x->p = y;
}
void right_rotate(RbTree &T, TNode* y){ //右旋相当于将左旋反过来,不做详细注释
TNode* x = y->left;
y->left = x->right;
if(x->right != T.nil){
x->right->p = y;
}
x->p = y->p;
if(y->p == T.nil){
T.root = x;
}
else if(y == y->p->left){
y->p->left = x;
}
else y->p->right = x;
x->right = y;
y->p = x;
}
3. 插入算法:BST树的插入思想 + 红黑树调整
插入过程代码:
void rb_insert(RbTree &T, TNode* z){ //z节点插入到 T树中
// x用于遍历指针,起始值为root
// y始终指向x的父节点,起始值为T.nil
TNode* y = T.nil;
TNode* x = T.root;
// 开启循环寻找插入位置,循环结束后x指向空节点,y指向x的父节点
while(x != T.nil){
y = x;
if(z->key < x->key)
x = x->left;
else
x = x->right;
}
// 将z的p节点设为y,
z->p = y;
// 若y为nil,则说明T为空树,设置z为新根
if(y == T.nil)
T.root = z;
// 若y非空,则按数值大小设为左孩子或右孩子
else if(z->key < y->key)
y->left = z;
else y->right = z;
// 将z的左右孩子都设为nil,颜色设为red
z->left = T.nil;
z->right = T.nil;
z->color = red;
// 插入成功,节点数++
T.size++;
// 调整红黑树
rb_insert_fixup(T, z);
}
Fixup调整过程代码如下:
void rb_insert_fixup(RbTree &T, TNode* z){ //z为T树中待调整节点(刚插入的)
TNode* y;
// 若z的父节点为红色才需要调整,若为黑色不需要调整
// 分为两大类共6种情况
while(z->p->color == red){
if(z->p == z->p->p->left){ //一类:z的父节点是z祖父节点的左孩子
y = z->p->p->right;
if(y->color == red){ //case1:叔叔节点为红色;
z->p->color = black; //处理:父、叔、祖父变色 + 矛盾上移到祖父
y->color = black;
z->p->p->color = red;
z = z->p->p;
}
else if(z == z->p->right){ //case2:叔叔节点为黑色 + z是z父的右孩子
z = z->p; //处理: 左旋变换为case3
left_rotate(T, z);
}
else{ //case3: 叔叔节点为黑色 + z是z父的左孩子
z->p->color = black; //处理:父、祖父变色 + 右旋 = 成功了
z->p->p->color = red;
right_rotate(T, z->p->p);
}
}
// 二类:z的父节点是z祖父节点的右孩子,情况和一类相反
// 把一类的代码,left和right互换即可
else{
y = z->p->p->left;
if(y->color == red){
z->p->color = black;
y->color = black;
z->p->p->color = red;
z = z->p->p;
}
else if(z == z->p->left){
z = z->p;
right_rotate(T, z);
}
else{
z->p->color = black;
z->p->p->color = red;
left_rotate(T, z->p->p);
}
}
}
T.root->color = black;
}
4. 算法正确性测试
主程序测试说明
程序输入:
文件名: insert.txt
文件格式:第一行为待插入数据的个数,第二行为待插入的数据(int类型, 空格分割)
注:1)初始时红黑树应为空。
2)按顺序插入, 例如,对于下图的数据,插入顺序应为 20,10,14
程序输出:
将插入完成后的红黑树进行 “先序遍历(NLR)” 和 “中序遍历(LNR)” 并将相应的遍历序列输出到文件中。
文件名: LNR.txt 中序遍历序列结果
NLR.txt 先序遍历序列结果
格式:每一行对应一个节点的信息(key, color)
主程序代码如下:
int main(){
ifstream ifile;
ofstream ofile1;
ofstream ofile2;
ifile.open("insert.txt");
ofile1.open("NLR.txt");
ofile2.open("LNR.txt");
RbTree t;
TNode* x;
int n;
ifile>>n; //输入插入节点数
for(int i = 0; i < n; i++){
x = new TNode;
ifile>>x->key;
cout<<x->key<<" ";
rb_insert(t, x);
}
cout<<endl<<t.size;
cout<<"先序遍历输出到文件NLR.txt:"<<endl;
pre_order(t, t.root, ofile1);
cout<<endl;
cout<<"中序遍历输出到文件LNR.txt:"<<endl;
in_order(t, t.root, ofile2);
ifile.close();
ofile1.close();
ofile2.close();
return 0;
}
先序遍历中序遍历代码如下:
void pre_order(RbTree &T, TNode* x, ofstream& ofile){
if(x == T.nil)
return;
ofile<<x->key<<", ";
if(x->color == red) ofile<<"red"<<endl;
else ofile<<"black"<<endl;
pre_order(T, x->left, ofile);
pre_order(T, x->right, ofile);
}
void in_order(RbTree &T, TNode* x, ofstream& ofile){
if(x == T.nil)
return;
in_order(T, x->left, ofile);
ofile<<x->key<<", ";
if(x->color == red) ofile<<"red"<<endl;
else ofile<<"black"<<endl;
in_order(T, x->right, ofile);
}
检测结果
1.输入数据如下:8个待插入数据
2.插入调整之后的红黑树先序遍历和中序遍历输出结果:
先序遍历NLR结果如下:
中序遍历结果如下:
3.结论:代码运行没有问题,数据处理结果正确,实验结果符合预期要求
5. 实验过程中遇到的问题:
问题:红黑树调整阶段的fixup算法思想了解不够深,照搬教材代码导致运行失败,经过逐步调试才发现问题所在:
教材伪代码应该稍作修改,程序才能运行,修改如下:
后续计划:
有时间的话,准备把红黑树的删除算法实现一下,删除算法共分为8种情况,理解起来都挺费劲的
如果xdm觉得我写的不错的话,给我点个赞或者评论下都行,拜拜啦