数据结构 - 红黑树

目录

一、红黑树概念

二、代码实现

1 节点定义

2 树的定义

3 插入

3.1 情况一:u节点存在且为红

 3.2 情况二:u节点不存在或存在且为黑。

 3.3 代码实现部分

4 验证


一、红黑树概念

        红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。红黑树就能保证其最长路径中节点个数不会超过最短路径节点个数的两倍。

满足性质:

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



二、代码实现

1 节点定义

    enum Color { RED, BLACK };
    RBTreeNode(const ValueType& data = ValueType())
		: _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
		, _data(data), _color(RED)
	{}
	RBTreeNode<ValueType>* _pLeft; // 节点的左孩子
	RBTreeNode<ValueType>* _pRight; // 节点的右孩子
	RBTreeNode<ValueType>* _pParent; // 节点的双亲
	ValueType _data; // 节点的值域
	Color _color; // 节点的颜色

2 树的定义

template<class ValueType>
class RBTree
{
	typedef RBTreeNode<ValueType> Node;
public:
	RBTree()
		:_root(nullptr)
	{}
	bool Insert(const ValueType& data);
	bool IsBalance();
private:
	bool _isBanlance(Node* cur,int banchmark,int blacknum);
	void RotateL(Node* parent);
	void RotateR(Node* parent);
private:
	RBTreeNode<ValueType>* _root;
};

3 插入

         在这里将节点按下图的方式命名,p表示当前节点的父亲节点,u表示叔叔节点,g表示祖父节点。插入节点默认为红节点,如果插入节点的父节点是黑,则红黑树规则不会被破坏,无需任何操作。据此,将节点插入分为两种需要操作的情况,情况一:u节点存在且为红,情况二:u节点不存在或存在且为黑。

        下图是p节点在左,p节点在右的情况是完全对称的,就不在区分出来分析。

3.1 情况一:u节点存在且为红

        将p、u改为黑,g改为红。

         如果g是根节点,则最后g改为黑。如果g是子树的根节点,且该节点的父节点还是红机点,则g是新的cur节点向上判断。

 3.2 情况二:u节点不存在或存在且为黑。

        该情况下二叉树需要旋转。旋转规则跟AVL树类似。又可细分两个情况,cur在p左插入,cur在p右插入。第一种需要g右旋,p改为黑,g改为红,第二种情况先对p左旋就变成了第一种情况。

 3.3 代码实现部分

	bool Insert(const ValueType& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_color = BLACK;
			return true;
		}
		//找到插入节点位置
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_data < data)
			{
				parent = cur;
				cur = cur->_pRight;
			}
			else if (cur->_data > data)
			{
				parent = cur;
				cur = cur->_pLeft;
			}
			else
			{
				return false;
			}
		}
		//插入节点
		cur = new Node(data);
		if (parent->_data > data)
			 parent->_pLeft = cur;
		else if (parent->_data < data)
			parent->_pRight = cur;
		else
			assert(false);
		cur->_pParent = parent;

		//控制平衡
		while (parent && parent->_color == RED)
		{
			Node* grandfather = parent->_pParent;
			if (parent == grandfather->_pLeft)//父节点在左 
			{
				Node* uncle = grandfather->_pRight;
				//uncle存在切为红,往上变色
				if (uncle && uncle->_color == RED)
				{
					parent->_color = uncle->_color = BLACK;
					grandfather->_color = RED;
					cur = grandfather;
					parent = grandfather->_pParent;
				}
				//uncle不存在或者存在为黑,需要旋转
				else 
				{
					if (cur == parent->_pLeft)//需要单旋
					{
						RotateR(grandfather);
						grandfather->_color = RED;
						parent->_color = BLACK;
					}
					else//需要双旋
					{
						RotateL(parent);
						RotateR(grandfather);
						grandfather->_color = RED;
						cur->_color = BLACK;
					}
				}
			}
			else
			{
				Node* uncle = grandfather->_pLeft;
				//uncle存在切为红,往上变色
				if (uncle && uncle->_color == RED)
				{
					parent->_color = uncle->_color = BLACK;
					grandfather->_color = RED;
					cur = grandfather;
					parent = grandfather->_pParent;
				}
				//uncle不存在或者存在为黑,需要旋转
				else
				{
					if (cur == parent->_pRight)//需要单旋
					{
						RotateL(grandfather);
						grandfather->_color = RED;
						parent->_color = BLACK;
					}
					else//需要双旋
					{
						RotateR(parent);
						RotateL(grandfather);
						grandfather->_color = RED;
						cur->_color = BLACK;
					}
				}
			}
		}
		_root->_color = BLACK;
		return true;
	}
	void RotateL(Node* parent)
	{
		Node* subR = parent->_pRight;
		Node* subRL = subR->_pLeft;

		parent->_pRight = subRL;
		if (subRL)
		{
			subRL->_pParent = parent;
		}

		Node* parentParent = parent->_pParent;
		subR->_pLeft = parent;
		parent->_pParent = subR;

		if (_root == parent)
		{
			_root = subR;
			subR->_pParent = nullptr;
		}
		else
		{
			if (parentParent->_pLeft == parent)
				parentParent->_pLeft = subR;
			else
				parentParent->_pRight = subR;
			subR->_pParent = parentParent;
		}
	}

	void RotateR(Node* parent)
	{
		Node* subL = parent->_pLeft;
		Node* subLR = subL->_pRight;

		parent->_pLeft = subLR;
		if (subLR)
			subLR->_pParent = parent;

		Node* parentParent = parent->_pParent;

		subL->_pRight = parent;
		parent->_pParent = subL;

		if (parent == _root)
		{
			_root = subL;
			_root->_pParent = nullptr;
		}
		else
		{
			if (parentParent->_pLeft == parent)
				parentParent->_pLeft = subL;
			else
				parentParent->_pRight = subL;
			subL->_pParent = parentParent;
		}
	}

4 验证

        首先验证根节点必须是黑色。遍历最左路径,找到一条路径上的黑色节点数作为参考值。通过递归的方法记录每条路径的黑色节点数,并与参考值比较。同时在遍历路径的时候检查是否出现连续的红色节点。

    bool _isBanlance(Node* cur,int banchmark,int blacknum)
	{
		if (cur == nullptr)
		{
			if (banchmark == blacknum)
				return true;
			std::cout << "路径黑色节点数不等" << std::endl;
			return false;
		}
		if (cur->_color == RED && cur->_pParent->_color == RED)
		{
			std::cout << "出现连续红色节点" << std::endl;
			return false;
		}
		if (cur->_color == BLACK)
			blacknum++;
		return _isBanlance(cur->_pLeft, banchmark, blacknum)
			&& _isBanlance(cur->_pRight, banchmark, blacknum);
	}
	bool IsBalance()
	{
		if (_root && _root->_color == RED)
		{
			std::cout << "根节点是红色" << std::endl;
			return false;
		}
		int banchmark = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_color == BLACK)
				banchmark++;
			cur = cur->_pLeft;
		}
		int blacknum = 0;
		return _isBanlance(_root,banchmark,blacknum);
	}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
红黑树是一种自平衡的二叉查找树,它在插入和删除节点时能够保持树的平衡。红黑树的概念可以参考。在Java中实现红黑树,可以按照以下步骤进行: 1. 首先将红黑树当作一颗二叉查找树,将新节点插入到适当的位置上。 2. 将插入的节点着色为"红色"。 3. 根据红黑树的特性,通过一系列的旋转和着色等操作,使树重新保持红黑树的性质。 具体的插入过程可以参考中提供的代码。在代码中,使用了左旋转、右旋转和颜色翻转等操作来重新平衡红黑树。 首先,如果节点的右子树是红色而左子树是黑色,可以通过左旋转操作将其变为左子树为红色,右子树为黑色的情况。 其次,如果节点的左子树和左子树的左子树都是红色,可以通过右旋转操作将其修正为上述情况。 最后,如果节点的左子树和右子树都是红色,可以通过颜色翻转操作将其修正为左子树和右子树都为黑色的情况。 在插入完节点后,需要将根节点的颜色设置为黑色,以确保红黑树的性质满足。 这样,通过以上的步骤,就能够实现对红黑树的插入操作。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Java数据结构红黑树的真正理解](https://download.csdn.net/download/weixin_38622475/12770272)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Java高阶数据结构红黑树](https://blog.csdn.net/qq15035899256/article/details/126678970)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Java数据结构——红黑树](https://blog.csdn.net/weixin_30699463/article/details/95256212)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值