[C++] 红黑树详解

红黑树

红黑树,一种特殊的二叉搜索树, 为每个结点描述了颜色(红色或黑色),通过对所有结点的着色方式限制,红黑树确保了没有一条路径会比其他路径的长度超出两倍,因而是近似平衡的。
在这里插入图片描述

红黑树的性质
红黑树通过以下性质来实现近似平衡结构:

  1. 根节点是黑色的 ;
  2. 每个结点不是红色就是黑色;
  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的 ; (红色结点不能连续出现)
  4. 对于任意结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点 ;
  5. 每个叶子结点都是黑色的 ( 此处的叶子结点指的是空结点----NIL结点 )。

根据性质我们可以推出:最短路径为只有黑色结点的路径,最长路径为红黑相间的路径

在这里插入图片描述

红黑树和AVL树对比

红黑树搜索的时间复杂度:O(logN)
最短路径:O(logN)
最长路径:2*O(logN)

AVL树搜索的时间复杂度:O(logN)

红黑树的效率比AVL树略差

1、红黑树的性能略差于AVL树,但是对于现代硬件的执行速度来说,差距非常小;
2、AVL树严格的平衡结构是通过大量的旋转及调节平衡因子来实现的,红黑树之所以比AVL树使用更广泛的一个重要原因:红黑树在插入和删除时的旋转操作更少

红黑树的结构

为了后续实现关联式容器简单,红黑树的实现中增加一个头结点,因为跟节点必须为黑色,为了与根节点进行区分,将头结点给成黑色,并且让头结点的 pParent 域指向红黑树的根节点,pLeft域指向红黑树中最小的节点,_pRight域指向红黑树中最大的节点,如下:
在这里插入图片描述

红黑树的插入

cur为当前结点;p为父节点;g为祖父结点;u为叔叔结点

情况一

在这里插入图片描述

1、如果这里是一棵完整的树(g是根结点),把g再变黑,结束调整;
2、如果这里的树是另一个结点的子树(g不是根结点),g的父节点也有可能是红色,所以还需要继续向上调整:

a、如果g的父亲是黑色,结束调整;
b、如果g的父亲是红色,cur更新到g的位置,继续调整。

情况二

在这里插入图片描述

情况三

在这里插入图片描述

代码:

#include<iostream>
using namespace std;

enum Color{
	BLACK,
	RED
};

template<class V>
struct RBTreeNode {
	RBTreeNode<V>* _left;
	RBTreeNode<V>* _right;
	RBTreeNode<V>* _parent;
	V _val;
	Color _color;

	RBTreeNode(const V&val = V())
		:_parent(nullptr)
		,_left(nullptr)
		,_right(nullptr)
		,_val(val)
		,_color(RED)
	{}
};

//KeyOfVal:通过V获取其对应的K值
template<class K, class V, class KeyOfVal>
class RBTree {
public:
	typedef RBTreeNode<V> Node;

	RBTree() :_header(new Node)
	{
		_header->_left = _header->_right = _header;
	}

	bool insert(const V&val)
	{
	//二叉搜索树的插入

		if (_header->_parent == nullptr)
		{
			//创建根结点
			Node* root = new Node(val);

			_header->_parent = root;
			root->_parent = _header;
			_header->_left = _header->_right = root;
			
			//根结点是黑色
			root->_color = BLACK;
			return true;
		}

		//从根节点开始搜索
		Node* cur = _header->_parent;
		Node* parent = nullptr;

		KeyOfVal kov;
		while (cur)
		{
			parent = cur;
			if (kov(cur->_val)==kov(val))
			{
				//key值不允许重复
				return false;
			}
			else if (kov(cur->_val) > kov(val))
			{
				cur = cur->_left;
			}
			else
			{
				cur = cur->_right;
			}
		}

		//创建待插入的结点
		cur = new Node(val);
		if (kov(parent->_left) == kov(cur->_val))
			parent->_left = cur;
		else
			parent->_right = cur;
		cur->_parent = parent;

	//修改颜色或调整结构

		//判断是否有红色连续的结点
		while (cur != _header->_parent&&cur->_parent->_color == RED)
		{
			parent = cur->_parent;
			Node* gfather = parent->_parent;
			
			//插入发生在祖父结点的左边时
			if (gfather->_left == parent)
			{

				Node* uncle = gfather->_right;

				//1.uncle存在并且是红色
				if (uncle&&uncle->_color == RED)
				{
					parent->_color = uncle->_color = BLACK;
					gfather->_color = RED;

					//继续向上更新
					if (gfather != _header->parent)
						cur = gfather;
				}
				else
				{
					//判断是否为双旋
					if (cur == parent->_right)
					{
						//左旋
						RotateL(parent);
						//交换cur和parent
						swap(cur, parent);
					}
					//右旋
					RotateR(gfather);
					gfather->_color = RED;
					parent->_color = BLACK;
					break;
				}	
			}

			//插入发生在祖父结点的右边时
			else
			{

			}

		}
		_header->_parent->_color = BLACK;
		_header->_left = leftMost();
		_header->_right = rightMost();
		return true;
	}

	Node* leftMost()
	{
		Node* cur = _header->_parent;
		while (cur&&cur->_left)
		{
			cur = cur->_left;
		}
		return cur;
	}
	Node* rightMost()
	{
		Node* cur = _header->_parent;
		while (cur&&cur->_right)
		{
			cur = cur->_right;
		}
		return cur;
	}

	void inorder()
	{
		_inorder(_header->_parent);
		cout << endl;
	}
	void _inorder(Node* root)
	{
		_inorder(root->_left);
		cout << root->_val<<" ";
		_inorder(root->_right);
	}

	//左旋
	void RotateL(Node* cur)
	{
		Node* curR = cur->_right;
		Node* curRL = curR->_left;

		curR->_left = cur;
		cur->_right = curRL;

		if (curRL)
		{
			curRL->_parent = cur;
		}
		if (cur == _header->parent)
		{
			_header->parent = curR;
			curR->_parent = _header;
		}
		else
		{
			Node* pparent = cur->_parent;
			if (pparent->_left == cur)
				pparent->_left = curR;
			else
				pparent->_right = curR;
			curR->_parent = pparent;
		}
		cur->_parent = curR;
	}


	//右旋
	void RotateR(Node* cur)
	{
		Node* curL = cur->_left;
		Node* curLR = curL->_right;

		curL->_right = cur;
		cur->_left = curLR;
		if (curLR)
		{
			curLR->_parent = cur;
		}
		if (cur == _header->parent)
		{
			_header->parent = curL;
			curL->_parent = _header;
		}
		else
		{
			Node* pparent = cur->_parent;
			if (pparent->_left == cur)
				pparent->_left = curL;
			else
				pparent->_right = curL;
			curL->_parent = pparent;
		}
		cur->_parent = curL;
	}
private:
	Node* _header = nullptr;
};

void test()
{}

int main()
{
	return 0;
}
  • 25
    点赞
  • 105
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值