【红黑树】

一、什么是红黑树?

红黑树是一种二叉搜索树,但是每个结点上都增加一个颜色,可以是红色或者黑色;通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍(相对于二叉搜索树条件弱化了),因而接近平衡;

二、红黑树的性质

(1)每个结点不是红色就是黑色;
(2)根节点是黑色的
(3)若一个结点是的那么它的两个孩子结点都是黑色的
(4)每个结点,从该结点到其他所有后代叶节点的路上,所包含的黑色结点数目是相同的
(5)每个叶子节点都是黑的;

三、红黑树结点的定义和结构
1、红黑树的结构

(1)红黑树实现中添加了头结点,因为根节点必须为黑色,为了与根节点进行区分,将头结点也给成黑色
(2)头结点的双亲指针指向红黑树的根节点, 左孩子指向红黑树中最小的结点,右孩子指向红黑树中最大的结点;
在这里插入图片描述

2、红黑树结点的定义

// 节点的颜色
enum Color{RED, BLACK};
// 红黑树节点的定义
template<class ValueType>
struct RBTreeNode
{
RBTreeNode(const ValueType& data = ValueType(),Color color = RED)
: _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
, _data(data), _color(color)
{}
RBTreeNode<ValueType>* _pLeft; // 节点的左孩子
RBTreeNode<ValueType>* _pRight; // 节点的右孩子
RBTreeNode<ValueType>* _pParent; // 节点的双亲(红黑树需要旋转,为了实现简单给出该字段)
ValueType _data; // 节点的值域
Color _color; // 节点的颜色

为什么要将结点的默认颜色设为红色?
插入红色节点树的性质可能不会改变,而插入黑色节点每次都会违反性质4,故将结点设为红色可以减少认为对红黑树的影响;

三、红黑树的操作
1、插入

(1)按照二叉搜索树的规则插入新的结点;
(2)检测插入新结点后,红黑树的性质是否遭到了破坏;
遭到破坏的情况:
(其中cur为当前结点,p为父节点,g为祖父节点,u为叔叔结点)
a)cur为红,p为红,g为黑,u存在且为红:(cur一定为新增店)
把p、u改为黑,g改为红,然后把g当成cur继续向上调整;
在这里插入图片描述
b)cur为红,p为红,g为黑,u不存在/u为黑
(若u存在且为黑,那么cur一定不是新增的)
在这里插入图片描述解决方法:进行右单旋,p变黑,g变红;
同理如果是在右子树的右边插入破坏了上述规则,进行左单旋即可,并进行变色;
c)cur为红,p为红,g为黑**,u不存在/u为黑;**
在这里插入图片描述解决方法:
对其进行左单旋,变成了情况b),进行右单旋;
其他情况适当取反,若是在g的右孩子的左边插入,则对其进行右旋,回到情况b);

注:上述情景只是一个抽象的情况,而不是一个具象的情况
2、验证

(1)验证其是否满足二叉搜索树(中序遍历是否为有序序列)
(2)检测其是否满足红黑树的性质;

红黑树实现代码:
https://github.com/Wilingpz/sunny/tree/master/%E7%BA%A2%E9%BB%91%E6%A0%91

四、红黑树和AVL树的比较

(1)红黑树和AVL树都是高效的平衡二叉树,增删查改的时间复杂度都是o(log以二为底的N)
(2)红黑树不追求绝对的平衡,只需要保证最长路径不超过最短路径的两倍即可;AVL树确保任何一个节点的左右子树的高度差的绝对值不大于1即可;
(3)相对于AVL树,红黑树降低了插入和旋转的次数,所以在增删过程中的性能优于AVL树,且红黑树的实现比较简单;

五、利用红黑树模拟实现map和set

红黑树中的迭代器

  • begin()和end();
    将begin()放在红黑树中最小结点(最左侧结点)的位置(红黑树的中序遍历会得到一个有序的序列),end()放在头结点位置
    -end()无法找到最大结点的下一个位置是在哪里,所以将end()的位置放在头结点处
static RBTreeNode<T> * increasement(RBTreeNode<T> * cur)//升序
		{
			RBTreeNode<T> * tmp = cur;
			if (cur->m_right)
			{//找cur右子树的最左子树
				tmp = cur->m_right;
				for (; tmp->m_left; tmp = tmp->m_left);
			}
			else//cur的右子树不存在,直到找到cur!=parent->right
			{
				tmp = tmp->m_parent;
				for (; cur != tmp->m_left && cur == tmp->m_right; tmp = tmp->m_parent)
				{
					cur = tmp;
				}
			}
			return tmp;//返回离cur最近的双亲结点(让cur作为左子树的结点)
		}

		static RBTreeNode<T> * decreasement(RBTreeNode<T> * cur)
		{
			RBTreeNode<T> * tmp = cur;
			if (cur->m_left)//cur的左子树存在
			{
				//找cur左子树的最右子树
				tmp = cur->m_left;
				for (; tmp->m_right; tmp = tmp->m_right);
			}
			else//直到找到cur!=parent->left
			{
				tmp = tmp->m_parent;
				for (; cur != tmp->m_right && cur == tmp->m_left; tmp = tmp->m_parent)
				{
					cur = tmp;
				}
			}
			return tmp;
		}
class iterator
		{
			RBTreeNode<T> * m_ptr;
		public:
			iterator(RBTreeNode<T> * val = nullptr) :
				m_ptr(val)
			{}

			iterator operator++()
			{
				m_ptr = increasement(m_ptr);
				return *this;
			}

			iterator operator++(int)
			{
				iterator tmp = *this;
				m_ptr = increasement(m_ptr);
				return tmp;
			}

			iterator operator--()
			{
				m_ptr = decreasement(m_ptr);
				return *this;
			}

			iterator operator--(int)
			{
				iterator tmp = *this;
				m_ptr = decreasement(m_ptr);
				return tmp;
			}

			T & operator*()
			{
				return m_ptr->m_data;
			}

			RBTreeNode<T> * operator->()
			{
				return m_ptr;
			}

			bool operator==(const iterator & val) const
			{
				return m_ptr == val.m_ptr;
			}

			bool operator!=(const iterator & val) const
			{
				return m_ptr != val.m_ptr;
			}
		};

		iterator begin()
		{
			return m_head->m_left;
		}

		iterator end()
		{
			return m_head;
		}
	};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值