C++中的红黑树

这篇具有很好参考价值的文章主要介绍了C++中的红黑树。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

搜索二叉树

搜索二叉树的概念:二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树

简单来说,搜索二叉树的一个原则:如果左右树,根不为空,左树永远比根小,右树永远比根大
这个原则可运用到搜索二叉树的每个节点

用一张图来了解搜索二叉树:
C++中的红黑树,c++,数据结构,开发语言

二叉搜索树的优点:
查找效率:如果现在要在数组中查找一个数字是否存在,我们只能去遍历数组,但是如果在搜索二叉树中查找,从根节点开始判断,要么查找的数比根小,要么比根大,要么和根相等,如果要找的数在左边,那么右树可以全部排除,如果在右边,那么左边的数可以全部排除,以此往复下去,每次都可以将一半的数据排除,所以查找效率大大提高

二叉搜索树的缺点:
极端情况:还是在二叉搜索树中查找一个数字,但是这个时候出现了一个极端情况
上图:
C++中的红黑树,c++,数据结构,开发语言

这颗树符合搜索二叉树的原则吗?
答案是 当然,如果左右树不为空,左树永远比根小,右树永远比根大,很显然这颗树就是搜索二叉树
那么再来看一张图
C++中的红黑树,c++,数据结构,开发语言

这颗树是搜索二叉树吗?
我就不多说了

如果在以上两种情况下,树的节点向左或者向右呈现线型结构,这个时候再去查询一个数据,假设这个数据在最下面,比如上图中的 16 ,那搜索二叉树和数组有什么区别?所以如果碰到这种极端情况,搜素二叉树的优势就全没了

但是在一般的情况下,搜索二叉树的查找效率和远远优于数组的

搜索二叉树的模拟实现

直接上代码:
先来看一下搜索二叉树的整体框架

#pragma once
#include<iostream>

using namespace std;

//自定义命名空间
namespace ys
{
	//定义搜索二叉树的节点
	//使用模板来定义数据类型
	template<class T>
	struct TreeNode
	{
		T _val;//数据
		TreeNode* _left;//左树
		TreeNode* _right;//右树

		//构造函数初始化
		TreeNode(T data)
			:_val(data)
			,_left(nullptr)
			,_right(nullptr)
		{
			
		}

	};


	//定义搜索二叉树类
	//使用模板接受数据类型
	template<class T>
	class Tree
	{
		//对树的结构体进行重命名,方便后续操作
		typedef TreeNode node;
	public:

		//插入
		bool insert()
		{}

		//删除
		bool erase()
		{}

		//查询
		bool find()
		{}

		//打印整颗树
		void Printf()
		{}


	private:
		node* _root = nullptr;//根节点
	};
}

总共4个接口,我们来逐一攻破
先从查询(find)开始:
在搜索二叉树中查询一个数字是否存在,从根节点开始查找,如果等于根节点返回true,否则和根节点比较大小,比根小转到左树去查找,比根大转到右树去查找
代码:

//查询
		bool find(const T& data)
		{
			node* cur = _root;

			while (cur)
			{
				//找到了
				if (data == cur->_val) return true;

				if (data < cur->_val) cur = cur->_left;//比根小,转到左树
				else cur = cur->_right;//比根大,转到右树
			}

			return false;
		}

插入(insert):和查询的逻辑大差不差,首先还是比较,要插入的数字比根小,转到左树寻找要插入的位置,比根大,转到右树寻找要插入的位置

//插入
		bool insert(const T& data)
		{
			//如果根为空,说明是空树,直接插入根即可
			if (_root == nullptr)
			{
				_root = new node(data);
				return true;
			}
			else
			{
				//首先查询树中是否有data,如果有直接返回false
				if (find(data)) return false;
				
				//如果没有再进行插入
				node* cur = _root;
				node* prev = nullptr;
				while (cur)
				{
					//如果data小于根,在左树寻找插入位置
					if (data < cur->_val)
					{
						prev = cur;
						cur = cur->_left;
					}
					//如果data大于根,在右树寻找插入位置
					else
					{
						prev = cur;
						cur = cur->_right;
					}
				}

				//循环结束,说明已经找到了插入的位置
				//插入到prev下面的两颗子树

				//判断插入prev左边还是右边
				if (data > prev->_val)
					prev->_right = new node(data);
				else
					prev->_left = new node(data);

				return true;
			}
		}

最关键的来了,删除(erase)
删除的步骤:
1,找到要删除的节点(cur)的父树(prev)

2,判断要删除的节点是否有左右树
(1)如果只有左树,将cur的左树连接到prev
(2)如果只有右树,将cur的右树连接到prev

3,如果左右树都有
(1)将左树的最大节点和要删除的节点进行替换
(2)将右树的最小节点和要删除的节点进行替换

//删除
		bool erase(const T& data)
		{
			//树为空,返回false
			if (_root == nullptr) return false;

			node* cur = _root;
			node* prev = nullptr;//记录父节点

			//找到要删除的节点
			while (cur)
			{
				if (data < cur->_val)
				{
					prev = cur;
					cur = cur->_left;
				}
				else if(data > cur->_val)
				{
					prev = cur;
					cur = cur->_right;
				}
				//找到了要删除的节点
				//判断要删除的节点是否拥有左右树
				else
				{
					//如果cur的左树为空,直接将cur的右树和cur的父树连接
					if (cur->_left == nullptr)
					{
						//判断cur是否为根节点
						if (cur == _root)
						{
							//如果cur就是根节点,并且左树为空,直接将cur的第一个右树设为root
							_root = cur->_right;
						}
						else
						{
							if (prev->_left == cur) prev->_left = cur->_right;
							else prev->_right = cur->_right;
						}
					}
					//如果cur的右树为空,直接将cur的左树和cur的父树连接
					else if (cur->_right == nullptr)
					{
						//判断cur是否为根节点
						if (cur == _root)
						{
							//如果cur就是根节点,并且左树为空,直接将cur的第一个右树设为root
							_root = cur->_right;
						}
						else
						{
							if (prev->_right == cur) prev->_right = cur->_left;
							else prev->_left = cur->_left;
						}
					}
					//如果要删除的节点同时拥有左右树,有两种删除方法
					else
					{
						//1,使用左树的最大节点进行替换
						//2,使用右树的最小节点进行替换

						//这里我们采用第一种方法,使用左树的最大节点进行替换

						node* leftmax = cur->_left;
						node* pleftmax = nullptr;
						//找到左树的最大节点
						while (leftmax)
						{
							pleftmax = leftmax;
							leftmax = leftmax->_right;
						}

						//如果左树的最大节点有左树
						//将最大节点的左树连接到他的父树
						if (leftmax->_left)
						{
							pleftmax->_right = leftmax;
						}

						//将要删除节点的数据和左树最大节点继续交换
						cur->_val = leftmax->_val;

						//释放左树最大节点
						delete leftmax;
						leftmax = nullptr;
					}

					return true;
				}

			}

			return false;

		}

中序遍历:
这个就不多说了,直接上代码

void _printf(node* root)
		{
			if (root == nullptr) return;

			_printf(root->_left);
			cout << root->_val << " ";
			_printf(root->_right);

		}
		//中序遍历打印整颗树
		void Printf()
		{
			if (_root == nullptr) cout << "空树" << endl;
			_printf(_root);
		}

整体代码:

#pragma once
#include<iostream>

using namespace std;

//自定义命名空间
namespace ys
{
	//定义搜索二叉树的节点

	//使用模板来定义数据类型
	template<class T>
	struct TreeNode
	{
		T _val;//数据
		TreeNode* _left;//左树
		TreeNode* _right;//右树

		//构造函数初始化
		TreeNode(T data)
			:_val(data)
			,_left(nullptr)
			,_right(nullptr)
		{
			
		}

	};


	//定义搜索二叉树类

	//使用模板接受数据类型
	template<class T>
	class Tree
	{
		typedef TreeNode<T> node;
	public:

		//插入
		bool insert(const T& data)
		{
			//如果根为空,说明是空树,直接插入根即可
			if (_root == nullptr)
			{
				_root = new node(data);
				return true;
			}
			else
			{
				//首先查询树中是否有data,如果有直接返回false
				if (find(data)) return false;
				
				//如果没有再进行插入
				node* cur = _root;
				node* prev = nullptr;
				while (cur)
				{
					//如果data小于根,在左树寻找插入位置
					if (data < cur->_val)
					{
						prev = cur;
						cur = cur->_left;
					}
					//如果data大于根,在右树寻找插入位置
					else
					{
						prev = cur;
						cur = cur->_right;
					}
				}

				//循环结束,说明已经找到了插入的位置
				//插入到prev下面的两颗子树

				//判断插入prev左边还是右边
				if (data > prev->_val)
					prev->_right = new node(data);
				else
					prev->_left = new node(data);

				return true;
			}
		}

		//删除
		bool erase(const T& data)
		{
			//树为空,返回false
			if (_root == nullptr) return false;

			node* cur = _root;
			node* prev = nullptr;//记录父节点

			//找到要删除的节点
			while (cur)
			{
				if (data < cur->_val)
				{
					prev = cur;
					cur = cur->_left;
				}
				else if(data > cur->_val)
				{
					prev = cur;
					cur = cur->_right;
				}
				//找到了要删除的节点
				//判断要删除的节点是否拥有左右树
				else
				{
					//如果cur的左树为空,直接将cur的右树和cur的父树连接
					if (cur->_left == nullptr)
					{
						//判断cur是否为根节点
						if (cur == _root)
						{
							//如果cur就是根节点,并且左树为空,直接将cur的第一个右树设为root
							_root = cur->_right;
						}
						else
						{
							if (prev->_left == cur) prev->_left = cur->_right;
							else prev->_right = cur->_right;
						}
					}
					//如果cur的右树为空,直接将cur的左树和cur的父树连接
					else if (cur->_right == nullptr)
					{
						//判断cur是否为根节点
						if (cur == _root)
						{
							//如果cur就是根节点,并且左树为空,直接将cur的第一个右树设为root
							_root = cur->_right;
						}
						else
						{
							if (prev->_right == cur) prev->_right = cur->_left;
							else prev->_left = cur->_left;
						}
					}
					//如果要删除的节点同时拥有左右树,有两种删除方法
					else
					{
						//1,使用左树的最大节点进行替换
						//2,使用右树的最小节点进行替换

						//这里我们采用第一种方法,使用左树的最大节点进行替换

						node* leftmax = cur->_left;
						node* pleftmax = nullptr;
						//找到左树的最大节点
						while (leftmax)
						{
							pleftmax = leftmax;
							leftmax = leftmax->_right;
						}

						//如果左树的最大节点有左树
						//将最大节点的左树连接到他的父树
						if (leftmax->_left)
						{
							pleftmax->_right = leftmax;
						}

						//将要删除节点的数据和左树最大节点继续交换
						cur->_val = leftmax->_val;

						//释放左树最大节点
						delete leftmax;
						leftmax = nullptr;
					}

					return true;
				}

			}

			return false;

		}

		//查询
		bool find(const T& data)
		{
			node* cur = _root;

			while (cur)
			{
				//找到了
				if (data == cur->_val) return true;

				if (data < cur->_val) cur = cur->_left;//比根小,转到左树
				else cur = cur->_right;//比根大,转到右树
			}

			return false;
		}

		void _printf(node* root)
		{
			if (root == nullptr) return;

			_printf(root->_left);
			cout << root->_val << " ";
			_printf(root->_right);

		}
		//中序遍历打印整颗树
		void Printf()
		{
			if (_root == nullptr) cout << "空树" << endl;
			_printf(_root);
		}


	private:
		node* _root = nullptr;//根节点
	};
}

测试用例:
插入:
C++中的红黑树,c++,数据结构,开发语言

查询:
C++中的红黑树,c++,数据结构,开发语言

删除:
C++中的红黑树,c++,数据结构,开发语言

平衡搜索二叉树(AVL Tree)

搜索二叉树有两个极端情况
1,当所有的节点都只有左树,那么整颗树就会呈现出向左的一条线性结构
C++中的红黑树,c++,数据结构,开发语言

2,当所有的节点都只有右树,那么整颗树就会呈现出向右的一条线性结构
C++中的红黑树,c++,数据结构,开发语言
AVL 是大学教授 G.M. Adelson-Velsky 和 E.M. Landis 名称的缩写,他们两个提出的平衡二叉树的概念,为了纪念他们,将 平衡二叉树 称为 AVL树。
当搜索二叉树出现这两种情况的时候,搜索二叉树的优势就全没有了,所以为了避免这种情况出现,伟大的早期程序员设计出了平衡搜索二叉树(AVL树)

AVL树的概念:
AVL树本质是就是一棵搜索二叉树,【但是】,为了不让树呈现出一边倒的现象,AVL树的设计者又给加了两个原则:
1,它是一棵空树或它的左右两个子树的高度之差(简称平衡因子)不超过1,
2,左右两个子树 也都是一棵平衡二叉树。

平衡因子:
一个没有左树和右树的节点平衡因子为0
如果插入一个左树,平衡因子减1
如果插入一个右树,平衡因子加1
不论是平衡因子减1或者加1,当前节点的父树的平衡因子也要跟随变动,依次类推
【注意】当平衡因子>= 2或者<= -2的时候,说明这颗树已经不平衡了,所以此时不要再向上调整父树平衡因子,而是在不平衡的节点做出处理

AVL树的旋转
1,左单旋
当一棵AVL树的右树高于左树的时候,将右树向左边旋转
C++中的红黑树,c++,数据结构,开发语言
旋转方式:1,将25连接到20的右树
C++中的红黑树,c++,数据结构,开发语言

2,将20练级到65的左树
C++中的红黑树,c++,数据结构,开发语言
旋转完成,树已经达成平衡状态

2,右单旋
当一个AVL树的左树高于右树,将左树向右旋转
C++中的红黑树,c++,数据结构,开发语言

1,将60连接到70的左树
C++中的红黑树,c++,数据结构,开发语言
2,将70连接到50的右树
C++中的红黑树,c++,数据结构,开发语言
旋转完成,树已经达成平衡状态

3,左右旋
新节点插入较高左子树的右侧—左右:先左单旋再右单旋
C++中的红黑树,c++,数据结构,开发语言
1,将2进行左旋
C++中的红黑树,c++,数据结构,开发语言
2,将5进行右旋
C++中的红黑树,c++,数据结构,开发语言
旋转完成,树已经达成平衡状态

4,右左旋
新节点插入较高右子树的左侧—右左:先右单旋再左单旋
C++中的红黑树,c++,数据结构,开发语言

先将5进行右旋
C++中的红黑树,c++,数据结构,开发语言
再将2进行左旋
C++中的红黑树,c++,数据结构,开发语言

平衡搜索二叉树的模拟实现

直接上代码:

#include<iostream>
#include<assert.h>
using namespace std;

namespace avlt
{
	template<class K,class V>
	struct AvlNode
	{
		pair<K,V> _kv;//值
		AvlNode* _left;//左树
		AvlNode* _right;//右树
		AvlNode* _parent;//父树
		int _bf;//平衡因子

		AvlNode(const pair<K, V>& data )
			:_kv(data)
			,_left(nullptr)
			,_right(nullptr)
			,_parent(nullptr)
			,_bf(0)
		{
		}

	};


	template<class K,class V>
	class AvlTree
	{
		typedef AvlNode<K,V> node;
	public:

		//插入
		bool insert(const pair<K, V>& data)
		{
			//判断根节点是否为空
			if (_root == nullptr)
			{
				//如果根节点为空,直接插入根节点
				_root = new node(data);
				return true;
			}

			//查询树中是否已经存在要插入的数据
			if (find(data.first))
			{
				cout << data.first << "已存在" << endl;
				return false;
			}

			//首先找到要插入的节点
			node* cur = _root;
			node* parent = nullptr;

			while (cur)
			{
				if (cur->_kv.first > data.first)
				{
					parent = cur;
					cur = cur->_left;
				}
				else 
				{
					parent = cur;
					cur = cur->_right;
				}
				
			}

			//插入并连接
			cur = new node(data);
			if (parent->_kv.first > cur->_kv.first)
			{
				parent->_left = cur;
				cur->_parent = parent;
			}
			else
			{
				parent->_right = cur;
				cur->_parent = parent;
			}

			//向上更新平衡因子,直到检查到根节点
			while (parent)
			{
				//更新平衡因子
				if (parent->_left == cur)
					parent->_bf--;
				else
					parent->_bf++;

				//以parent为根节点的树是平衡的,但不是完全平衡,继续向上更新
				if (parent->_bf == 1 || parent->_bf == -1)
				{
					//cur = cur->_parent;
					cur = parent;
					parent = parent->_parent;
				}
				//平衡因子为0,说明树是平衡的,不要再做调整,直接跳出循环
				else if (parent->_bf == 0)
				{
					break;
				}
				//平衡因子不平衡
				else if(parent->_bf == 2 || parent->_bf == -2)
				{
					//右树高,左单旋
					if (parent->_bf == 2 && cur->_bf == 1)
					{
						RotateL(parent);
					}
					//左树高,右单旋
					else if (parent->_bf == -2 && cur->_bf == -1)
					{
						RotateR(parent);
					}
					//左树的右树高,先左旋再右旋
					else if (parent->_bf == -2 && cur->_bf == 1)
					{
						RotateLR(parent);
					}
					//右树的左树高,先右旋再左旋
					else if (parent->_bf == 2 && cur->_bf == -1)
					{
						RotateRL(parent);
					}
					else
						assert(false);

					break;
				}
				else
					assert(false);
			}

			return true;

		}

private:
		//旋转
		//左旋
		void RotateL(node* parent)
		{
			node* subR = parent->_right;
			node* subRL = subR->_left;

			parent->_right = subRL;
			if (subRL)
				subRL->_parent = parent;

			node* ppnode = parent->_parent;

			subR->_left = parent;
			parent->_parent = subR;

			if (ppnode == nullptr)
			{
				_root = subR;
				_root->_parent = nullptr;
			}
			else
			{
				if (ppnode->_left == parent)
				{
					ppnode->_left = subR;
				}
				else
				{
					ppnode->_right = subR;
				}

				subR->_parent = ppnode;
			}

			parent->_bf = subR->_bf = 0;
		}

		//右旋
		void RotateR(node* parent)
		{
			node* subL = parent->_left;
			node* subLR = subL->_right;

			parent->_left = subLR;
			if (subLR)
				subLR->_parent = parent;

			node* ppnode = parent->_parent;

			subL->_right = parent;
			parent->_parent = subL;

			if (parent == _root)
			{
				_root = subL;
				_root->_parent = nullptr;
			}
			else
			{
				if (ppnode->_left == parent)
				{
					ppnode->_left = subL;
				}
				else
				{
					ppnode->_right = subL;
				}
				subL->_parent = ppnode;
			}

			subL->_bf = parent->_bf = 0;
		}

		//左右旋
		void RotateLR(node* parent)
		{
			node* subL = parent->_left;
			node* subLR = subL->_right;
			int bf = subLR->_bf;

			RotateL(parent->_left);
			RotateR(parent);

			if (bf == 1)
			{
				parent->_bf = 0;
				subLR->_bf = 0;
				subL->_bf = -1;
			}
			else if (bf == -1)
			{
				parent->_bf = 1;
				subLR->_bf = 0;
				subL->_bf = 0;
			}
			else if (bf == 0)
			{
				parent->_bf = 0;
				subLR->_bf = 0;
				subL->_bf = 0;
			}
			else
			{
				cout << "左右旋" << endl;
				assert(false);
			}
		}

		//右左旋
		void RotateRL(node* parent)
		{
			node* subR = parent->_right;
			node* subRL = subR->_left;
			int bf = subRL->_bf;

			RotateR(parent->_right);
			RotateL(parent);

			if (bf == 1)
			{
				subR->_bf = 0;
				parent->_bf = -1;
				subRL->_bf = 0;
			}
			else if (bf == -1)
			{
				subR->_bf = 1;
				parent->_bf = 0;
				subRL->_bf = 0;
			}
			else if (bf == 0)
			{
				subR->_bf = 0;
				parent->_bf = 0;
				subRL->_bf = 0;
			}
			else
			{
				cout << "右左旋" << endl;
				assert(false);
			}
		}


		//查询
		bool find(const K& data)
		{
			if (_root == nullptr) return false;

			node* cur = _root;
			while (cur)
			{
				if (cur->_kv.first == data)
					return true;
				if (cur->_kv.first > data)
					cur = cur->_left;
				else
					cur = cur->_right;
			}

			return false;

		}
public:
		//中序遍历
		void _print(node* root)
		{
			if (root == nullptr) return;

			_print(root->_left);
			cout << root->_kv.first << ":" << root->_kv.second << endl;
			_print(root->_right);

		}

		void print()
		{
			_print(_root);
		}


	private:
		node* _root = nullptr;//根结点
	};
}

来看一下效果;
C++中的红黑树,c++,数据结构,开发语言
C++中的红黑树,c++,数据结构,开发语言
我们多试几次:
C++中的红黑树,c++,数据结构,开发语言
C++中的红黑树,c++,数据结构,开发语言

红黑树(Red Black Tree)

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

红黑树的性质

  1. 每个结点不是红色就是黑色
  2. 根节点是黑色的
  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
    C++中的红黑树,c++,数据结构,开发语言
    红黑树本身就是一棵AVL树,但是他比AVL树具有更高的效率

当红黑树不平衡的时候,他不能像AVL树那样只进行旋转,而是旋转加变色

假设现在有一个红黑树
cur为新插入的节点
C++中的红黑树,c++,数据结构,开发语言
此时新插入的cur违反了红黑树【如果一个节点是红色的,则它的两个孩子结点是黑色的 】的规则
那么怎么解决这个问题呢?

第一种情况,叔叔节点存在且为红色(只变色)

第一步,判断父树是否为黑色节点,如果是黑色节点,那就不用做处理,因为没有违反红黑树的规则

第二步,如果父树是红色节点,将父树变成黑色节点
C++中的红黑树,c++,数据结构,开发语言
第三步,如果叔叔节点存在,将叔叔节点(父树的兄弟节点)也变成黑色
C++中的红黑树,c++,数据结构,开发语言
第四步,将爷爷节点变成红色
C++中的红黑树,c++,数据结构,开发语言
第五步,将爷爷节点当做一个新插入的节点,继续向上更新变色
C++中的红黑树,c++,数据结构,开发语言

然后重复上面的4个步骤:
C++中的红黑树,c++,数据结构,开发语言
如果最后发现走到了根节点,必须将根节点变成黑色
C++中的红黑树,c++,数据结构,开发语言

第二种情况:旋转加变色
当插入的节点没有叔叔节点的时候
C++中的红黑树,c++,数据结构,开发语言
首先将爷爷节点进行右单旋
C++中的红黑树,c++,数据结构,开发语言
再将父节点变成黑色,爷爷节点变成红色
C++中的红黑树,c++,数据结构,开发语言

第三种情况:叔叔存在且为黑
C++中的红黑树,c++,数据结构,开发语言
首先cur是新增节点,但是一般情况下,叔叔节点颜色和父节点颜色是相同的,但是,当上面这种情况出现后,向上调整会变成:
C++中的红黑树,c++,数据结构,开发语言
由于向上调整变色,4被当做新增节点,此时4的叔叔节点是黑色,父节点是红色,这个时候就要采用双旋的方案来解决这个问题
1,将2左旋
C++中的红黑树,c++,数据结构,开发语言
2,对7进行右旋
C++中的红黑树,c++,数据结构,开发语言

最后进行变色
C++中的红黑树,c++,数据结构,开发语言

当然红黑树增加节点旋转变色的情况很多,但是上述几种方案基本概述了所有情况的原理,其他情况与之不同的就是旋转的方向不一样,原理都是一样的

红黑树的模拟实现

话不多说,直接上代码:

#pragma once
#include<iostream>

using namespace std;


namespace rb_tree
{
	//枚举定义节点颜色
	enum Colour
	{
		RED,
		BLACK
	};

	//红黑树节点
	template<class K,class V>
	struct RBTreeNode
	{
		pair<K, V> _kv;//数据
		RBTreeNode* _left;//左树
		RBTreeNode* _right;//右树
		RBTreeNode* _parent;//父树
		Colour _col;//节点颜色

		RBTreeNode(const pair<K, V>& data)
			:_kv(data)
			,_left(nullptr)
			,_right(nullptr)
			,_parent(nullptr)
			,_col(RED)//节点颜色初始化为红色
		{}
	};

	template<class K,class V>
	class RBTree
	{
		typedef RBTreeNode<K,V> node;
	public:

		//插入
		bool insert(const pair<K,V>& data)
		{
			//如果根节点为空,插入根节点,并将颜色改为黑色
			if (_root == nullptr)
			{
				_root = new node(data);
				_root->_col = BLACK;
				return true;
			}

			//找到可以插入的地方
			node* cur = _root;
			node* parent = nullptr;

			while (cur)
			{
				if (cur->_kv.first > data.first)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if(cur->_kv.first < data.first)
				{
					parent = cur;
					cur = cur->_right;
				}
				else 
					return false;

			}

			//插入并连接
			cur = new node(data);
			if (parent->_kv.first > cur->_kv.first)
				parent->_left = cur;
			else
				parent->_right = cur;
			cur->_parent = parent;

			//如果父节点是黑色,插入的节点是红色,直接返回true,不需要做处理
			if (parent->_col == BLACK) return true;

			//如果父节点是红色,开始处理
			while (parent && parent->_col == RED)
			{
				//找到祖父节点
				node* grandfather = parent->_parent;
				//找到叔叔节点
				if (grandfather->_left == parent)
				{
					node* uncle = grandfather->_right;
					//如果叔叔节点不为空且是红色
					if (uncle && uncle->_col == RED)
					{
						//将叔叔和父节点变黑
						uncle->_col = parent->_col = BLACK;
						//将祖父节点变红
						grandfather->_col = RED;

						//继续向上调整
						cur = grandfather;
						parent = cur->_parent;
					}
					//叔叔节点存在且为黑,或者叔叔节点不存在
					else
					{
						//第一种情况
						//     g
						//   p   u
						// c
						if (cur == parent->_left)
						{
							//对p继续右旋
							RotateR(parent);
							//再进行变色
							parent->_col = BLACK;
							grandfather->_col = RED;
						}
						//第二种情况
						//    g
						//  p   u
						//    c
						else
						{
							//先左旋parent
							RotateL(parent);
							//再右旋grandfather
							RotateR(grandfather);
							//变色
							grandfather->_col = RED;
							cur->_col = BLACK;
						}

						break;
					}

				}
				//如果parent是祖父节点的右边,叔叔节点就是祖父节点的左边
				else
				{
					//找到叔叔节点
					node* uncle = grandfather->_left;

					//如果叔叔节点存在且为红
					if (uncle && uncle->_col == RED)
					{
						uncle->_col = BLACK;
						parent->_col = BLACK;
						grandfather->_col = RED;

						cur = grandfather;
						parent = cur->_parent;
					}
					//如果叔叔节点不存在或存在且为黑
					else
					{
						//第一种情况
						//      g
						//    u   p
						//          c
						if (cur == parent->_right)
						{
							//首先对grandfather进行左单旋
							RotateL(grandfather);
							//变色
							parent->_col = BLACK;
							grandfather->_col = RED;
						}
						//第二种情况
						//      g
						//   u     p
						//       c
						else
						{
							//首先对parent进行右单旋
							RotateR(parent);
							//再对grandfather进行左单旋
							RotateL(grandfather);
							//变色
							cur->_col = BLACK;
							grandfather->_col = RED;
						}

					}

					break;

				}

			}

			//不论什么情况下,根节点都必须是黑色的
			_root->_col = BLACK;
			return true;

		}

		//中序遍历
		void _print(node* root)
		{
			if (root == nullptr) return;

			_print(root->_left);
			cout << root->_kv.first << ":" << root->_kv.second << endl;
			_print(root->_right);
		}

		void print()
		{
			_print(_root);
		}

private:

		//左单旋
		void RotateL(node* parent)
		{
			node* SubR = parent->_right;
			node* SubRL = SubR->_left;

			parent->_right = SubRL;

			if (SubRL)
				SubRL->_parent = parent;

			node* pparent = parent->_parent;
			parent->_parent = SubR;
			SubR->_left = parent;


			if (pparent)
			{
				if (pparent->_left == parent)
					pparent->_left = SubR;
				else
					pparent->_right = SubR;

				SubR->_parent = pparent;
			}
			else
			{
				_root = SubR;
				SubR->_parent = nullptr;
			}

		}

		//右单旋
		void RotateR(node* parent)
		{
			node* SubL = parent->_left;
			node* SubLR = SubL->_right;
			node* pparent = parent->_parent;

			parent->_left = SubLR;
			SubL->_right = parent;
			parent->_parent = SubL;

			if (SubLR)
				SubLR->_parent = parent;

			if (pparent)
			{
				if (pparent->_left == parent)
					pparent->_left = SubL;
				else
					pparent->_right = SubL;
				SubL->_parent = pparent;
			}
			else
			{
				_root = SubL;
				SubL->_parent = nullptr;
			}

		}

	private:
		node* _root = nullptr;//根节点
	};
}

测试一下:

#include"RBTree.h"
#include<set>
#include<map>
#include<utility>
#include<ctime>
using namespace std;

void test1()
{
	srand(time(nullptr));

	rb_tree::RBTree<int, int> rb;
	for (int i = 0; i < 30; i++)
	{
		rb.insert(make_pair(rand() % 100, rand() % 10));
	}

	rb.print();
}



int main()
{
	test1();
	return 0;
}

C++中的红黑树,c++,数据结构,开发语言
再试几次
C++中的红黑树,c++,数据结构,开发语言
C++中的红黑树,c++,数据结构,开发语言
没毛病…

红黑树的应用(Map 和 Set)

Map和Set的基本使用

Map和Set的封装

看完Map和Set的基本使用,基于上面的红黑树代码我们来手写一个简单的Map和Set
主要功能有三个,插入,查询,迭代器

重点说一下迭代器和红黑树的模板参数设计:

STL的map和set是共用红黑树的代码的,也就是说,一张红黑树的代码,map可以直接封装,set也可以
C++中的红黑树,c++,数据结构,开发语言

我们来看上面我们写的红黑树代码:
C++中的红黑树,c++,数据结构,开发语言

C++中的红黑树,c++,数据结构,开发语言

我们设计的红黑树是key Value结构的,如果是用在map上,是合适的
但是set呢?set的key就是value,但往往set只有一个参数,而要使用红黑树至少得两个参数,那就不能使用这个红黑树了
C++中的红黑树,c++,数据结构,开发语言

怎么办?STL的设计者想出了一个非常棒的点子,修改红黑树的模板参数
C++中的红黑树,c++,数据结构,开发语言

C++中的红黑树,c++,数据结构,开发语言

set
C++中的红黑树,c++,数据结构,开发语言

map
C++中的红黑树,c++,数据结构,开发语言

我们来画图演示一下:
C++中的红黑树,c++,数据结构,开发语言
首先将红黑树的参数改成三个,第一个参数不重要,重要的是第二个参数,set和map指明参数类型的时候,以第二个参数为准,这样红黑树既可以让set使用,也可以让map使用
但是问题又来了,在插入的时候需要比较大小,map传入的是一个pair对象,不能直接比较,所以第三个参数的作用就体现出来了,这是一个仿函数类,在比较的时候,创建一个仿函数对象,用仿函数去比较,如果是set,比较的是Key,如果是map,就返回Key去比较

不得不说STL这个设计,非常棒!!!!!

具体封装,直接上代码:
红黑树代码:

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;


namespace rb_tree
{
	//枚举定义节点颜色
	enum Colour
	{
		RED,
		BLACK
	};

	//红黑树节点
	template<class T>
	struct RBTreeNode
	{
		T _data;//数据
		RBTreeNode* _left;//左树
		RBTreeNode* _right;//右树
		RBTreeNode* _parent;//父树
		Colour _col;//节点颜色

		RBTreeNode(const T& data)
			:_data(data)
			, _left(nullptr)
			, _right(nullptr)
			, _parent(nullptr)
			, _col(RED)//节点颜色初始化为红色
		{}
	};

	//迭代器
	//                     Ref = T&  Ptr = T*
	template<class T,class Ref,class Ptr>
	struct RBTreeIterator
	{
		typedef RBTreeIterator<T, Ref, Ptr> Self;
		typedef RBTreeNode<T> node;
		node* nod;

		RBTreeIterator(node* root)
			:nod(root)
		{}

		Ref operator *()
		{
			return nod->_data;
		}

		Ptr operator ->()
		{
			return &(nod->_data);
		}

		bool operator !=(const Self& data)
		{
			return nod != data.nod;
		}

		Self operator ++()
		{
			//如果nod的右树不为空,右树的最左节点就是下一个位置
			if (nod->_right)
			{
				node* subleft = nod->_right;
				while (subleft->_left)
				{
					subleft = subleft->_left;
				}

				nod = subleft;
			}
			//如果右树为空,沿着到根的路径找,子树为父树的左子树就是下一个位置
			else
			{
				node* cur = nod;
				node* parent = cur->_parent;
				while (parent && cur == parent->_right)
				{
					cur = parent;
					parent = parent->_parent;
				}

				nod = parent;
			}

			return *this;

		}

		Self operator --()
		{
			//如果左树存在,左树的右节点就是上一个,否则左树就是上一个

			if (nod->_left)
			{
				node* subright = nod->_left;
				while (subright->_right)
				{
					subright = subright->_right;
				}

				nod = subright;
			}
			else
			{
				//向上找,如果当前节点是父树的右节点,则父树就是上一个节点
				node* parent = nod->_parent;
				node* cur = nod;
				while (parent && parent->_left == nod)
				{
					cur = parent;
					parent = cur->_parent;
				}

				nod = parent;

			}

			return *this;
		}

	};

	template<class T, class Ref, class Ptr>
	struct Reverse_Iterator
	{
		typedef Reverse_Iterator<T,Ref,Ptr> Self;
		typedef RBTreeNode<T> node;
		RBTreeIterator<T,Ref,Ptr> _it;
		Reverse_Iterator(node* root)
			:_it(root)
		{}

		//使用正向迭代器来构造反向迭代器

		Ref operator *()
		{
			return _it.nod->_data;
		}

		Ptr operator ->()
		{
			return &_it->nod;
		}

		bool operator !=(const Self& data)
		{
			return _it.nod != data._it.nod;
		}

		Self operator ++()
		{
			--_it;
			return *this;
		}

		Self operator --()
		{
			++ _it;
			return *this;
		}
		

	};

	template<class K, class T,class KeyOfT>
	class RBTree
	{
	public:
		typedef RBTreeNode<T> node;
		typedef RBTreeIterator<T, T&, T*> iterator;//迭代器
		typedef Reverse_Iterator<T, T&, T*>  reverse_iterator;//反向迭代器

	public:

		//迭代器
		iterator begin()
		{
			assert(_root);
			//返回树的最左节点
			node* cur = _root;
			while (cur && cur->_left)
			{
				cur = cur->_left;
			}

			return iterator(cur);
		}

		iterator end()
		{
			return iterator(nullptr);
		}

		reverse_iterator rbegin()
		{
			return reverse_iterator(nullptr);
		}

		reverse_iterator rend()
		{
			assert(_root);
			//返回树的最左节点
			node* cur = _root;
			while (cur && cur->_left)
			{
				cur = cur->_left;
			}

			return reverse_iterator(cur);
		}

		//插入
		bool insert(const T& data)
		{
			//如果根节点为空,插入根节点,并将颜色改为黑色
			if (_root == nullptr)
			{
				_root = new node(data);
				_root->_col = BLACK;
				return true;
			}

			//找到可以插入的地方
			node* cur = _root;
			node* parent = nullptr;

			KeyOfT kot;

			while (cur)
			{
				if (kot(cur->_data) > kot(data))
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (kot(cur->_data) < kot(data))
				{
					parent = cur;
					cur = cur->_right;
				}
				else
					return false;

			}

			//插入并连接
			cur = new node(data);
			if (kot(parent->_data) > kot(cur->_data))
				parent->_left = cur;
			else
				parent->_right = cur;
			cur->_parent = parent;

			//如果父节点是黑色,插入的节点是红色,直接返回true,不需要做处理
			if (parent->_col == BLACK) return true;

			//如果父节点是红色,开始处理
			while (parent && parent->_col == RED)
			{
				//找到祖父节点
				node* grandfather = parent->_parent;
				//找到叔叔节点
				if (grandfather->_left == parent)
				{
					node* uncle = grandfather->_right;
					//如果叔叔节点不为空且是红色
					if (uncle && uncle->_col == RED)
					{
						//将叔叔和父节点变黑
						uncle->_col = parent->_col = BLACK;
						//将祖父节点变红
						grandfather->_col = RED;

						//继续向上调整
						cur = grandfather;
						parent = cur->_parent;
					}
					//叔叔节点存在且为黑,或者叔叔节点不存在
					else
					{
						//第一种情况
						//     g
						//   p   u
						// c
						if (cur == parent->_left)
						{
							//对p继续右旋
							RotateR(parent);
							//再进行变色
							parent->_col = BLACK;
							grandfather->_col = RED;
						}
						//第二种情况
						//    g
						//  p   u
						//    c
						else
						{
							//先左旋parent
							RotateL(parent);
							//再右旋grandfather
							RotateR(grandfather);
							//变色
							grandfather->_col = RED;
							cur->_col = BLACK;
						}

						break;
					}

				}
				//如果parent是祖父节点的右边,叔叔节点就是祖父节点的左边
				else
				{
					//找到叔叔节点
					node* uncle = grandfather->_left;

					//如果叔叔节点存在且为红
					if (uncle && uncle->_col == RED)
					{
						uncle->_col = BLACK;
						parent->_col = BLACK;
						grandfather->_col = RED;

						cur = grandfather;
						parent = cur->_parent;
					}
					//如果叔叔节点不存在或存在且为黑
					else
					{
						//第一种情况
						//      g
						//    u   p
						//          c
						if (cur == parent->_right)
						{
							//首先对grandfather进行左单旋
							RotateL(grandfather);
							//变色
							parent->_col = BLACK;
							grandfather->_col = RED;
						}
						//第二种情况
						//      g
						//   u     p
						//       c
						else
						{
							//首先对parent进行右单旋
							RotateR(parent);
							//再对grandfather进行左单旋
							RotateL(grandfather);
							//变色
							cur->_col = BLACK;
							grandfather->_col = RED;
						}

					}

					break;

				}

			}

			//不论什么情况下,根节点都必须是黑色的
			_root->_col = BLACK;
			return true;

		}


		iterator find(const K& data)
		{
			node* cur = _root;
			KeyOfT kot;
			while (cur)
			{
				if (kot(cur->_data) == data) return iterator(cur);
				else if (kot(cur->_data) > data) cur = cur->_left;
				else cur = cur->_right;
			}

			return iterator(nullptr);

		}

	private:

		//左单旋
		void RotateL(node* parent)
		{
			node* SubR = parent->_right;
			node* SubRL = SubR->_left;

			parent->_right = SubRL;

			if (SubRL)
				SubRL->_parent = parent;

			node* pparent = parent->_parent;
			parent->_parent = SubR;
			SubR->_left = parent;


			if (pparent)
			{
				if (pparent->_left == parent)
					pparent->_left = SubR;
				else
					pparent->_right = SubR;

				SubR->_parent = pparent;
			}
			else
			{
				_root = SubR;
				SubR->_parent = nullptr;
			}

		}

		//右单旋
		void RotateR(node* parent)
		{
			node* SubL = parent->_left;
			node* SubLR = SubL->_right;
			node* pparent = parent->_parent;

			parent->_left = SubLR;
			SubL->_right = parent;
			parent->_parent = SubL;

			if (SubLR)
				SubLR->_parent = parent;

			if (pparent)
			{
				if (pparent->_left == parent)
					pparent->_left = SubL;
				else
					pparent->_right = SubL;
				SubL->_parent = pparent;
			}
			else
			{
				_root = SubL;
				SubL->_parent = nullptr;
			}

		}

	private:
		node* _root = nullptr;//根节点
	};
}

set封装代码:

#pragma once
#include"RBTree.h"

namespace myset
{
	template<class K>
	class set
	{
	public:
		typedef rb_tree::RBTreeIterator<K, K&, K*> iterator;
		typedef rb_tree::Reverse_Iterator<K, K&, K*> reverse_iterator;

		struct SetKeyOfT
		{
			K operator()(const K& key)
			{
				return key;
			}
		};

		//迭代器
		iterator begin()
		{
			return _rb.begin();
		}

		iterator end()
		{
			return _rb.end();
		}

		reverse_iterator rbegin()
		{
			return _rb.rbegin();
		}

		reverse_iterator rend()
		{
			return _rb.rend();
		}
		

		bool insert(const K& data)
		{
			return _rb.insert(data);
		}

		iterator find(K& data)
		{
			return _rb.find(data);
		}
		

	private:
		rb_tree::RBTree<K,K,SetKeyOfT> _rb;
	};
}


template<class K,class V>
class map
{
	struct MapKeyOfT
	{
		K operator()(const pair<K, V>& data)
		{
			return data.first;
		}
	};
private:
	rb_tree::RBTree<K, pair<K, V>, MapKeyOfT> _rb;
};

map封装代码:文章来源地址https://www.toymoban.com/news/detail-707082.html

#pragma once
#include"RBTree.h"

namespace mymap
{
	template<class K, class V>
	class map
	{
		struct MapKeyOfT
		{
			K operator()(const pair<K, V>& data)
			{
				return data.first;
			}
		};

	public:
		typedef rb_tree::RBTreeIterator<pair<K, V>, pair<K, V>&, pair<K, V>*> iterator;
		typedef rb_tree::Reverse_Iterator<pair<K, V>, pair<K, V>&, pair<K, V>*> reverse_iterator;



		//迭代器
		iterator begin()
		{
			return _rb.begin();
		}

		iterator end()
		{
			return _rb.end();
		}

		reverse_iterator rbegin()
		{
			return _rb.rbegin();
		}

		reverse_iterator rend()
		{
			return _rb.rend();
		}

		bool insert(const pair<K, V>& data)
		{
			return _rb.insert(data);
		}

		iterator find(const K& data)
		{
			return _rb.find(data);
		}

	private:
		rb_tree::RBTree<K, pair<K, V>, MapKeyOfT> _rb;
	};
}

到了这里,关于C++中的红黑树的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构:红黑树讲解(C++)

    本文旨在 理解红黑树基本概念以及变色旋转规则 ,以理解C++ map 和 set 的底层原理,不会讲红黑树的删除操作。 对于基本的旋转操作(单旋和双旋),本文不会展开讲,详细讲解在这里: AVL树旋转讲解。 2.1概念 红黑树,是一种二叉搜索树,但在每个结点上 增加一个存储位表示

    2024年02月05日
    浏览(43)
  • 剑指XX游戏(六) - 轻松搞定面试中的红黑树问题

    原文地址 http://blog.csdn.net/silangquan/article/details/18655795?utm_source=tuicoolutm_medium=referral 连续两次面试都问到了红黑树,关键两次都没有答好,这次就完整地来学习整理一下。 没有学习过红黑树的同学请参考: Introduction to Algorithms Chapter 13 Red-Black Trees Chapter 14 Augmenting Data Structure

    2024年02月08日
    浏览(39)
  • 【高阶数据结构】红黑树 {概念及性质;红黑树的结构;红黑树的实现;红黑树插入操作详细解释;红黑树的验证}

    红黑树(Red Black Tree) 是一种自平衡二叉查找树,在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。 AVL树 VS 红黑树 红黑树是

    2024年02月09日
    浏览(49)
  • 数据结构 | 红黑树

    节点的左边比节点的值小,右边比节点的值大。 节点要么是 红色 ,要么是 黑色 根节点 是黑色 叶子节点都是黑色的空节点 红黑树中红色节点的子节点都是黑色 从任一节点到叶子节点的所有路径都包含相同数目的黑色节点 在添加或者删除节点的时候,如果不满足这些性质会

    2024年01月21日
    浏览(44)
  • [数据结构]-红黑树

    前言 作者 : 小蜗牛向前冲 名言: 我可以接受失败,但我不能接受放弃   如果觉的博主的文章还不错的话,还请 点赞,收藏,关注👀支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 目录 一、红黑树的基本知识  1、红黑树的概念 2、性质  二、红黑树的模拟实

    2024年02月04日
    浏览(46)
  • 【数据结构】红黑树

    🐱作者:一只大喵咪1201 🐱专栏:《数据结构与算法》 🔥格言: 你只管努力,剩下的交给时间! 在学习AVL树的时候,我们知道,当修改AVL树的结构(插入,删除)时,会通过旋转来保证平衡因子不超过1,所以频繁的修改结构会导致效率低下,今天我们学习的红黑树就完美解

    2023年04月17日
    浏览(49)
  • 数据结构——红黑树

    目录 概念 性质 结点的定义  插入 调整 当p是g的左孩子时 当p为g的右孩子时 插入完整代码 红黑树的检测 红黑树完整代码(包括测试数据)   红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是RED或BLACK。 通过对任何一条从根到叶子的路径

    2023年04月09日
    浏览(46)
  • 【数据结构-树】红黑树

    💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学习,不断总结,共同进步,活到老学到老 导航 檀越剑指大厂系列:全面总

    2024年02月07日
    浏览(44)
  • 红黑树数据结构

    现在JAVASE中HashMap中底层源码是由数组+链表+红黑树进行设计的,然后很多地方也是用到红黑树,这里单独对红黑树数据结构进行简单的介绍。 目录 红黑树概念 红黑树的性质 自平衡规则 代码   红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可

    2024年02月01日
    浏览(40)
  • 数据结构入门-11-红黑树

    史上最负盛名的平衡二叉树–红黑树,但其实就是2-3树的一种实现 也是BST,每一个节点都有颜色 性质 看 后面推导出来的结论 2-3树 :和红黑树是等价的 满足BST的基本性质,但不是一种二叉树 有两种节点: 2-3 绝对平衡:根节点到叶子节点 一定相同 2.3.1 如何维护绝对平衡

    2023年04月17日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包