红黑树,关联式容器的异同

在学习红黑树之前先看这篇文章
红黑树和2-3树之间的联系
红黑树:是一颗平衡二叉搜索树,给书中的结点增加颜色(红色和黑色) + 红黑树性质的限制
保证:最长路径中结点个数不会超过最短路径中结点个数的两倍

不像AVL树觉得平衡
红黑树是一颗近似平衡的二叉搜索树---->时间复杂度:O(logN)
大量的应用表明:红黑树的性质比AVL树的性能好,并且实现起来也比红黑树简单一些,所以在很多地方我们使用的其实是红黑树

**红黑树的概念:**红黑树,是一种二叉搜索树,但在每个节点上增加一个存储位表示节点的颜色,可以是Red或Black.通过对任何一条从根到叶子节点的路径会比其他路径长出两倍,因为是接近平衡的。
在这里插入图片描述
红黑树的性质
1.每个结点不是红色就是黑色
2.根结点是黑色
3.如果一个结点是红色的,则它的两个孩子结点是黑色的
4.对于每个结点,从该节点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点
5.每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

红黑树中不存在连在一起的红色结点,但是可以有黑色结点连在一起

通过颜色以及性质的限定:最长路径中结点个数不会超过最短路径中结点个数的两倍。

一般插入的时候新插入的结点都是红色的,这是为什么呢?
假如有下图这样一个黑色根节点
在这里插入图片描述
现在要插入一个新节点,假如是黑色的那就违反了四号性质,也就是说有一条路径有2个黑色结点,另一个路径只有一个黑色结点,所以新插入的结点一般都给成红色。

也就是说,下面这颗红黑树是不可能存在的
在这里插入图片描述
认为红黑树是一个近似平衡的二叉搜索树

在给红黑树中插入一个结点的时候到底是给成红色好,还是给成黑色好?
关于这个问题我们可以来假设一下,

在这里插入图片描述
按道理来说给成红色还是黑色都是可以的,因为本来插入的过程就是一个不断调整的过程,在插入之前整个红黑树已经平衡了,假如你插入的是一个黑色结点,那么肯定会破坏红黑树的性质,也就是说又要继续调整,并且是必学的,假如你插入的是红的结点的话,那么就有可能不需要调整,所以一般来说的花,我们一般选择默认插入的是红色结点。

接着我们就试着自己封装简单的红黑树:
首先我们来认识一下什么是空的红黑树

在这里插入图片描述
在这里插入图片描述

有关红黑树的插入:
该如何来调整节点的颜色呢?
在这里插入图片描述
将p结点设置为黑色,将u结点设置为黑色,双亲g改为红色,然后cur代替p,p代替g,继续向上更新。

在这里插入图片描述
在这里插入图片描述
情况2:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上面讨论的这三种情况都是p节点是g结点的左孩子,当然也可以是右孩子,反过来又是三种情况,这里就不详细介绍了。

//简单实现

#include<iostream>
#include<vector>
using namespace std;
namespace mytree
{
	enum color{ RED, BLACK };
	template<class T>
	struct RBTreeNode
	{
		RBTreeNode(const T& val = T(), color color = RED)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _val(val)
		, _color(color)
		{
		
		}


		RBTreeNode* _left;
		RBTreeNode* _right;
		RBTreeNode* _parent;
		T _val;
		color _color;
	};



	//假设红黑树中的值是唯一的
	template<class T>
	class RBTree
	{
		typedef RBTreeNode<T> Node;
		public:
			RBTree()  //创建头节点
			{
				pHead = new Node;
				pHead->_left = pHead;
				pHead->_right = pHead;
				pHead->_parent = nullptr;
				size = 0;
			}
			
			bool insert(const T& data)
			{
				Node*& pRoot = getRoot();
				if (nullptr == pRoot) //假如现在只有一个头节点
				{
					pRoot = new Node(val, BLACK);
					pHead->_left = pRoot;
					pHead->_right = pRoot;
					pHead->_parent = pHead;
					return true;
				}
				//非空
				//按照二叉搜索树的规律插入新节点
				
				Node* pCur = pRoot;
				Node* pParent = nullptr;
				while (pCur)
				{
					pParent = pCur;
					if (val < pCur->_val)
					{
						pCur = pCur->_left;
					}
					else if (val > pCur->_val)
					{
						pCur = pCur->_right;
					}
					else
					{
						//val对应结点已经存在
						return false;
					}

					//插入新节点
					pCur = new Node(val);
					if (val < pParent->_val)
						pParent->_left = pCur;
					else
						pParent->_right = pCur;

					pCur->_parent = pParent;

					//检测插入是否违反红黑树的性质
					//只需要检测一下性质三
					//新节点的默颜色是红色,如果新节点的双亲节点也是红色,必定会违反性质3
					
					
					while (pParent != pHead && RED == pParent->_color)
					{
						Node* grandFather = pParent->_parent;
						if (pParent == grandFather->_left)  //p节点是祖父节点的左孩子三种情况
						{
							//上面讨论的三种情况
							Node* uncle = grandFather->_right;
							if (uncle && RED = uncle->_color)
							{
								//情况1 :叔叔结点存在且为红色
								pParent->_color = BLACK;
								uncle->_color = BLACK;
								grandFather->_color = RED;

								//因为祖父有双亲,所以需要继续向上调整
								pCur = grandFather;
								pParent = pCur->_parent;
							}
							else
							{
								//情况二和情况三
								//先处理情况三
								if (pCur == pParent->_right)
								{
									//对以pParent为根的二叉树进行左单旋
									RotateLeft(pParent);
									swap(pParent, pCur);
								}
								//交换双亲和祖父的颜色
								grandFather->_color = RED;
								pParent->_color = BLACK;
								RotateRight(grandFather);

							}
						}
						else   //是右孩子的三种情况
						{
							Node* uncle = grandFather->_left;
							if (uncle && RED == uncle->_color)
							{
								//情况一的反情况
								pParent->_color = BLACK;
								pCur->_color = BLACK;
								grandFather->_color = RED;

								pCur = grandFather;
								pParent = pCur->_parent;

							}
							else
							{
								//情况二和情况三的反情况
								if (pCur == pParent->_left)
								{
									RotateRight(pParent);
									swap(pCur, pParent);
								}

								pParent->_color = BLACK;
								grandFather->_color = RED;
								RotateLeft(grandFather);
							}
						}

					}





					//更新pHead的左右指针域
					pRoot->_color = BLACK;
					pHead->_left = LeftMost();
					pHead->_right = RightMost();
					size++;
					return true;
				}
			}

			

	private:
		void RotateLeft(Node* pParent)  //左单选
		{
			Node* pSubR = pParent->_right;
			Node* pSubRL = pSubR->_left;
			pParent->_right = pSubRL;
			if (pSubRL)
				pSubRL->_parent = pParent;
			pSubR->_left = pParent;
			Node* pPParent = pParent->_parent;
			pParent->_parent = pSubR;
			pSubR->_parent = pPParent;
			if (pPParent == pHead)
			{
				//pParent就是根节点
				pHead->_parent = pSubR;
			}
			else
			{
				if (pParent == pPParent->_left)
					pPParent->_left = pSubR;
				else
					pPParent->_right = pSubR;
			}
		}

		void Rotateright(Node* pParent)  //右单旋
		{
			Node* pSubL = pParent->_left;
			Node* pSubLR = pSubL->_right;
			pParent->_left = pSubLR;
			if (pSubLR)
				pSubLR->_parent = pParent;

			pSubR->_right = pParent;
			Node* pPParent = pParent->_parent;
			pSubL->_parent = pPParent;
			pParent->_parent = pSubL;
			if (pPParent == pHead)
			{
				//pParent就是根节点
				pHead->_parent = pSubL;
			}
			else
			{
				if (pParent == pPParent->_left)
					pPParent->_right = pSubL;
				else
					pPParent->_left = pSubL;
			}
		}



		Node* LeftMost()
		{
			Node* pRoot = getRoot();
			if (nullptr == pRoot)
				return pHead;
			Node*  pCur = pRoot;
			while (pCur->_left)
				pCur = pCur->_left;
			return pCur;
		}

		Node* RightMost()
		{
			Node* pRoot = getRoot();
			if (nullptr == pRoot)
				return pHead;
			Node*  pCur = pRoot;
			while (pCur->_right)
				pCur = pCur->_right;
			return pCur;
		}

		Node*& gerRoot()
		{
			return pHead->_parent;
		}
		Node* pHead;
		size_t size;
	};
};

树形结构 map / set / multimap / multiset
相同点:
1.STL标准库提供的关联式容器
2.进行中序遍历可以得到有序序列—>默认情况下升序
3.模板参数列表是按照less小于来比较K的,如果将来插入自定义类型的元素需要用户自己提供比较的方式(仿函数)
4.查询的时间复杂度:O(logN)
5.底层应用好的都是红黑树
6.迭代器移动的方式:都是按照中序遍历的方式进行移动的
不同点:
map:存储的是K-V键值对,要求K必须要唯一。
set:只存储了K,要i去K必须唯一
multimap:存储的是K-V键值对,K可以重复
mutliset:只存储了K,并且K可以重复
作用:
map:按照key进行去重 + 排序
set的作用:去重和对去重之后的结果进行排序。
multimap:按照key进行排序
multiset:排序

map/multimap在实例化的时候:必须提供K-V的类型 + map头文件& 命名空间
set/multiset在实例化的时候只需要提供K的类型 + set头文件 & 命名空间

接口上:大部分接口都是一样的,map多了一个根据key取value的操作:operator[ ] (key)

如果查找 操作比较多,那就用unordered系列的,如果需要有序就使用树形结构

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值