RB-tree (red-black tree) detailed explanation

RB-tree (red-black tree)

The rules of red-black tree are as follows:

1. Each node is either red or black

2. The root node is black

3. If a node is red, then its child nodes must be black

4. Any path from any node to NULL (the end of the tree) contains the same number of black nodes

In short, each path has the same number of black nodes

The newly added nodes must be red, because the cost of adding red nodes will be much smaller than that of black ones. If the newly added nodes are black, then the fourth rule will be broken, and the entire tree will have to be adjusted. The cost is quite big

Red-black tree node definition

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()
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
	{
    
    }
};

Insert operation in red-black tree

The insertion operation of the red-black tree is divided into several situations

Case 1:

When cur is red, parent is red, grandparent is black, uncle exists and is red

Operation process:

Dye the parent and uncle into black, the grandparent into red, and then give cur to the grandparent to continue to judge upwards. If the parent of cur is still red, then continue to dye. If cur is _root, dye the root node black .

Concrete map:

Abstract graph:

In the abstract graph, the dyeing mechanism is triggered in the a or b subtree, and then it is dyed all the way up until cur is also dyed red. Cur and p cause conflicts and need to continue dyeing.

Case two:

When cur is red, p is red, g is black, u does not exist/u is black

operate:

p is the left child of g, and cur is the left child of p, then perform a right single rotation;

On the contrary, if p is the right child of g, and cur is the right child of p, perform left single rotation

Finally p becomes black, cur and g become red

Concrete map:

When uncle is empty

Abstract graph:

Case three:

When cur and p are red, g is black, u is black/absent

If parent is the left son of grandparent, turn left to parent, then turn right to grandparent

If the parent is the right son of the grandparent, right-hand the parent, then left-hand the grandparent

cur is dyed black, and cur and grandparent are dyed red.

Concrete map:

Abstract graph:

Red-black tree + test code

#pragma once
#include"AVLTree.h"

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)
	{
    
    }
};


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)
		{
    
    
			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;
		}
		else
		{
    
    
			parent->_left = cur;
		}
		cur->_parent = parent;

		//检查是否满足红黑树的要求
		while (parent && parent->_col == Red)
		{
    
    
			//如果父亲节点的颜色为红色,那么就有可能需要改变颜色或者旋转
			Node* grandparent = parent->_parent;
			assert(grandparent);
			assert(grandparent->_col == Black);//父亲为红色,那么祖父一定为黑色,不然之前的插入就存在问题
			
			if (parent == grandparent->_left)
			{
    
    
				Node* uncle = grandparent->_right;
				//情况一,uncle存在并且uncle为红色,就要继续往上染色
				if (uncle && uncle->_col == Red)
				{
    
    
					parent->_col = uncle->_col = Black;
					grandparent->_col = Red;
					cur = grandparent;
					parent = cur->_parent;
				}
				else
				{
    
    
					//uncle不存在或者uncle为黑色就会走到这里
					//情况二+情况三
					if (cur == parent->_left)
					{
    
    
						// 情况二:右单旋+变色
						//     g 
						//   p   u
						// c
						//右旋+染色
						
						RotateR(grandparent);
						parent->_col = Black;
						grandparent->_col = Red;
					}
					else
					{
    
    
						// 情况三:左右单旋+变色
						//     g 
						//   p   u
						//     c
						RotateL(parent);
						RotateR(grandparent);
						cur->_col = Black;
						parent->_col = grandparent->_col = Red;
					}
					break;
				}
			}
			else
			{
    
    
				Node* uncle = grandparent->_left;
				if (uncle && uncle->_col == Red)
				{
    
    
					parent->_col = uncle->_col = Black;
					grandparent->_col = Red;
					cur = grandparent;
					parent = cur->_parent;
				}
				else
				{
    
    
					if (cur == parent->_right)
					{
    
    
						RotateL(grandparent);
						parent->_col = Black;
						grandparent->_col = Red;
					}
					else
					{
    
    
						RotateR(parent);
						RotateL(grandparent);
						cur->_col = Black;
						grandparent->_col = Red;
					}
					break;
				}
			}
		}
		_root->_col = Black;
		return true;
	}

	void InOrder()
	{
    
    
		_InOrder(_root);
		cout << endl;
	}

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

		if (_root->_col == Red)
		{
    
    
			cout << "根节点不是黑色" << endl;
			return false;
		}

		// 黑色节点数量基准值
		int benchmark = 0;
		Node* cur = _root;

		while (cur)
		{
    
    
		if (cur->_col == Black)
		++benchmark;

		cur = cur->_left;
		}

		return PrevCheck(_root, 0, benchmark);
	}

private:

	bool PrevCheck(Node* root, int blackNum, int& benchmark)
	{
    
    
		if (root == nullptr)
		{
    
    
			if (blackNum != benchmark)
			{
    
    
				cout << "某条黑色节点的数量不相等" << endl;
				return false;
			}
			else
			{
    
    
				return true;
			}
		}
		if (root->_col == Black)
		{
    
    
			++blackNum;
		}

		if (root->_col == Red && root->_parent->_col == Red)
		{
    
    
			cout << "存在连续的红色节点" << endl;
			return false;
		}
		return PrevCheck(root->_left, blackNum, benchmark)
			&& PrevCheck(root->_right, blackNum, benchmark);
	}


	void _InOrder(Node* root)
	{
    
    
		if (root == nullptr)
		{
    
    
			return;
		}

		_InOrder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_InOrder(root->_right);
	}

	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;
			}
			else
			{
    
    
				pparent->_right = subR;;
			}
			subR->_parent = pparent;
		}
	}

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

		parent->_left = subLR;

		Node* pparent = parent->_parent;

		//防止空指针
		if (subLR)
		{
    
    
			subLR->_parent = parent;
		}

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

		if (parent == _root)
		{
    
    
			//如果parent就是根节点
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
    
    
			//如果parent只是一颗子树的根节点,就还需要连接好parent
			//判断是左还是右节点
			if (pparent->_left == parent)
			{
    
    
				pparent->_left = subL;
			}
			else
			{
    
    
				pparent->_right = subL;
			}
			subL->_parent = pparent;
		}
	}

	Node* _root = nullptr;
};

void TestRBTree2()
{
    
    
	size_t N = 1000;
	srand(time(0));
	RBTree<int, int> t1;
	for (size_t i = 0; i < N; ++i)
	{
    
    
		int x = rand();
		cout << "Insert:" << x << ":" << i << endl;
		t1.Insert(make_pair(x, i));
	}
	cout << "IsBalance:" << t1.IsBalance() << endl;
}

operation result:

Guess you like

Origin blog.csdn.net/AkieMo/article/details/131905842