[Data structure] red-black tree

Red-black tree concept

A red-black tree is a binary search tree, but a storage bit is added to each node to indicate the color of the node, which can be Red or Black. By restricting the coloring of each node on any path from the root to the leaf, the red-black tree ensures that no path will be twice as long as the other path, so it is close to balance
insert image description here

Properties of red-black trees

  1. Each node is either red or black
  2. the root node is black
  3. If a node is red, its two child nodes are black (cannot have consecutive red nodes)
  4. For each node, the simple path from the node to all its descendant leaf nodes contains the same number of black nodes
  5. Each leaf node is black (leaf nodes here refer to empty nodes)

Why satisfying the above properties, the red-black tree can guarantee that the number of nodes in the longest path will not exceed twice the number of nodes in the shortest path?
insert image description here

The shortest path is all black, the longest path is red and black nodes alternating (because the red nodes cannot be continuous), and the black nodes of each path are the same, then the longest path is exactly twice the shortest path.

Best case: all black or every path is a full binary tree with one black and one red, height logN
Worst case: every subtree left subtree is all black, right subtree one black and one red. Height 2*logN.

Insertion operation of red-black tree (core)

Why the newly inserted node must be given red color?
If the new node is red, it may violate the above-mentioned property 3 of the red-black tree; if the new node is black, it will definitely violate property 4.

According to the structure of the red-black tree that will be destroyed after inserting the node, it is divided into three cases

Case 1: uncle exists and is red

Cur, parent, and grandfather are all determined colors. Uncle exists and is red.
insert image description here
Change p, u to black, and g to red, then use g as cur, and continue to adjust upwards

Understanding: if cur is red, then the parent needs to be changed to black; the parent needs to control the number of black nodes on each path to be the same, so the uncle must be turned black; if grandfather is not the root, it needs to be reversed to red to control the same number of black nodes on the path. Continue to adjust upwards

Case 2: uncle does not exist/exists and is black (on the same side)

The first type: uncle does not exist, then cur is the insertion node, and single rotation is enough.
insert image description here
The second type: uncle exists and is black, which is a change from the first case.
insert image description here

p is the left child of g, cur is the left child of p, then perform right single rotation; on the contrary,
p is the right child of g, cur is the right child of p, then perform left single rotation
p, g change color - p turns black, g turns red

Case 3: uncle does not exist/exists and is black (on both sides)

The first type: uncle does not exist, then cur is the insertion node, left and right double rotation
insert image description here
The second type: uncle exists and is black
insert image description here

p is the left child of g, cur is the right child of p, then do a left single rotation for p; on the contrary, if
p is the right child of g, cur is the left child of p, then do a right single rotation for p, then convert to case 2

Summarize

When inserting a new node, the parent node is red, see the color of the uncle.

  1. Uncle exists and is red, changes color, adjusts upwards (may become any of the three cases)

  2. Uncle does not exist or exists and is black, same side. Single rotation + color change

  3. Uncle does not exist or exists and is black, opposite side, double single rotation + color change

Simple implementation of red-black tree

#pragma once
#include<assert.h>
#include<time.h>
enum Color
{
    
    
	Red,
	Black,
};
template<class K, class V>
struct RBTreenode
{
    
    
	pair<K, V> _kv;
	RBTreenode<K, V>* _left;
	RBTreenode<K, V>* _right;
	RBTreenode<K, V>* _parent;
	Color _col;

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

template<class K, class V>
struct 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->_left;
			}
			else if(cur->_kv.first < kv.first)
			{
    
    
				parent = cur;
				cur = cur->_right;
			}
			else//存在与插入结点相同的结点key
			{
    
    
				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;
			if (parent==grandfather->_left)
			{
    
    
				Node* uncle = grandfather->_right;
				//一:uncle存在且为红
				if (uncle && uncle->_col==Red)
				{
    
    
					parent->_col = uncle->_col = Black;
					grandfather->_col = Red;

					//如果祖父节点同时是子节点,考虑双红,需要
					//继续向上调整
					cur = grandfather;
					//考虑parent是否为空
					parent = cur->_parent;
				}
				else
				{
    
    
					//情况二:cur为红,p红,g黑
					// uncle存在且为黑/不存在
					// 祖孙三代全在同一侧
					//左单选或右单旋
					if (cur==parent->_left)
					{
    
    
						RotateR(grandfather);
						parent->_col = Black;
						grandfather->_col = Red;
					}
					else
					{
    
    
						//情况三:
						// c,p为红色,g为黑
						// u不存在/存在且为黑
						//需要进行双旋操作
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = Black;
						grandfather->_col = Red;
					}
					break;
				}
			}
			else//parent==grandfather->_right
			{
    
    
				Node* uncle = grandfather->_left;
				if (uncle&&uncle->_col==Red)
				{
    
    
					parent->_col = uncle->_col = Black;
					grandfather->_col = Red;
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
    
    
					//   g                
					//      p
					//         c
					if (cur==parent->_right)
					{
    
    
						RotateL(grandfather);
						parent->_col = Black;
						grandfather->_col = Red;
					}
					else
						{
    
    //   g                
						//		   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;
		if (subRL)
			subRL->_parent = parent;

		Node* ppNode = parent->_parent;
		subR->_left = parent;
		parent->_parent = subR;


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

			subR->_parent = ppNode;
		}

	}

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

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

		Node* ppNode = parent->_parent;
		subL->_right = parent;
		parent->_parent = subL;

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

			subL->_parent = ppNode;
		}
	}
	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 Check(Node* root,int BlackNum,const int ref)
	{
    
    
		//这里用传值(不用引用)BlackNum,
		//递归返回上一层的时候BlackNum不会被改变
		//每个节点都有一个BlackNum
		if (root==nullptr)
		{
    
    
			if (BlackNum != ref)
			{
    
    
				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, ref) &&
			Check(root->_right, BlackNum, ref);
	}
	bool IsRBTree()
	{
    
    
		if (_root==nullptr)
		{
    
    
			return true;
		}
		if (_root->_col != Black)
		{
    
    
			return false;
		}
		//所有路径黑色节点数相同,以最左侧路径黑色节点数为基准
		//递归查看是否匹配
		int ref = 0;
		Node* left = _root;
		while (left)
		{
    
    
			if (left->_col==Black)
			{
    
    
				++ref;
			}
			left = left->_left;
		}
		return Check(_root,0,ref);

	}
private:
	Node* _root = nullptr;
};

Guess you like

Origin blog.csdn.net/m0_54469145/article/details/131716849