【数据结构】红黑树的实现

本篇博文主旨是介绍红黑树的概念及其性质,并用C++代码进行实现;红黑树的重难点是剖析插入、删除节点的旋转情况;最后再进行了红黑树和AVL树的对比,说明为什么红黑树优于AVL树

红黑树的概念及其性质

红黑树是一颗搜索二叉树,但不同之处是每个节点有颜色的标记;

除此之外,还有下列特点:

(1)每个节点的颜色为黑色或者红色,并且根节点为黑色

(2)从根节点到每个叶子节点的路径上,黑色节点的数量相同

(3)父亲节点和子节点不能同时为红色,也就是说,没有连续的红节点

(4)从根节点到叶子节点,最长路径不超过最短路径的两倍

红黑树节点的定义

//定义枚举类型变量
//表示节点颜色
enum Color
{
	RED,
	BLACK,
};

//红黑树节点的定义
template<typename K,typename V>
struct RBTreeNode
{
	//K/V 结构,可以存储一个pair
	K _key;
	V _value;

	//颜色
	Color _col;

	//节点我们采用三叉链,方便父亲的寻找
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;

	//构造函数
	RBTreeNode(const K& key,const V& value)
		: _key(key)
		, _value(value)
		, _left(NULL)
		, _right(NULL)
		, _parent(NULL)
		, _col(RED)
	{}
};

红黑树的插入以及旋转情况

这里,我们先定义一下几个节点的名称

parent—插入节点的父亲节点

cur — 当前插入的节点

grandfather — parent的父亲节点

uncle — 叔叔节点,grandfather除parent的另一个子节点

分以下情况讨论

情况1、叔叔存在且为红

当出现如图所示的这种情况时,parent和uncle,cur都为红;

这种情况只需要将parent,uncle的颜色换成黑色,grandfather节点设置为红色的

由于grandfather被设置成红色,那么需要向上调整,因为如果grandfather的父亲如果是红的,修改后就又出现了两个红色节点连续的情况了

《【数据结构】红黑树的实现》

情况2、叔叔不存在/叔叔存在,但是为黑色

这里又分两种情况:

a.插入的节点是parent的左孩子,那么直接对grandfather进行右旋,结束调整,因为最上面的节点为黑,不会导致出现两个连续红节点的情况

《【数据结构】红黑树的实现》

b.插入的节点是parent的右孩子,先需要对parent进行左旋,转化成a的情况,然后再对grandfather进行右旋,结束调整,因为最上面的节点为黑,不会导致出现两个连续红节点的情况

《【数据结构】红黑树的实现》

说明:上面列举的是parent为grandfather的左孩子的情况,反之,当parent是grandfather的右孩子时,也按照上面的方法

左旋和右旋的代码

//和AVL树的旋转逻辑相同,区别仅是不用修改平衡因子
	//右旋
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		Node* ppNode = parent->_parent;

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

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

	//左旋
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		Node* ppNode = parent->_parent;

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

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

		if (ppNode)
		{
			if (ppNode->_left == parent)
				ppNode->_left = subR;
			else
				ppNode->_right = subR;

			subR->_parent = ppNode;
		}
		else
		{
			_root = subR;
			subR->_parent = NULL;
		}
	}

Insert函数的代码实现

	pair<Node*,bool> Insert(const K& key, const V& value)
	{
		//如果根节点为空,直接插入,并且将节点颜色设置为黑
		if (_root == NULL)
		{
			_root = new Node(key, value);
			_root->_col = BLACK;
			return make_pair(_root,true);
		}

		//用cur和parent 判断插入的Key知否存在
		//若不存在,用parent来表示要插入的节点的父亲
		Node* cur = _root;
		Node* parent = NULL;

		//进行循环查找
		while (cur)
		{
			if (key > cur->_key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (key < cur->_key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				//不存在,返回查找到元素的指针,以及插入失败的FALSE
				return make_pair(cur, false);
			}
		}

		//进行插入
		cur = new Node(key, value);
		Node* newNode = cur;

		//判断插入的是parent的左孩子还是右孩子
		if (key < parent->_key)
		{
			parent->_left = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_right = cur;
			cur->_parent = parent;
		}

		//进行调整
		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
				Node* uncle = grandfather->_right;
				//1.叔叔存在且为红,变色
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					cur = grandfather;
					parent = cur->_parent;
				}
				else //叔叔不存在/叔叔存在且为黑
				{
					//2.进行单旋
					if (cur == parent->_left)
					{
						RotateR(grandfather);

						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else //3.进行双旋
					{
						RotateL(parent);
						RotateR(grandfather);

						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
			else
			{
				Node* uncle = grandfather->_left;
				//1.叔叔存在且为红,变色
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
					cur = grandfather;
					parent = cur->_parent;
				}
				else //叔叔不存在/叔叔存在且为黑
				{
					//2.进行单旋
					if (cur == parent->_right)
					{
						RotateL(grandfather);

						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else //3.进行双旋
					{
						RotateR(parent);
						RotateL(grandfather);

						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
		}

		_root->_col = BLACK;
		return make_pair(newNode, true);
	}

	bool IsBalance()
	{
		//1.空树,属于红黑树
		if (_root == NULL)
			return true; 

		//2.根节点不为黑
		if (_root->_col != BLACK)
			return false;

		//3.统计出一条路径上黑色节点的数量
		size_t k = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
				k++;

			cur = cur->_left;
		}

		//进行判别条件2和3的判断
		return CheckColor(_root) && CheckBlackNum(_root, k, 0);
	}

如何判断一棵树是否为红黑树

我们可以从下面三个方面判别,需要注意的是,空树也是红黑树

判别1:根节点是否为黑色

判别2:每条路径的黑色节点数量是否相同

判别3:每一个红节点,其父亲节点是否为黑节点

判别1:

	bool IsBalance()
	{
		//1.空树,属于红黑树
		if (_root == NULL)
			return true; 

		//2.根节点不为黑
		if (_root->_col != BLACK)
			return false;

		//3.统计出一条路径上黑色节点的数量
		size_t k = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
				k++;

			cur = cur->_left;
		}

		//进行判别条件2和3的判断
		return CheckColor(_root) && CheckBlackNum(_root, k, 0);
	}

判别2:

	bool CheckColor(Node* root)
	{
		//1.空树,满足
		if (root == NULL)
			return true;

		//2.判断父亲,是否是连续的红色
		if (root->_col == RED && root->_parent->_col == RED)
			return false;

		return CheckColor(root->_left) && CheckColor(root->_right);
	}

判别3:

	bool CheckBlackNum(Node* root, const size_t &k, size_t num)
	{
		//1.空树,满足
		if (root == NULL)
			return true;

		if (root->_col == BLACK)
			++num;

		//2.叶子节点,判断黑色节点数量
		if (root->_left == NULL && root->_right == NULL && num != k)
			return false;

		return CheckBlackNum(root->_left, k, num) 
			&& CheckBlackNum(root->_right, k, num);
	}

红黑树和AVL树的比较

1、红黑树相对AVL树来说,它不追求完全平衡,在他们的增删查改的时间复杂度都为O(lg(N))的情况下,降低了插入节点后的旋转次数;

2、红黑树没有AVL树的平衡因子的判断,实现起来简单了许多

3、而AVL树由于本身追求完全平衡的苛刻性,旋转次数多,旋转因子的判断较为麻烦

红黑树代码实现

下面是实现红黑树的github链接,可以关注一下,一起学习~
https://github.com/haohaosong/DataStruct/blob/master/RBTree.h

    原文作者:算法小白
    原文地址: https://blog.csdn.net/qq_31828515/article/details/56496533
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞