【数据结构】—— 浅析红黑树及模拟实现

红黑树

红黑树的概念
  • 红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。
  • 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

红黑树

红黑树的性质

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

模拟实现红黑树

红黑树的定义
  template<class K,class V>
    struct RBSTreeNode
    {
    	RBSTreeNode(const pair<K, V>& kv)
    	:_left(nullptr)
    	, _right(nullptr)
    	, _parent(nullptr)
    	, _kv(kv)
    	, _col(RED)
    	{}
    	RBSTreeNode<K, V>* _left;
    	RBSTreeNode<K, V>* _right;
    	RBSTreeNode<K, V>* _parent;
    
    	pair<K, V> _kv;
    	color _col;
    };
红黑树的结点操作
  • 红黑树也属于二叉搜索树
  • 因此红黑树的插入操作和二叉搜索树一样,若树为空,则直接插入,若树不为空,则先找到插入位置,再插入节点**,插入节点的颜色默认为红色,破坏性质三**,因为破坏性质三后更好调整红黑树使其重新符合性质。
  • 唯一不同的是红黑树必须根据红黑树的性质调整节点的颜色
  • 检测新节点插入后,红黑树的性质是否造到破坏

因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:

情况一:

  • cur为新插入的节点,parent是grandfather的左孩子,uncle节点存在且颜色是红色
  • 此时只需要将parent和uncle的颜色变为黑色,把grandfather的颜色变为红色,再将grandfather给cur,继续向上调整节点颜色即可
    情况一
    情况二:
  • cur为新插入的节点,parent是grandfather的左孩子,uncle节点不存在或是颜色是黑色
  • cur是parent的左孩子,此时只需进行一个右单旋,之后将grandfather的颜色变为红色,parent的颜色变为黑色,就可终止调整
    在这里插入图片描述
    情况三:
  • cur为新插入的节点,parent是grandfather的左孩子,uncle节点不存在或是颜色是黑色
  • cur是parent的右孩子,此时只需进行一个左单旋转换为情况二后,按照情况二进行调整

情况三
情况四:

  • cur是新插入的节点,parent是grandfather的右孩子,uncle节点存在且颜色是红色
  • 此时只需要将parent和uncle的颜色变为黑色,把grandfather的颜色变为红色,再将grandfather给cur,继续向上调整节点颜色即可
    在这里插入图片描述
    情况五:
  • cur为新插入的节点,parent是grandfather的右孩子,uncle节点不存在或是颜色是黑色
  • cur是parent的右孩子,此时只需进行一个左单旋,之后将grandfather的颜色变为红色,parent的颜色变为黑色,就可终止调整
    情况五

情况六:

  • cur为新插入的节点,parent是grandfather的右孩子,uncle节点不存在或是颜色是黑色
  • cur是parent的左孩子,此时只需进行一个右单旋将情况转换为情况五继续处理即可
    情况六

红黑树的代码实现

  • 主要实现了红黑树的插入功能,以及验证该树是否满足红黑树的所有性质
#pragma once

#include <iostream>
using namespace std;

enum color
{
	RED,
	BLACK
};

template<class K,class V>
struct RBSTreeNode
{
	RBSTreeNode(const pair<K, V>& kv)
	:_left(nullptr)
	, _right(nullptr)
	, _parent(nullptr)
	, _kv(kv)
	, _col(RED)
	{}
	RBSTreeNode<K, V>* _left;
	RBSTreeNode<K, V>* _right;
	RBSTreeNode<K, V>* _parent;

	pair<K, V> _kv;
	color _col;
};

template<class K, class V>
class RBTree
{
	typedef RBSTreeNode<K, V> Node;
public:
	RBTree()
		:_root(nullptr)
	{}

	bool Insert(const pair<K, V>& kv)
	{
		//若树为空,直接插入
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = BLACK;
			return true;
		}

		//不为空,先找到插入位置再插入节点
		Node* parent = nullptr;
		Node* cur = _root;

		while (cur)
		{
			parent = cur;
			if (kv.first < cur->_kv.first)
				cur = cur->_left;
			else if (kv.first > cur->_kv.first)
				cur = cur->_right;
			else
				return false;//如果树中已经有该元素,则插入失败
		}

		//找到插入位置,插入节点
		//插入的节点颜色为红色,破坏红黑树的性质3,更好处理
		cur = new Node(kv);
		cur->_col = RED;
		if (kv.first < parent->_kv.first)
		{
			parent->_left = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_right = cur;
			cur->_parent = parent;
		}

		//插入节点成功后,检查红黑树的性质有没有被破坏
		//若是有则要进行节点的颜色调整以满足红黑树性质
		//若是父节点存在且父节点的颜色为红色则需要调整,否则满足红黑树性质
		while (parent && parent->_col == RED)
		{
			// 注意:grandFather一定存在
			// 因为parent存在,且不是黑色节点,则parent一定不是根,则其一定有双亲
			Node* grandfather = parent->_parent;
			
			//1、父节点是祖父节点的左孩子
			if (grandfather->_left == parent)
			{
				Node* uncle = grandfather->_right;
				//1、叔叔节点存在且叔叔节点的颜色为红色
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					cur = grandfather;
					parent = cur->_parent;
				}

				//2、叔叔节点不存在或者叔叔节点的颜色为黑色
				else
				{
					//1、如果cur是parent的右孩子,此时需要进行左单旋将情况转换为情况2
					if (parent->_right == cur)
					{
						RotateL(parent);
						swap(cur, parent);
					}

					//1、如果cur是parent的z左孩子,此时只需进行一个右单旋,并将parent的颜色变为黑,grandparent的颜色置红
					RotateR(grandfather);
					grandfather->_col = RED;
					parent->_col = BLACK;
					break;
				}

			}

			//2、父节点是祖父节点的右孩子
			else
			{
				Node* uncle = grandfather->_left;
				//1、叔叔节点存在且叔叔节点的颜色为红色
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					cur = grandfather;
					parent = cur->_parent;
				}

				//2、叔叔节点不存在或者叔叔节点的颜色为黑色
				else
				{
					//1、若是cur为parent的左孩子,先进行一个右单旋转换为情况二一起处理
					if (parent->_left == cur)
					{
						RotateR(parent);
						swap(cur, parent);
					}

					//2、若是cur为parent的右孩子,进行一个左单旋,并将parent的颜色变为黑,grandparent的颜色置红
					RotateL(grandfather);
					grandfather->_col = RED;
					parent->_col = BLACK;
					break;
				}
			}
		}

		//旋转完成之后,将根节点的颜色置成黑色
		_root->_col = BLACK;
		return true;
	}

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

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

		subR->_left = parent;
		parent->_parent = subR;

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

	//右单旋
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		Node* pparent = parent->_parent;

		parent->_left = subLR;
		if (subLR)//置parent->_left的时候可以不管subLR是否为空,但是若是subLR为空取其parent就会出错
		{
			subLR->_parent = parent;
		}

		subL->_right = parent;
		parent->_parent = subL;

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

		}
	}


	void Inorder()
	{
		_Inorder(_root);
	}

	void _Inorder(Node* root)
	{
		if (root == nullptr)
			return;
		_Inorder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_Inorder(root->_right);
	}

	bool IsValidRBTree()
	{
		Node* pRoot = _root;

		// 空树也是红黑树
		if (nullptr == pRoot)
			return true;

		// 检测根节点是否满足情况
		if (BLACK != pRoot->_col)
		{
			cout << "违反红黑树性质二:根节点必须为黑色" << endl;
			return false;
		}

		// 获取任意一条路径中黑色节点的个数
		size_t blackCount = 0;
		Node* pCur = pRoot;
		while (pCur)
		{
			if (BLACK == pCur->_col)
				blackCount++;
			pCur = pCur->_left;
		}

		// 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数
		size_t k = 0;
		return _IsValidRBTree(pRoot, k, blackCount);
	}

	bool _IsValidRBTree(Node* root, size_t k, const size_t blackCount)
	{
		if (nullptr == root)
			return true;

		// 统计黑色节点的个数
		if (BLACK == root->_col)
			k++;

		// 检测当前节点与其双亲是否都为红色
		Node* parent = root->_parent;
		if (parent && RED == parent->_col && RED == root->_col)
		{
			cout << "违反性质三:没有连在一起的红色节点" << endl;
			return false;
		}

		// 如果root是因子节点,检测当前路径中黑色节点的个数是否有问题
		if (nullptr == root->_left&& nullptr == root->_right)
		{
			if (k != blackCount)
			{
				cout << "违反性质四:每条路径中黑色节点的个数必须相同" << endl;
				return false;
			}
		}

		//递归判断左右子树都满足红黑树的性质
		return _IsValidRBTree(root->_left, k, blackCount) &&
			_IsValidRBTree(root->_right, k, blackCount);
	}

private:
	Node* _root;
};


void TestRBTree()
{
	int a[] = { 5, 3, 15, 10, 8, 7, 17, 16 };
	RBTree<int, int> rt;

	for (auto& e : a)
	{
		rt.Insert(make_pair(e, e));
	}

	cout << rt.IsValidRBTree() << endl;

	rt.Inorder();
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值