C++ 写一颗红黑树 代码

红黑树代码

#pragma once

const bool rb_red = false;
const bool rb_black = true;

class  node {
public:
	node(int n, bool color_type = true) :val(n), color(color_type), left(nullptr), right(nullptr), parent(nullptr) {};
	int val;
	node* left;
	node* right;
	node* parent;
	bool color;
};

template<typename T>
class rbtree
{
public:
	rbtree(node* _root = nullptr) :root(_root) {};
	void insert(T x);
	node* get_root() { return root; }
	node* predecessor(node* p);
	node* successor(node* p);
	void remove(T x);  //删除节点
	node* get_node(T x);		//根据键值找节点
private:
	
	void left_rotate(node* p);
	void fix_after_insert(node* p);
	void right_rotate(node* p);
	void fix_after_remove(node* p);
	bool get_color(node* p);
	node* root;
};
template<typename T>
bool rbtree<T>::get_color(node* p)
{
	if (p == nullptr)
		return rb_black;	//空节点默认黑色
	else
		return p->color;
}

/*寻找后继节点,即大于p->val的最小值*/
template<typename T>
node* rbtree<T>::successor(node* p)
{
	if (p == nullptr)
		return nullptr;
	else if (p->right)	//右节点都是大于p的
	{
		node* rbnode = p->right;
		while (rbnode->left)	//往左手边找小值
		{
			rbnode = rbnode->left;
		}
		return rbnode;		//后继节点一定没有左子树
	}
	else    //子节点没有小值,考虑往父节点方向找,删除操作时不执行此步
	{
		node* parent_node = p->parent;
		node* child_node = p;
		while (parent_node != nullptr && child_node == parent_node->right)
		{
			child_node = parent_node;
			parent_node = parent_node->parent;
		}
		return parent_node;
	}

}


/*寻找前驱节点,即小于p->val的最大值*/
template<typename T>
node* rbtree<T>::predecessor(node* p)
{
	if (p == nullptr)
		return nullptr;
	else if (p->left)		//左节点都是小于p的
	{
		node* rbnode = p->left;
		while (rbnode->right)	//往右手边找大值
		{
			rbnode = rbnode->right;
		}
		return rbnode;
	}
	else  //子节点没有小值,考虑往父节点方向找,删除操作时不执行此步
	{
		node* parent_node = p->parent;
		node* child_node = p;
		while (parent_node!=nullptr&& child_node == parent_node->left)
		{
			child_node = parent_node;
			parent_node = parent_node->parent;
		}
		return parent_node;
	}
}

/*删除节点
1.叶子节点直接删除。
2.只有一个子节点,子节点直接代替。
3.有两个子节点的话,需要找到前驱节点或者后继节点。
本函数采用后继节点方式
*/
template<typename T>
void rbtree<T>::remove(T x)
{
	node* delet_node = get_node(x);	//找到键值对应节点
	if (delet_node == nullptr)
		return;

	if (delet_node->left != nullptr && delet_node->right != nullptr)	//3.有两个子节点的话,需要找到前驱节点或者后继节点
	{
		node* successor_node = successor(delet_node);   //找后继节点
		delet_node->val = successor_node->val;		//相当于后继节点替代删除节点,不会破坏搜索树的结构
		delet_node = successor_node;				//现在情况变为删除后继节点,注意后继节点没有左子树
	}
	node* replace_node = delet_node->left != nullptr ? delet_node->left : delet_node->right;	//找替代节点

	if (replace_node != nullptr)
	{
		//替代节点认爹
		replace_node->parent = delet_node->parent;
		if (delet_node->parent == nullptr)	      //删除的是有一个子节点的根节点,注意,两个子节点的情况经上面处理后会变成后继节点
		{
			root = replace_node;
		}
		else if (delet_node == delet_node->parent->left)	//替代到左子树位置上,爹找儿子
		{
			delet_node->parent->left = replace_node;
		}
		else if(delet_node == delet_node->parent->right)   //替代到右子树位置上
		{
			delet_node->parent->right = replace_node;
		}
		
		//结构发生变化,需要重新调整树
		if (delet_node->color == rb_black)	//删除黑色节点才需要调整
		{
			fix_after_remove(replace_node);		//替代节点一定是红色
		}
		delet_node->left = delet_node->right = delet_node->parent = nullptr;	//删除节点变为游离节点
		//delete delet_node;
		
	}
	else if (delet_node->parent == nullptr)		//删除的是孤独的根节点
	{
		this->root = nullptr;
	}
	else	//删除的是叶子节点
	{
		if (delet_node->color == rb_black)		//删除黑色节点才需要调整
		{
			fix_after_remove(delet_node);
		}

		if (delet_node == delet_node->parent->left)		//父节点失去左儿子
		{
			delet_node->parent->left = nullptr;
		}
		else if(delet_node == delet_node->parent->right)
		{
			delet_node->parent->right = nullptr;		//父节点失去右儿子
		}
		delet_node->parent = nullptr;
	}


}


/*根据键值找节点*/
template<typename T>
node* rbtree<T>::get_node(T x)
{
	node* rb_node = this->root;
	while (rb_node != nullptr)
	{
		if (x < rb_node->val)
			rb_node = rb_node->left;
		else if (x > rb_node->val)
			rb_node = rb_node->right;
		else
			return rb_node;
	}
	return nullptr;
}


/*二叉树调整变色*/
template<typename T>
void rbtree<T>::fix_after_insert(node* t)
{
	t->color = rb_red;
	//父节点是黑色就不用考虑调整
	while (t != root && t->parent->color == rb_red)	//父节点是红色节点就一直调整
	{
		node* parent_ptr = t->parent;
		if (t->parent == parent_ptr->parent->left)  //父节点是爷爷的左子节点,左边有3个节点(包括爷爷)情况
		{
			node* uncle_node = parent_ptr->parent->right;
			bool uncle_color = (uncle_node == nullptr ? rb_black : uncle_node->color);   //叔叔节点为空时默认为黑色节点
			if (uncle_color == rb_red)
				//叔叔节点为红色,说明新插入的元素合并了2-3-4树的4-节点,此时元素插入位置上方是一黑(爷爷节点)和两红(父亲和叔叔)结构
				//此时新插入元素和父亲节点构成了连续的两个红色节点,需要调整颜色
			{
				parent_ptr->color = rb_black;
				uncle_node->color = rb_black;	//叔叔和父亲变黑
				parent_ptr->parent->color = rb_red;	//爷爷变红
				t = parent_ptr->parent;
				//递归操作,t变为爷爷节点,再while判断爷爷节点的父节点是否是红色,如果是红色就继续调整

			}
			else    //叔叔节点为黑色,说明新插入的元素合并了2-3-4树的3-节点
			{
				if (t == parent_ptr->right)		//新插入元素在父节点右侧,需要调整成3节点左倾结构
				{
					t = parent_ptr;
					left_rotate(t);	//原来父亲节点左旋,t左旋完重新变成子节点
				}
				t->parent->color = rb_black;	//父亲变黑
				t->parent->parent->color = rb_red;		//爷爷变红
				right_rotate(t->parent->parent);	//爷爷节点右旋

			}
		}
		else	//父节点是爷爷的右子节点,右边有3个节点(包括爷爷)情况
		{
			node* uncle_node = parent_ptr->parent->left;
			bool uncle_color = (uncle_node == nullptr ? rb_black : uncle_node->color);   //叔叔节点为空时默认为黑色节点
			if (uncle_color == rb_red)
				//叔叔节点为红色,说明新插入的元素合并了2-3-4树的4-节点,此时元素插入位置上方是一黑(爷爷节点在上面)和两红(父亲和叔叔)结构
				//此时新插入元素和父亲节点构成了连续的两个红色节点,需要调整颜色
			{
				parent_ptr->color = rb_black;
				uncle_node->color = rb_black;	//叔叔和父亲变黑
				parent_ptr->parent->color = rb_red;	//爷爷变红
				t = parent_ptr->parent;
				//递归操作,t变为爷爷节点,再while判断爷爷节点的父节点是否是红色,如果是红色就继续调整
			}
			else    //叔叔节点为黑色,说明新插入的元素合并了2-3-4树的3-节点
			{
				if (t == parent_ptr->left)		//新插入元素在父节点右侧,需要调整成3节点右倾结构
				{
					t = parent_ptr;
					right_rotate(t);		//原来父亲节点右旋,t右旋完重新变成子节点
				}
				t->parent->color = rb_black;	//父亲变黑
				t->parent->parent->color = rb_red;		//爷爷变红
				left_rotate(t->parent->parent);	//爷爷节点左旋
			}
		}
	}
	root->color = rb_black;		//保证根节点是黑色
}


/*删除后调整树的位置*/
template<typename T>
void rbtree<T>::fix_after_remove(node* p)
{
	while (p!=root && p->color == rb_black)		//黑色叶子节点删除进入此处
	{
		if (p == p->parent->left)	//p在左子树
		{
			node* brother_node = p->parent->right;	//兄弟节点,不一定是2-3-4树中真正和p在同一层的兄弟节点
			if (brother_node->color == rb_red)		//判断是否是真正的兄弟节点
			{
				brother_node->color = rb_black;			//变色
				p->parent->color = rb_red;
				left_rotate(p->parent);
				brother_node = p->parent->right;	//此时找到在2-3-4树中真正和p在同一层的兄弟节点

			}

			//找兄弟节点借,兄弟没得借
			if (get_color(brother_node->left)==rb_black && get_color(brother_node->right)==rb_black)	//注意空节点默认黑色
			{
				brother_node->color = rb_red;		//兄弟节点变红来平衡这一层的黑节点平衡
				p = p->parent;	//在while循环中向上递归
			}

			//找兄弟节点借,兄弟有得借
			//操作就是把父节点拉下来到删除位置,从兄弟节点中借一个节点喜当爹
			else
			{
				//分两种情况,兄弟节点是3-节点或者4-节点的情况

				if (brother_node->right->color == rb_black)	//3-节点情况,要借的节点在兄弟节点左边
				{
					brother_node->color = rb_red;
					brother_node->left->color = rb_black;
					right_rotate(brother_node);		//右旋,把要借的节点拉上来成为新的兄弟节点
					brother_node = p->parent->right;	//新的兄弟节点
				}
				brother_node->color = brother_node->parent->color;	//变为父亲节点颜色,主要考虑父节点为红色情况
				brother_node->parent->color = rb_black;		//父节点变黑
				brother_node->right->color = rb_black;
				left_rotate(p->parent);
				p = root;		//结束循环
			}

		}
		else		//p在右子树
		{
			node* brother_node = p->parent->left;	//兄弟节点,不一定是2-3-4树中真正和p在同一层的兄弟节点
			if (brother_node->color == rb_red)		//判断是否是真正的兄弟节点
			{
				brother_node->color = rb_black;			//变色
				p->parent->color = rb_red;
				right_rotate(p->parent);
				brother_node = p->parent->left;	//此时找到在2-3-4树中真正和p在同一层的兄弟节点

			}

			//找兄弟节点借,兄弟没得借
			if (get_color(brother_node->left) == rb_black && get_color(brother_node->right) == rb_black)	//注意空节点默认黑色
			{
				brother_node->color = rb_red;		//兄弟节点变红来平衡这一层的黑节点平衡
				p = p->parent;	//在while循环中向上递归
			}

			//找兄弟节点借,兄弟有得借
			//操作就是把父节点拉下来到删除位置,从兄弟节点中借一个节点喜当爹
			else
			{
				//分两种情况,兄弟节点是3-节点或者4-节点的情况

				if (brother_node->left->color == rb_black)	//3-节点情况,要借的节点在兄弟节点左边
				{
					brother_node->color = rb_red;
					brother_node->right->color = rb_black;
					left_rotate(brother_node);		//右旋,把要借的节点拉上来成为新的兄弟节点
					brother_node = p->parent->left;	//新的兄弟节点
				}
				brother_node->color = brother_node->parent->color;	//变为父亲节点颜色,主要考虑父节点为红色情况
				brother_node->parent->color = rb_black;		//父节点变黑
				brother_node->left->color = rb_black;
				right_rotate(p->parent);
				p = root;		//结束循环
			}
		}
	}
	p->color=rb_black;	//补偿删除的黑色节点
	
}



/*插入元数*/
template<typename T>
void rbtree<T>::insert(T x)
{
	//没有元素的话直接插入
	if (this->root == nullptr)
	{
		this->root = new node(x, rb_black);
		this->root->parent = nullptr;
		return;
	}
	bool left_or_right;		//记录插入在左还是右位置
	node* parent = root;
	node* t = root;
	while (t != nullptr)
	{
		parent = t;	//保存父节点位置
		if (x > t->val)
		{
			t = t->right;
			left_or_right = 0;
		}
			
		else if (x < t->val)
		{
			t = t->left;
			left_or_right = 1;	
		}	
		else
			return;  //相同元素返回
	}
	t = new node(x, rb_red);	//到达叶子节点,new节点
	if (left_or_right)		//左边插入
		parent->left = t;
	else					//右边插入
		parent->right = t;
	t->parent = parent;

	//插入之后调整树,旋转和变色
	fix_after_insert(t);
}

/*左旋*/
template<typename T>
void rbtree<T>::left_rotate(node* p)
{
	node* rbnode = p->right;
	rbnode->parent = p->parent;
	p->right = rbnode->left;
	if (rbnode->left != nullptr)
		rbnode->left->parent = p;
	if (p == root)
		root = rbnode;
	else if (p == p->parent->left)
		p->parent->left = rbnode;
	else if (p == p->parent->right)
		p->parent->right = rbnode;
	rbnode->left = p;
	p->parent = rbnode;
}


/*右旋*/
template<typename T>
void rbtree<T>::right_rotate(node* p)
{
	node* rbnode = p->left;
	rbnode->parent = p->parent;
	p->left = rbnode->right;
	if (rbnode->right != nullptr)
		rbnode->right->parent = p;
	if (p == root)
		root = rbnode;
	else if (p == p->parent->left)
		p->parent->left = rbnode;
	else
		p->parent->right = rbnode;
	rbnode->right = p;
	p->parent = rbnode;
}

测试函数(插入)

#include<iostream>
#include<queue>
#include"rbtree.h"
#include<string>
using namespace std;

string to_rb_color(bool color_type)
{
	if (color_type)
		return "<黑>";
	else
		return "<红>";
}

//  ************* 输出图形二叉树 *************
void output_impl(node* n, bool left, string const& indent)
{
	if (n->right)
	{
		output_impl(n->right, false, indent + (left ? "|     " : "      "));
	}
	cout << indent;
	cout << (left ? '\\' : '/');
	cout << "-----";
	cout << n->val << to_rb_color(n->color) << endl;
	if (n->left)
	{
		output_impl(n->left, true, indent + (left ? "      " : "|     "));
	}
}
void output(node* root)
{
	if (root == nullptr)
		return;
	if (root->right)
	{
		output_impl(root->right, false, "");
	}
	cout << root->val << to_rb_color(root->color)<< endl;
	if (root->left)
	{
		output_impl(root->left, true, "");
	}
	}
//  ***************************************

int main()
{
	rbtree<int> tree;
	int n = 20;
	int x = 1;
	while (n--)
	{
		tree.insert(x);
		x += 2;
	}
	output(tree.get_root());

	return 0;
}

测试结果(插入)

在这里插入图片描述

测试函数(删除)

int main()
{
	rbtree<int> tree;
	int n = 20;
	int x = 1;
	while (n--)
	{
		tree.insert(x);
		x += 2;
	}
	
	tree.remove(15);
	tree.remove(7);
	tree.remove(29);
	output(tree.get_root());
	return 0;
}

测试结果(删除)

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值