红黑树插入算法的C++实现

红黑树插入算法的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觉得我写的不错的话,给我点个赞或者评论下都行,拜拜啦

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值