[Data structure] Insertion and verification of red-black tree

1. Basic concepts

1. Background of the times

  • In 1972, Rudolf Bayer invented a data structure, which is a special 4th order case of B-tree. These trees maintain all paths from root to leaves with the same number of nodes, creating a perfectly balanced tree. However, they are not binary search trees. Bayer called them "symmetric binary B-trees" in his paper. This is the origin of the red-black tree.
    Insert image description here

  • In a 1978 paper, "A dichromatic framework for balanced trees," Leonidas J. Guibas and Robert Sedgewick derived from symmetric binary B-trees Red-black trees are derived from . "Red" was chosen because it was the best-looking color produced by the color laser printer available to the author while working at Xerox PARC. Another response from Gibas was that it was because they could use red and black pens to draw trees.
    Insert image description here
    Insert image description here

The first is Leonidas Gibas, the second is Robert Sedgwick.

  • In 1993, Arne Andersson introduced the idea of ​​right-skewed trees to simplify insertion and deletion operations.
    Insert image description here
  • In 1999, Chris Okasaki showed how to make insertion operations purely functional. Its balancing function only needs to handle 4 unbalanced situations and a default balanced situation.
    Insert image description here

For details, please see: Wikipedia

2. Basic concepts

A red-black tree is a binary search tree, but a storage bit is added to each node to represent the color of the node, which can be Red or
Black. By limiting the coloring of each node on any path from the root to the leaf, , 红黑树确保没有一条路 径会比其他路径长出俩倍it is close to balance.

  • The key to achieving balance: 最长路径小于等于最短路径的两倍.

3.Basic properties

  1. Each node is either red or black
  2. 根节点是黑色的
  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的(如果一个结点是黑色的,则其两个孩子可以是红的也可以是黑的。)
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点
  5. Each leaf node is black (the leaf node here refers to the empty node)

Emphasis: Points 2, 3, and 4 are related and are the most critical 3 points.

  • Assume that if the root node is red, then the inserted node can only be black (3), which violates (4).
  • For 3 analysis, the child node is empty, but the empty node is also understood as black (5), so (5) is used to assist (3). However, this single analysis cannot tell what color node is inserted each time, so it must be analyzed in conjunction with (4).
  • For the 4 analysis, two conclusions can be drawn -
    1. The inserted node must be red. If black is inserted, the black node of each path will inevitably change.
    2. The longest path is less than twice the shortest path (concept). This point can be seen as an interval problem, that is, there are n intervals between n numbers (not counting the first number), that is, n black nodes (not counting the root node), and there are at most n red nodes between them. .

2. Implementation principle

1. Insert

1.1 Discoloration

Basic logic: The black nodes based on each path remain unchanged.

The first way to change color:
Insert image description here
Does this mean that the number of black nodes on each path has not changed?

So what is the prerequisite for such a change?

  • The left and right children of the black node are red and not empty.

So when does discoloration occur?

  • Based on property 3, 红色结点的两个孩子必须为黑, but from 4 we can deduce that every time the inserted node must be red, then 按照4的原则进行处理,使处理结果符合3即可what we do at this time is to proceed 变色.

Insert image description here
At this time, a new node is inserted to the right of the parent, and the parent is to the left of the grandfather.

Insert image description here

At this time, the insertion is performed to the right of the parent, and the parent is the left node of the grandfather.

  • Summarize
  1. The premise of changing color is that the black nodes of each path remain unchanged.
  2. uncle is non-empty and red, and parent is red (condition). Change grandfather to red, parent and uncle to black (operation).

Continuing the analysis, if the grandfather is red, is it possible that its parent node is red?

  • The answer is possible.

So we need to continue updating:

  1. Update cur to grandfather
  2. parent is the parent of cur

Next, analyze, what if grandfather is the root node?

  • Due to property 2, we need to change the color of the root node to black again.

1.2 Rotation + color change

Earlier we analyzed a simple situation that only needs to change color. Next, we will analyze another situation.

The second type of discoloration needs to be classified and discussed on the basis of rotation. There are four specific situations.

①Left-handed

Insert image description here

Supplement: When uncle is a black node, the left subtree of parent is not empty and the root node is black. The same is true for the left and right subtrees of cur. I will not analyze too much here, just for understanding, because it is easy to analyze too much in specific situations. Increase the difficulty of understanding.

  • At the beginning, the parent is on the right side of the grandfather, and cur is on the right side of the parent.

②Right rotation

Insert image description here
The supplement to uncle is the same as levorotation

  • At the beginning, parent is to the left of grandfather, and cur is to the left of parent.

③Right/left double rotation

Insert image description here
The supplement to uncle is the same as levorotation

  • At the beginning, parent is on the right side of grandfather, and cur is on the left side of parent.

④ Double left and right rotation

Insert image description here
The supplement to uncle is the same as levorotation

  • At the beginning, parent is to the left of grandfather, and cur is to the right of parent.

  • Summary
    According to the position of the parent, we can roughly divide it into two situations:

  1. parent is on the left side of grandfather
    Insert image description here

  2. Parent is on the right side of grand.
    Insert image description here
     It is not difficult to see that both AVL and red-black trees will be rotated, but the AVL tree will process the balance factor after rotation, and the red-black tree will process discoloration after rotation. In the final analysis, it is all about balancing the tree.

  • core code

//判断是否要进行旋转变色
//当父节点为红色说明要进行判断
while (parent && parent->_col == RED)
{
    
    
	//爷爷结点
	Node* grandfather = parent->_parent;
	if (grandfather->_left == parent)
	{
    
    
		Node* uncle = grandfather->_right;
		if (uncle && uncle->_col == RED)//如果uncle存在且为红色
		{
    
    
			//变色
			parent->_col = uncle->_col = BLACK;
			grandfather->_col = RED;

			//继续往上迭代进行分析
			cur = grandfather;
			parent = cur->_parent;
		}
		else//如果uncle不存在或者为黑色
		{
    
    

			//旋转
			if (parent->_left == cur)
			{
    
    
				RotateR(grandfather);
				parent->_col = BLACK;

				cur->_col = grandfather->_col = RED;
			}
			else
			{
    
    
				RotateL(parent);
				RotateR(grandfather);

				cur->_col = BLACK;

				parent->_col = grandfather->_col = RED;
			}

			break;
		}
	}
	else//grandfather->_right == parent
	{
    
    
		Node* uncle = grandfather->_left;

		if (uncle && uncle->_col == RED)
		{
    
    
			//变色
			grandfather->_col = RED;
			parent->_col = uncle->_col = BLACK;

			//往上更新
			cur = grandfather;
			parent = cur->_parent;
		}
		else
		{
    
    
			if (parent->_right == cur)
			{
    
    
				//旋转
				RotateL(grandfather);

				//变色
				parent->_col = BLACK;
				grandfather->_col = cur->_col = RED;
			}
			else
			{
    
    
				RotateR(parent);
				RotateL(grandfather);

				cur->_col = BLACK;
				grandfather->_col = parent->_col = RED;
			}
			break;
		}
	}
}
  • Insert code
bool insert(const pair<Key, Val>& val)
{
    
    
	//第一步:插入操作
	//如果根节点为空
	if (_root == nullptr)
	{
    
    
		_root = new Node(val);
		_root->_col = BLACK;
		return true;
	}
	else
	{
    
    
		Node* cur = _root, * parent = _root;
		while (cur)
		{
    
    
			if (cur->_key > val.first)
			{
    
    
				parent = cur;
				cur = cur->_left;

			}
			else if (cur->_key < val.first)
			{
    
    
				parent = cur;
				cur = cur->_right;

			}
			else
			{
    
    
				return false;
			}
		}
		cur = new Node(val);
		if (parent->_key > val.first)
		{
    
    
			parent->_left = cur;
		}
		else
		{
    
    
			parent->_right = cur;
		}
		//更新新增结点的_parent
		cur->_parent = parent;

		//判断是否要进行旋转变色
		//当父节点为红色说明要进行判断
		while (parent && parent->_col == RED)
		{
    
    
			//爷爷结点
			Node* grandfather = parent->_parent;
			if (grandfather->_left == parent)
			{
    
    
				Node* uncle = grandfather->_right;
				if (uncle && uncle->_col == RED)
				//如果uncle存在且为红色
				{
    
    
					//变色
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					//继续往上迭代进行分析
					cur = grandfather;
					parent = cur->_parent;
				}
				else//如果uncle不存在或者为黑色
				{
    
    

					//旋转
					if (parent->_left == cur)
					{
    
    
						RotateR(grandfather);
						parent->_col = BLACK;

						cur->_col = grandfather->_col = RED;
					}
					else
					{
    
    
						RotateL(parent);
						RotateR(grandfather);

						cur->_col = BLACK;

						parent->_col = grandfather->_col = RED;
					}

					break;
				}
			}
			else//grandfather->_right == parent
			{
    
    
				Node* uncle = grandfather->_left;

				if (uncle && uncle->_col == RED)
				{
    
    
					//变色
					grandfather->_col = RED;
					parent->_col = uncle->_col = BLACK;

					//往上更新
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
    
    
					if (parent->_right == cur)
					{
    
    
						//旋转
						RotateL(grandfather);

						//变色
						parent->_col = BLACK;
						grandfather->_col = cur->_col = RED;
					}
					else
					{
    
    
						RotateR(parent);
						RotateL(grandfather);

						cur->_col = BLACK;
						grandfather->_col = parent->_col = RED;
					}
					break;
				}
			}
		}
		//根节点可能为红色,不管红色还是黑色都弄成黑色
		_root->_col = BLACK;
		return true;
	}
}

2.Verification

  • principle
  1. The root node cannot be red
  2. Each path has the same number of black nodes
  3. There cannot be consecutive red nodes on each path.
  • code
bool _IsRBTree(Node* root)
{
    
    
	if (root == nullptr)
		return true;
	//根节点是黑色的
	if (root->_col == RED)
		return false;
	//各个路径的黑色结点数是相同的,因此设立一个基准进行比较合适,
	//再对树进行遍历求每个路径的黑色结点的数量,最后比较即可。
	int benchmark = 0;
	Node* cur = root;
	while (cur)
	{
    
    
		if (cur->_col == BLACK)
			benchmark++;
		cur = cur->_left;
	}
	return Check(root);
}
bool Check(Node* root, int BCount,int benchmark)
{
    
    
	if (root == nullptr)
	{
    
    
		//验证基准值是否等于黑色结点数
		//只要有一个不是,即不是红黑树。
		if (BCount != benchmark)
			return false;

		return true;
	}

	//求每条黑色结点的个数
	if (root->_col == BLACK)
		BCount++;

	//验证性质3,即不能有连续的红色结点。
	if (root->_col == RED && root->_parent 
	&& root->_parent->_col == RED)
	{
    
    
		return false;
	}
	return Check(root->_left,BCount,benchmark) 
		&& Check(root->_right, BCount, benchmark);
}

Source code

#pragma once
#include<iostream>
using namespace std;
namespace MY_STL
{
    
    
	enum Color
	{
    
    
		RED = 0,
		BLACK = 1
	};
	template<class Key,class Val>
	struct RBTreeNode
	{
    
    
		typedef RBTreeNode<Key, Val> Node;
		RBTreeNode(const pair<Key,Val>& key)
			:_key(key.first)
			,_val(key.second)
			,_right(nullptr)
			,_left(nullptr)
			,_parent(nullptr)
			,_col(RED)
		{
    
    }

		Node* _right;
		Node* _left;
		Node* _parent;

		Key _key;
		Val _val;
		Color _col;
	};

	template<class Key,class Val>
	class RBTree
	{
    
    
		typedef RBTreeNode<Key, Val> Node;

	public:
		bool insert(const pair<Key, Val>& val)
		{
    
    
			//第一步:插入操作
			//如果根节点为空
			if (_root == nullptr)
			{
    
    
				_root = new Node(val);
				_root->_col = BLACK;
				return true;
			}
			else
			{
    
    
				Node* cur = _root, * parent = _root;
				while (cur)
				{
    
    
					if (cur->_key > val.first)
					{
    
    
						parent = cur;
						cur = cur->_left;

					}
					else if (cur->_key < val.first)
					{
    
    
						parent = cur;
						cur = cur->_right;

					}
					else
					{
    
    
						return false;
					}
				}
				cur = new Node(val);
				if (parent->_key > val.first)
				{
    
    
					parent->_left = cur;
				}
				else
				{
    
    
					parent->_right = cur;
				}
				//更新新增结点的_parent
				cur->_parent = parent;

				//判断是否要进行旋转变色
				//当父节点为红色说明要进行判断
				while (parent && parent->_col == RED)
				{
    
    
					//爷爷结点
					Node* grandfather = parent->_parent;
					if (grandfather->_left == parent)
					{
    
    
						Node* uncle = grandfather->_right;
						if (uncle && uncle->_col == RED)
						//如果uncle存在且为红色
						{
    
    
							//变色
							parent->_col = uncle->_col = BLACK;
							grandfather->_col = RED;

							//继续往上迭代进行分析
							cur = grandfather;
							parent = cur->_parent;
						}
						else//如果uncle不存在或者为黑色
						{
    
    

							//旋转
							if (parent->_left == cur)
							{
    
    
								RotateR(grandfather);
								RotateCount++;

								parent->_col = BLACK;
								cur->_col = grandfather->_col = RED;
							}
							else
							{
    
    
								RotateL(parent);
								RotateR(grandfather);
								RotateCount+=2;

								cur->_col = BLACK;

								parent->_col = grandfather->_col
								 = RED;
							}

							break;
						}
					}
					else//grandfather->_right == parent
					{
    
    
						Node* uncle = grandfather->_left;

						if (uncle && uncle->_col == RED)
						{
    
    
							//变色
							grandfather->_col = RED;
							parent->_col = uncle->_col = BLACK;

							//往上更新
							cur = grandfather;
							parent = cur->_parent;
						}
						else
						{
    
    
							if (parent->_right == cur)
							{
    
    
								//旋转
								RotateL(grandfather);
								RotateCount++;

								//变色
								parent->_col = BLACK;
								grandfather->_col = cur->_col = RED;
							}
							else
							{
    
    
								RotateR(parent);
								RotateL(grandfather);
								RotateCount += 2;

								cur->_col = BLACK;
								grandfather->_col = parent->_col 
								= RED;
							}
							break;
						}
					}
				}
				//根节点可能为红色,不管红色还是黑色都弄成黑色
				_root->_col = BLACK;
				return true;
			}
		}
		bool IsRBTree()
		{
    
    
			return _IsRBTree(_root);
		}
		size_t Height()
		{
    
    
			return Height(_root);
		}
	private:
		size_t Height(Node* root)
		{
    
    
			if (root == nullptr)
			{
    
    
				return 0;
			}

			int LHeight = Height(root->_left);
			int RHeight = Height(root->_right);

			return max(LHeight, RHeight) + 1;
		}
		bool _IsRBTree(Node* root)
		{
    
    
			if (root == nullptr)
				return true;

			//根节点是黑色的
			if (root->_col == RED)
				return false;
			//各个路径的黑色结点数是相同的,因此设立一个基准进行比较
			int benchmark = 0;
			Node* cur = root;
			while (cur)
			{
    
    
				if (cur->_col == BLACK)
					benchmark++;

				cur = cur->_left;
			}
			return Check(root,0,benchmark);
		}
		bool Check(Node* root, int BCount,int benchmark)
		{
    
    
			if (root == nullptr)
			{
    
    
				//验证基准值是否等于黑色结点数
				//只要有一个不是,即不是红黑树。
				if (BCount != benchmark)
					return false;

				return true;
			}

			//求每条黑色结点的个数
			if (root->_col == BLACK)
				BCount++;

			//验证性质3,即不能有连续的红色结点。
			if (root->_col == RED && root->_parent 
			&& root->_parent->_col == RED)
			{
    
    
				return false;
			}
			return Check(root->_left,BCount,benchmark) &
			& Check(root->_right, BCount, benchmark);
		}

		void RotateL(Node* parent)
		{
    
    
			//画图分析:
			//操作的结点有cur,cur_left,ppnode
			Node* cur = parent->_right;
			Node* cur_left = cur->_left;
			//将parent的右节点改为cur_left
			parent->_right = cur_left;
			//改变cur_left父节点的转向
			//cur_left可能为空
			if (cur_left != nullptr)
			{
    
    
				cur_left->_parent = parent;
			}
			//将parent链接在cur的左边
			//为了更新cur的parent需要保存parent的父节点
			Node* ppnode = parent->_parent;

			cur->_left = parent;
			parent->_parent = cur;

			//ppnode可能为空
			if (ppnode == nullptr)
			{
    
    
				//需要修改根节点
				_root = cur;
				cur->_parent = nullptr;
			}
			else
			{
    
    
				//改变ppnode的指向
				if (ppnode->_left == parent)
				{
    
    
					ppnode->_left = cur;
				}
				else
				{
    
    
					ppnode->_right = cur;
				}
				cur->_parent = ppnode;

			}

		}
		void RotateR(Node* parent)
		{
    
    
			//操作的结点
			Node* cur = parent->_left;
			Node* cur_right = cur->_right;

			//第一步:将cur_right链接到parent的left
			parent->_left = cur_right;
			//更改cur_right的父节点
			//注意:cur_right可能为空
			if (cur_right != nullptr)
			{
    
    
				cur_right->_parent = parent;
			}
			//第二步:将parent链接到cur的右结点。
			//先保存一下parent的父节点
			Node* ppnode = parent->_parent;

			cur->_right = parent;
			parent->_parent = cur;
			//ppnode为空说明需要修改根节点
			if (ppnode == nullptr)
			{
    
    
				_root = cur;
				cur->_parent = nullptr;
			}
			else
			{
    
    
				if (ppnode->_left == parent)
				{
    
    
					ppnode->_left = cur;
				}
				else
				{
    
    
					ppnode->_right = cur;
				}

				cur->_parent = ppnode;
			}
		}
		Node* _root = nullptr;
		public:
			size_t RotateCount = 0;
	};
};

Summarize

 The understanding of red-black trees is more abstract than that of AVL trees, which is necessary. 画图分析However, with AVL trees 旋转的基础, the difficulty here is much reduced. It's still the same as before, as long as the drawing is good, the code will not run, so the key here lies in drawing.
 Anyway, today’s sharing ends here.如果感觉有所帮助,不妨点个赞鼓励一下吧!

Guess you like

Origin blog.csdn.net/Shun_Hua/article/details/132794642