红黑树详解

本文详细介绍了红黑树的概念,包括其性质和实现过程,重点讨论了插入操作中的颜色调整策略,以及如何通过检验确保红黑树的正确性。
摘要由CSDN通过智能技术生成

目录

1.红黑树的概念;

2.红黑树的实现;

3.最后统一代码;


1.红黑树的概念;

        红黑树是一个搜索二叉树,给结点进行添加颜色(红色和黑色);对给条路径的结点进行颜色的限制,达到每条路径不会超过其他路径的两倍。红黑树是一种相对平衡的搜索二叉树。


1.1红黑树的性质:

(1)结点颜色不是红就是黑;

(2)根结点一定是黑色;

(3)如果一个结点为红色那么他的孩子结点一定为黑色(一黑一红)

(4)每条路径的黑色结点的数量都是一样多

(5)每个结点的叶子结点都是黑色的(空结点也看做黑色).

小tips:
        为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点
个数的两倍?        

        因为最短路径是全黑结点(根结点一定是黑色的),最长路径是一黑一红的;那么就会保证最长路径的结点个数不会超过最短路径结点个数的两倍。


2.红黑树的实现;

2.1结点初始化:

小tips:
        在节点的定义中,为什么要将节点的默认颜色给成红色的?

默认给红色结点是避免给黑色结点造成每条路径的黑色结点数量不一致。

enum Colour
{
	RED,
	BLACK
};


template<class K, class V>
struct RBTreeNode
{
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;

	pair<K, V> _kv;//数据
	Colour _col;

	RBTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _col(RED)
	{}
};

2.2红黑树的插入:

        首先处理根空结点为空的情况, 需要new一个新结点来存放根节点,并且将这个结点的颜色设置为黑色;

        再其次就是将root结点的parent置空. 那么我们插入数据需要找到插入的位置.

        那么也需要处理位置.因为数据只插入一次重复的也就不需要插入了.

        而且我们插入的结点一定是红色的,黑色结点可能会破坏每条路径的黑色结点个数相同的规定.

if (_root == nullptr)//结点为空
		{
			_root = new Node(kv);
			_root->_col = BLACK;
			return true;
		}
		Node* parent = nullptr;
		Node* cur = _root;

		while (cur)//找插入的位置
		{
			if (cur->_kv.first < kv.first)//右边
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)//左边
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}

        cur = new Node(kv);
		cur->_col = RED;

		if (parent->_kv.first < kv.first)//右边
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		else//左边
		{
			parent->_left = cur;
			cur->_parent = parent;
		}

    插入都是红结点,插入结点的双亲结点是黑色那么不需要修改; 如果是双亲结点都是红色就是需要修改,因为不符合红色结点不能连续.

深究:

        其实本质还是看u结点的颜色和是否存在,因为只有u不能确定,c,p,g确定了那么一定可以确定相应的结点的颜色.

情况一:  cur为红色; p为红色, g为黑色, u存在并且为红色.

小tips: cur和p均为红,违反了性质三,此处能否将p直接改为黑?

        绝对不可以,直接改掉p那么你可以又可能违反每条路径黑色结点的数量一样多.你还需要去关注其他点是否满足这个条件非常麻烦.

修改方法: 将p, u结点都修改成黑色结点, g修改成红色结点.

 红黑树简化可以这样看,可能p在g的左子树或者右子树,那么就有下面的两种情况.

 //    g                    //    g
 //  p   u                 // u     p  
 //c                        //           c

        while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			//    g
			//  p   u
			//c           
			if (parent == grandfather->_left)//父亲结点在祖父结点的左边;
			{
				Node* uncle = grandfather->_right;
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					cur = grandfather;
					parent = cur->_parent;
				}
            }
            else//父亲结点在祖父结点的右边;
			{
				//    g
				//  u   p
				//        c
				Node* uncle = grandfather->_left;
				if (uncle  && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					//继续去上面结点处理
					cur = grandfather;
                    parent = cur->_parent;
				}
             }

情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑

画图分析一下:分u不存在和u存在两种情况看看罗;

 修改方法:   旋转+变色

看看下面处理的图:

(1) u不存在的话, 可以分析出来cur一定是新增结点.因为红色结点是不能连续的.

让p作根结点,然后将p变黑, g变红;

(2) u存在的话,cur一定不是新增结点, 新增结点一定在a/b结点上面.因为路径的黑色结点数目不一样.

将g和p进行旋转,然后将p改成黑,g变成红色.

然后我们再对cur结点的位置进行判断;有两种情况; 旋转的方法不同.前面是进行右单旋即可,后面是先左旋p再左旋g. 旋转完一定记得break.

 //       g                     //         g
 //    p                        //     p
 // c                           //         c

                    if (cur == parent->_left)
					{    //    g
						//   p
						// c

						RotateR(grandfather);//直接右单旋
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//     g
						//   p
						//     c
						RotateL(parent);//先左单旋parent
						RotateL(grandfather);//在旋转grandfather
						cur->_col = BLACK;
						grandfather->_col =  RED;
					}
					break;

情况三:cur为红,p为红,g为黑,u不存在/u存在且为黑

修改方法:先进行旋转,最后变色

p为g的左孩子, c为p的右孩子; p左单选, c左旋转,最后将c变黑色,g变红色.

 p为g的左孩子, c为p的左孩子; p左单选, c左旋转,最后将c变黑色,g变红色.

                else//u结点是空或者黑色结点.
				{
					//   g
				   //  u   p
				   //        c
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//    g
						//  u   p
						//    c
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}

左旋右旋代码这里就偷个懒详细可以去AVL树那篇博客看看.

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

		parent->_right = subRL;
		subR->_left = parent;

		Node* parentParent = parent->_parent;

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

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

			subR->_parent = parentParent;
		}
	}


	void RotateR(Node* parent)//对于第二种情况下左单旋和右单旋;
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

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

		Node* parentParent = parent->_parent;

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

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

			subL->_parent = parentParent;
		}
	}

2.3红黑树的检验:

检验红黑树首先就要确定是否为搜索二叉树,这里看升序, 那么就中序遍历即可.

    void Inorder()
	{
		_Inorder(_root);
		cout << endl;
	}

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

其次就是要检查红黑树的性质是否都符合:

(1)判断根节点是否为黑色的;

(2)检查红黑树是否是一黑一红;

(3)检查红黑树的每条路径的黑色结点是否相同.

(4)叶子结点都是黑色的.

	bool check(Node* root, int blacknum, const int refVal)
	{
		if (root == nullptr)
		{
			if (blacknum != refVal)
			{
				cout << "存在黑色结点不相等的路径" << endl;
				return false;
			}
			return true;
		}
			

		//判断一黑一红
		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout << "有连续的红色结点" << endl;
			return false;
		}

		if (root->_col == BLACK)
		{
			blacknum++;
		}

		return check(root->_left, blacknum, refVal) && check(root->_right, blacknum, refVal);
	}


    bool IsBalance()
	{
		if (_root == nullptr)
			return true;

		//检查根结点
		if (_root->_col == RED)
			return false;
		//参考值,用来看blacknum的.
		int refVal = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
			{
				refVal++;
			}
			cur = cur->_left;
		}

		//检查一黑一红//检查黑色结点数量
		int blacknum = 0;
		return check(_root, 0, refVal);
	}

3.最后统一代码;

#pragma once
#include<iostream>
using namespace std;
enum Colour
{
	RED,
	BLACK
};


template<class K, class V>
struct RBTreeNode
{
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;

	pair<K, V> _kv;//数据
	Colour _col;

	RBTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _col(RED)
	{}
};

template<class K, class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;

public:
	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)//固定cur的位置
		{
			if (cur->_kv.first < kv.first)//右边
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)//左边
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}

		cur = new Node(kv);
		cur->_col = RED;

		if (parent->_kv.first < kv.first)//右边
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		else//左边
		{
			parent->_left = cur;
			cur->_parent = parent;
		}

		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			//    g
			//  p   u
			//c           
			if (parent == grandfather->_left)//父亲结点在祖父结点的左边;
			{
				Node* uncle = grandfather->_right;
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_left)
					{    //    g
						//   p
						// c

						RotateR(grandfather);//直接右单旋
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//     g
						//   p
						//     c
						RotateL(parent);//先左单旋parent
						RotateL(grandfather);//在旋转grandfather
						cur->_col = BLACK;
						grandfather->_col =  RED;
					}
					break;
				}
			}
			else//父亲结点在祖父结点的右边;
			{
				//    g
				//  u   p
				//        c
				Node* uncle = grandfather->_left;
				if (uncle  && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					//继续去上面结点处理
					cur = grandfather;
                    parent = cur->_parent;
				}
				else//u结点是空或者黑色结点.
				{
					//   g
				   //  u   p
				   //        c
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//    g
						//  u   p
						//    c
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
		}


		_root->_col = BLACK;//根节点处理为黑.
		return true;
	}

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

		parent->_right = subRL;
		subR->_left = parent;

		Node* parentParent = parent->_parent;

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

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

			subR->_parent = parentParent;
		}
	}


	void RotateR(Node* parent)//对于第二种情况下左单旋和右单旋;
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

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

		Node* parentParent = parent->_parent;

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

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

			subL->_parent = parentParent;
		}
	}

	void Inorder()
	{
		_Inorder(_root);
		cout << endl;
	}

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

	bool check(Node* root, int blacknum, const int refVal)
	{
		if (root == nullptr)
		{
			if (blacknum != refVal)
			{
				cout << "存在黑色结点不相等的路径" << endl;
				return false;
			}
			return true;
		}
			

		//判断一黑一红
		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout << "有连续的红色结点" << endl;
			return false;
		}

		if (root->_col == BLACK)
		{
			blacknum++;
		}

		return check(root->_left, blacknum, refVal) && check(root->_right, blacknum, refVal);
	}

	bool IsBalance()
	{
		if (_root == nullptr)
			return true;

		//检查根结点
		if (_root->_col == RED)
			return false;
		//参考值
		int refVal = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
			{
				refVal++;
			}
			cur = cur->_left;
		}

		//检查一黑一红//检查黑色结点数量
		int blacknum = 0;
		return check(_root, 0, refVal);
	}
private:
	Node* _root = nullptr;
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值