红黑树的插入、及模拟实现红黑树(附代码 + 图解 + 注释)

1.红黑树概念及性质

红黑树:为一棵二叉搜索树,树中的每一个结点颜色不是红色就是黑色。

红黑树性质

  1. 根节点和所有的外部节点的颜色都是黑色;外部节点为叶子节点的左右孩子节点
  2. 从根节点到外部节点的途中没有连续两个节点的颜色为红色;
  3. 从根节点到外部节点的所有路径上都有着相同的黑色节点。
  4. 最长路径中节点个数不会超过最短路径节点个数的两倍。

证第4条性质:
假设一红黑树任意路径的黑色节点个数为r,由性质2可得,不存在连续两个红色的节点,因此红色节点 后面都会跟随着黑色节点,则从根节点到任一外部节点路径上都有r~2r个节点。故得证。

结论1.设h为红黑树的高度,n为树中节点的个数,r为任一路径的黑色节点个数,则有以下关系:
(1) h ≤ 2r
(2) n ≥ 2^r - 1
(3) h ≤ 2log2(n + 1)

证明:
(1)从根到任一外部节点的路径长度不超过2r,同时从树的定义可知,树的高度即为根节点的高度,等于从根到离根最远的外部节点的路径的长度,因此有h ≤ 2r
(2)红黑树任一路径黑色节点数为r,则从第1层到第r层没有外部节点,因而在这些层内共有2^r - 1个内部节点,也就是说,内部节点的总数至少为2 ^ r - 1
(3)由(2)得 r ≤ log2(n + 1),则推得,h ≤ 2log2(n + 1)

红黑树的最大高度为2log2(n + 1),所以插入、搜索、删除的时间复杂度为O(log2(n))

2.红黑树的插入

使用bst的插入算法将一个元素插入到红黑树中,将该元素作为新的叶节点插入到某一位置,并为该元素进行染色。

  1. 如果插入前为空树,那么新元素将成为根节点,根据性质1,根节点必须为黑色;
  2. 如果插入前树非空,若新节点被染成黑色,那么将违反性质3,根节点到任一外部节点路径上黑色节点数不相等,因此,一般将新插入的节点默认为染成红色,但这可能会违反性质2,出现连续2个红色节点,故需要重新平衡。

cur:当前节点,parent:父节点,grandfather:祖父节点,uncle:叔叔节点

2.1 第一种情况

cur为红,parent为红,grandfather为黑,uncle存在且为红。

cur 和 parent 出现连续两个红色节点,则违反了性质3,解决办法:对parent、uncle节点染色为黑,将grandfather染色为红色,此时可能依然会出现两个连续的红色节点,则将cur节点变更为grandfather节点,继续向上更新。

在这里插入图片描述

情况1伪代码:

if (uncle && uncle->_color == RED)//uncle节点存在且为红色
{
	parent->_color = BLACK;//父节点变为黑色
	uncle->_color == BLACK;//叔叔节点变为黑色
	grandfather->_color = RED;//祖父节点变为红色
	cur = grandfather;//cur变更为当前祖父节点,继续向上更新
	parent = cur->_parent;//parent变为当前cur的父节点
}

继续向上更新是因为,当前祖父节点可能是子树,可能为根节点,若为子树,则需要向上更新,若为根节点,则在更新完毕之后,再将根节点置为黑色。

2.2 第二种情况

cur为红,parent为红,grandfather为黑,uncle不存在或者uncle为黑。

uncle的两种情况

  1. 如果uncle节点不存在,则cur一定为刚插入的新节点,因为如果cur不是新插入的节点,则cur和parent一定有一个节点的颜色为黑色,则不满足性质3,每条路径上黑色节点数相同;
  2. 如果uncle节点存在,则一定为黑色,那么cur节点原来的颜色一定是黑色的,现在变为红色是因为cur的子树在调整的过程中将cur节点的颜色由黑色变为了红色。

解决办法:parent为grandfather的左孩子,cur为parent的左孩子,对grandfather进行右单旋,parent变为黑色,grandfather变为红色。

  1. uncle存在且为黑
    在这里插入图片描述
  2. uncle不存在
    在这里插入图片描述

2.3 第三种情况

cur为红,parent为红,grandfather为黑,uncle不存在或者uncle为黑。

这种情况下,主要是cur节点在parent节点右侧。

解决办法:parent为grandfather的左孩子,cur为parent的右孩子,先对parent进行左单旋,交换cur和parent节点,再对grandfather进行右单旋,parent变为黑色,grandfather变为红色。

  1. uncle存在且为黑
    在这里插入图片描述
  2. uncle不存在
    在这里插入图片描述

情况2与情况3伪代码:

if (cur == parent->_right)//cur节点在右侧,先左单旋,转化为情况2
{
	RotateLeft(parent);//左单旋
	swap(parent, cur);//交换parent和cur节点
}

RotateRight(grandfather);//祖父节点右单旋
grandfather->_color = RED;//祖父颜色变为红色
parent->_color = BLACK;//父节点颜色变为黑色

上述三种情况,全部是在parent节点在祖父节点左侧的情况。
下面讨论,parent节点在祖父节点右侧的情况。

2.4 第四种情况

cur为红,parent为红,grandfather为黑,uncle存在且为红。

与情况一类似,解决方法相同。

解决办法:对parent、uncle节点染色为黑,将grandfather染色为红色,此时可能依然会出现两个连续的红色节点,则将cur节点变更为grandfather节点,继续向上更新。

在这里插入图片描述

情况4伪代码:

if (uncle && uncle->_color == RED)//uncle节点存在且为红色
{
	parent->_color = BLACK;//父节点变为黑色
	uncle->_color == BLACK;//叔叔节点变为黑色
	grandfather->_color = RED;//祖父节点变为红色
	cur = grandfather;//cur变更为当前祖父节点,继续向上更新
	parent = cur->_parent;//parent变为当前cur的父节点
}

2.5 第五种情况

cur为红,parent为红,grandfather为黑,uncle不存在或者uncle为黑。

cur为parent的右孩子。

解决办法:parent为grandfather的右孩子,cur为parent的右孩子,对grandfather进行左单旋,parent变为黑色,grandfather变为红色。
在这里插入图片描述

2.6 第六种情况

cur为红,parent为红,grandfather为黑,uncle不存在或者uncle为黑。

cur为parent的左孩子。

解决办法:parent为grandfather的右孩子,cur为parent的左孩子,先对parent进行右单旋,交换cur和parent节点,再对grandfather进行左单旋,parent变为黑色,grandfather变为红色。

在这里插入图片描述

情况5与情况6伪代码:

if (cur == parent->_left)//cur节点在左侧,先右单旋,转化为情况5
{
	RotateRight(parent);//左单旋
	swap(parent, cur);//交换parent和cur节点
}

RotateLeft(grandfather);//祖父节点右单旋
grandfather->_color = RED;//祖父颜色变为红色
parent->_color = BLACK;//父节点颜色变为黑色

3.模拟实现红黑树

#pragma once

#include <iostream>
using namespace std;

enum Color{ RED, BLACK };
template<class T>
struct RBTreeNode
{
	RBTreeNode(const T& x = T(), Color c = RED)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_data(x)
		,_color(c)
	{}
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	T _data;
	Color _color;
};

template<class T>
class RBTree
{
	typedef RBTreeNode<T> Node;
public:
	RBTree()
	{
		_head = new Node;
		_head->_left = _head;
		_head->_right = _head;
		_head->_parent = nullptr;
	}

	~RBTree()
	{
		Destroy(_head->_parent);
		delete _head;
		_head = nullptr;
	}

	bool insert(const T& data)
	{
		//先找到红黑树的根节点
		Node*& root = GetRoot();
		if (root == nullptr)//说明红黑树为空
		{
			root = new Node(data, BLACK);
			root->_parent = _head;
			_head->_left = root;
			_head->_right = root;
			_head->_parent = root;
			return true;
		}

		Node* cur = root;
		Node* parent = _head;
		while (cur)
		{
			parent = cur;
			if (data > cur->_data)
				cur = cur->_right;
			else if (data < cur->_data)
				cur = cur->_left;
			else//不插入重复的元素
				return false;
		}

		cur = new Node(data);
		cur->_parent = parent;
		if (data > parent->_data)
			parent->_right = cur;
		else
			parent->_left = cur;

		//检测节点颜色

		while (parent != _head && parent->_color == RED)
		{
			Node* grandfather = parent->_parent;//祖父

			if (parent == grandfather->_left)//在左侧
			{
				Node* uncle = grandfather->_right;//叔叔节点
				if (uncle && uncle->_color == RED)//第一种情况
				{
					parent->_color = BLACK;
					uncle->_color = BLACK;
					grandfather->_color = RED;
					cur = grandfather;
					parent = cur->_parent;
				}
				else//第2 3种情况
				{
					if (cur == parent->_right)//第三种情况,先左单旋,交换cur 和parent节点
					{
						RotateLeft(parent);
						swap(parent, cur);
					}

					RotateRight(grandfather);//再对grandfather节点进行右单旋
					parent->_color = BLACK;
					grandfather->_color = RED;//重新染色
				}
			}
			else//在右侧
			{
				Node* uncle = grandfather->_left;//叔叔节点
				if (uncle && uncle->_color == RED)//第4种情况
				{
					parent->_color = BLACK;
					uncle->_color = BLACK;
					grandfather->_color = RED;
					cur = grandfather;
					parent = cur->_parent;
				}
				else//第5 6种情况
				{
					if (cur == parent->_left)//第5种情况,先右单旋,交换cur 和parent节点
					{
						RotateRight(parent);
						swap(parent, cur);
					}

					RotateLeft(grandfather);//再对grandfather节点进行左单旋
					parent->_color = BLACK;
					grandfather->_color = RED;//重新染色
				}
			}
		}

		root->_color = BLACK;
		_head->_left = LeftMost();
		_head->_right = RightMost();
		return true;
	}

	void inOrder()
	{
		_inOrder(GetRoot());
		cout << endl;
	}

	bool isRBTree()
	{
		Node* root = _head->_parent;
		Node* cur = root;
		int BLACKCOUNT = 0;
		while (cur != nullptr)
		{
			if (cur->_color == BLACK)
				++BLACKCOUNT;
			cur = cur->_left;
		}

		return _isRBTree(root, 0, BLACKCOUNT);
	}
private:

	bool _isRBTree(Node* root, int PATHBLACKCOUNT, const int& BLACKCOUNT)
	{
		if (root == nullptr)
			return true;
		if (root->_color == BLACK)
			PATHBLACKCOUNT++;

		//性质3
		if (root->_left == nullptr && root->_right == nullptr && (PATHBLACKCOUNT != BLACKCOUNT))
		{
			cout << "违反了性质3" << endl;
			return false;
		}

		//性质2
		if (root->_parent != _head && root->_color == RED && root->_parent->_color == RED)
		{
			cout << "违反了性质2" << endl;
			return false;
		}

		return _isRBTree(root->_left, PATHBLACKCOUNT, BLACKCOUNT) && _isRBTree(root->_right, PATHBLACKCOUNT, BLACKCOUNT);
	}

	Node*& GetRoot()const
	{
		return _head->_parent;
	}

	void Destroy(Node*& root)
	{
		if (root == nullptr)
			return;
		Destroy(root->_left);
		Destroy(root->_right);
		delete root;
		root = nullptr;
	}

	Node* LeftMost()const 
	{
		Node* cur = _head->_parent;
		while (cur->_left)
			cur = cur->_left;
		return cur;
	}

	Node* RightMost()const
	{
		Node* cur = _head->_parent;
		while (cur->_right)
			cur = cur->_right;
		return cur;
	}

	void RotateRight(Node* parent)//右单旋
	{
		Node* subL = parent->_left;

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

	void RotateLeft(Node* parent)//左单旋
	{
		Node* subR = parent->_right;
		parent->_right = subR->_left;
		subR->_parent = parent->_parent;
		if (subR->_left != nullptr)
			subR->_left->_parent = parent;
		subR->_left = parent;
		if (parent->_parent != _head)
		{
			if (parent == parent->_parent->_left)
				parent->_parent->_left = subR;
			else
				parent->_parent->_right = subR;
		}
		else
		{
			parent->_parent->_parent = subR;
		}
		parent->_parent = subR;
	}

	void _inOrder(Node* root)const
	{
		if (root == nullptr)
			return;
		_inOrder(root->_left);

		cout << root->_data << "  ";

		_inOrder(root->_right);
	}
private:
	Node* _head;
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值