数据结构——红黑树

作者:小 琛
欢迎转载,请标明出处

红黑树的概念

红黑树,是一种二叉搜索树,它在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。

AVL树确保平衡是通过调节平衡因子实现,即每个节点平衡因子绝对值不大于1, 红黑树通过对任何一条从根到叶子的路径上各个结点着色方式的限制,而红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的。

相比较之前的AVL树,红黑树对于高度的要求比AVL差一些,但由于现代硬件设备的优化,这种差异几乎可以忽略。

红黑树的特点

红黑树要求最长路径的长度不会大于最短路径的两倍。

特点:

  1. 根节点的颜色为黑
  2. 树中每条路径的黑节点数量一定相同
  3. 若一个节点颜色为红,则其父亲节点和孩子节点一定为黑,即没有连续的红节点。

分析:
当满足上述的特点后,实际上实现了:最短路径就是全黑节点路径,最长路径就是一黑一红路径,因此最多是二倍的关系。
在这里插入图片描述

红黑树节点的设定

这里写一个kv模型的

成员:

  1. 该节点的左右孩子指针
  2. 该节点的父亲节点指针
  3. 该节点的颜色
  4. 该节点的值
enum Colour
{
	Red,
	Black,
};
template <class K,class V>
struct RBTreeNode
{
	RBTreeNode<K, V>* _parent;//该节点的父亲节点
	RBTreeNode<K, V>* _left;//该节点的左孩子
	RBTreeNode<K, V>* _right;//该节点的右孩子
	std::pair<K,V> _data;//该节点的数据
	Colour _col;//节点的颜色

	RBTreeNode(const std::pair<K, V> data)
		:_parent(nullptr)
		, _left(nullptr)
		, _right(nullptr)
		, _col(Red)
		, _data(data)
	{}
};

红黑树的插入

  1. 按照二叉搜索树的方式先进行插入,新插入的节点默认为红节点

    这里默认新插入的节点为红,原因在于:对于红黑树的三个限制条件,在新插入节点后总可能破坏一个,而条件(没有连续的红节点)比条件(没有连续的黑节点)更好进行调整

  2. 检查红黑树的条件是否被破坏

  3. 若第二步中,红黑树的条件已被破坏,则进行颜色调整

针对新插入的节点,总结下来有三种情况需要调整该树

设:待调整的节点为cur,父节点为p,叔叔节点为u,爷爷节点为g
在这里插入图片描述

  • cur为红色,p为红色,u存在且为红色,g为黑色。
    在这里插入图片描述

解决方法:p和u均变黑,g变红。若g为根,将g再变黑;若g不为根,g为cur继续向上检查。

在这里插入图片描述

  • cur为红,p为红,g为黑,u不存在或存在且为黑(若u不存在,则cur为新增节点;若u存在且为黑,证明该节点是第一种调整后产生的不符合)
    在这里插入图片描述
    在这里插入图片描述

解决方法:右旋,将p该为黑,g改为红;左旋,将p改为黑,g改为红。
在这里插入图片描述
在这里插入图片描述
举个例子:
在这里插入图片描述
在这里插入图片描述

  • cur为红且为左子树的右边或右子树的左边,p为红,g为黑,u不存在或存在为黑
    在这里插入图片描述

解决办法:先进行左旋,变为情况二,再根据情况二的解决方法解决。
在这里插入图片描述

整体代码

# pragma once

#include <iostream>

enum Colour
{
	Red,
	Black,
};
template <class K,class V>
class RBTreeNode
{
	RBTreeNode<K, V>* _parent;//该节点的父亲节点
	RBTreeNode<K, V>* _left;//该节点的左孩子
	RBTreeNode<K, V>* _right;//该节点的右孩子
	std::pair<K,V> _data;//该节点的数据
	Colour _col;//节点的颜色

	RBTreeNode(const std::pair<K, V> data)
		:_parent(nullptr)
		, _left(nullptr)
		, _right(nullptr)
		, _col(Red)
		, _data(data)
	{}
};

template <class K,class V>
class RBTree
{
	typedef RBTreeNode Node;
public:
	bool Insert(std::pair<K, V>& kv)
	{
		newnode = new Node(kv);
		if (_root == nullptr)
		{
			_root = newnode;
			_root->_col = Black;
			return true;
		}
		//按照二叉搜索树的规则插入
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			if (kv.first > cur->_data.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kv.first < cur->_data.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
				return false;
		}
		cur = newnode;
		if (cur->_data.first>parent->_data.first)
		{
			cur->_parent = parent;
			parent->_right = cur;
		}
		else
		{
			cur->_parent = parent;
			parent->_left = cur;
		}
		cur->_col = Red;//默认新插入节点为红

		//检查颜色是否满足要求
		while (parent && parent->_col == Red)
		{
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)//表明在左树进行插入
			{
				Node* uncle = grandfather->_right;
				//情况1:uncle存在且为红
				if (uncle && uncle->_col == Red)
				{
					//父亲叔叔变黑,爷爷变红
					parent->_col = uncle->_col = Black;
					grandfather->_col = Red;
					//继续向上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				//情况2:uncle不存在或uncle存在为黑
				else
				{
					//情况3:为左子树的右边,需要转换为情况2
					if (cur == parent->_right)
					{
						RotateL(parent);
					}
					//处理情况2
					RotateR(grandfather);
					grandfather->_col = Red;
					parent->_col = Black;

					break;
				}
			}
			//右树插入
			else
			{
				Node* uncle = grandfther->_left;
				// 情况1:uncle存在,且为红
				// 情况2 or 情况3:uncle不存在 or uncle存在,且为黑
				if (uncle && uncle->_col == Red)
				{
					parent->_col = uncle->_col = Black;
					grandfther->_col = Red;

					cur = grandfther;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_left)
					{
						RotateR(parent);
					}

					RotateL(grandfther);
					grandfther->_col = Red;
					parent->_col = Black;
				}
			}
			//根恒为黑色
			_root->_col = Black;
			return true;
		}

	}
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

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

		subR->_left = parent;
		Node* ppNode = parent->_parent;
		parent->_parent = subR;

		// 1、原来parent是这颗树的跟,现在subR是根
		// 2、parent为根的树只是整颗树中的子树,改变链接关系,那么subR要顶替他的位置
		if (_root == parent)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
				ppNode->_left = subR;
			else
				ppNode->_right = subR;

			subR->_parent = ppNode;
		}
	}

	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

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

		subL->_right = parent;
		Node* ppNode = parent->_parent;
		parent->_parent = subL;

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

			subL->_parent = ppNode;
		}
	}
private:
	Node* _root = nullptr;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值