红黑树笔记总结(内附旋转思维导图)

目录

什么是红黑树?

为什么要引入红黑树?

红黑树的性质

红黑树的旋转:关键看“叔叔”

思维导图

 完整源码


什么是红黑树?

        红黑树是一种特化的AVL树(平衡二叉树),它的结点由红色结点和黑色结点组成


为什么要引入红黑树?

        平衡二叉树可以防止最坏的二叉查找树生成,让时间复杂度维持在O(logN),大大提高了二叉查找树的效率,但是平衡二叉树的插入、删除需要多次的旋转来维持平衡,维护代价太大。于是我们引入红黑树来代替平衡二叉树。红黑树和平衡二叉树效率相差不大(尤其在数据小的时候),且比平衡二叉树维护代价小。

        红黑树算是平衡二叉树和二叉查找树的一种“折中”方法

ps.STL中的容器map、set的底层都是红黑树


红黑树的性质

  1. 每一个结点不是黑色就是红色
  2. 根节点必须是黑色
  3. 红色结点的子节点必须是黑色的(红色结点不能连续,黑色可以)
  4. 每个nil结点是黑色的(nil结点:可以当成null的代称)
  5. 每一个路径上包含相同数目的黑色结点(从根节点到nil结点为一条路径!)

 因为红黑树保持上述性质,所以红黑树能做到确保没有一条路径会比其他路径长出两倍

下面就是一颗红黑树:

(nil其实就是null结点,画了一个方便理解) 

35ccb615c48e4221bc01de9a8cc1dcb9.png


红黑树的旋转:关键看“叔叔”

        判断红黑树是否的旋转可以大致分为以下两种情形:

  • 有叔叔结点且叔叔结点为红色:变色
  • 没有叔叔结点或叔叔结点为黑色:旋转+变色

(左旋右旋的原理在这里不做赘述) 

让我们放大来看这两个情形:

(设在左子树中新插入一个结点)

叔叔存在且叔叔为红色:

叔叔不存在或叔叔为黑色:

1.在左子树的左边结点插入:

 

2.在左子树的右边结点插入:

 这一部分的代码实现如下:

(左旋和右旋的代码附在最后)

        //关键看叔叔
            if (parent == grandfather->_left)
            {
                node* uncle = grandfather->_right;
                //叔叔是红色的
				if (uncle && uncle->_col == Red)
				{
					parent->_col = uncle->_col = Black;
					grandfather->_col = Red;
					//向上调整
					cur = grandfather;
					parent = cur->_parent;
				}
				else //叔叔不存在或叔叔为黑色
				{
					右单旋+变色
					if (cur == parent->_left)
					{
						RotateR(grandfather);
						parent->_col = Black;
						grandfather->_col = Red;
					}
					左右单旋+变色
					else
					{
						RotateL(parent);
						RotateR(grandfather);
						grandfather->_col = Red;
						cur->_col = Black;
					}
					break;
				}
			}

(如果在右子树中插入新结点就是左子树的反向,这里就直接附代码了,可以自己对比理解一下)

            else //看右边去
			{
				node* uncle = grandfather->_left;

				if (uncle && uncle->_col == Red)
				{
					parent->_col = uncle->_col = Black;
					grandfather->_col = Red;
					//向上调整
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					左单旋+变色
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						parent->_col = Black;
						grandfather->_col = Red;
					}
					else
					{
						RotateR(parent);
						RotateL(grandfather);
						grandfather->_col = Red;
						cur->_col = Black;
					}
					break;
				}
			}
		}

思维导图

于是可以总结出一个关于红黑树旋转过程的思维导图:

 思维导图放大版:

 完整源码

        插入时红黑树调整的代码:

        //红黑树调整
		while (parent && parent->_col == Red)
		{
			node* grandfather = parent->_parent;
			assert(grandfather);
			assert(grandfather->_col == Black);
			//关键看叔叔
			if (parent == grandfather->_left)
			{
				node* uncle = grandfather->_right;
				//叔叔是红色的
				if (uncle && uncle->_col == Red)
				{
					parent->_col = uncle->_col = Black;
					grandfather->_col = Red;
					//向上调整
					cur = grandfather;
					parent = cur->_parent;
				}
				else //叔叔不存在或叔叔为黑色
				{
					右单旋+变色
					if (cur == parent->_left)
					{
						RotateR(grandfather);
						parent->_col = Black;
						grandfather->_col = Red;
					}
					左右单旋+变色
					else
					{
						RotateL(parent);
						RotateR(grandfather);
						grandfather->_col = Red;
						cur->_col = Black;
					}
					break;
				}
			}
			else //看右边去
			{
				node* uncle = grandfather->_left;

				if (uncle && uncle->_col == Red)
				{
					parent->_col = uncle->_col = Black;
					grandfather->_col = Red;
					//向上调整
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					左单旋+变色
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						parent->_col = Black;
						grandfather->_col = Red;
					}
					else
					{
						RotateR(parent);
						RotateL(grandfather);
						grandfather->_col = Red;
						cur->_col = Black;
					}
					break;
				}
			}
		}
        //调整结束后令根节点为黑色
		_root->_col = Black;

        左旋右旋的代码:

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

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

		node* pparent = parent->_parent;
		SubL->_right = parent;
		parent->_parent = SubL;

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

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

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

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

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值