[Data structure and algorithm]——Hand-tearing red and black trees

Table of contents

(1) Definition of red-black tree

1. Introduction of red-black trees

2. The concept of red-black trees

3. Properties of red-black trees

(2) Operation of red-black trees

1. Definition of red-black tree nodes

2. Insertion operation of red-black tree

1️⃣ Ideas

2️⃣ Code implementation

3. Deletion operation of red-black tree (understand)

4. Comparison between red-black trees and AVL trees

5. Verification of red-black trees

Summarize


(1) Definition of red-black tree

1. Introduction of red-black trees

In order to maintain the balance of the AVL tree, the overall topology of the entire tree is adjusted very frequently after insertion and deletion operations, which is costly. To this end, the conditions for the balance standard of the AVL tree are further relaxed and the structure of the red-black tree is introduced.

2. The concept of red-black trees

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 restricting the coloring of each node on any path from root to leaf, the red-black tree ensures that no path is twice as long as
any other path, and is therefore close to balance .
 

3. Properties of red-black trees

A red-black tree is a binary sorted tree that satisfies the following red-black properties:

  • ①Each node is either red or black
  • ②The root node is black
  • ③Leaf nodes (fictitious external nodes, NULL nodes) are all black
  • ④ There are no two adjacent red nodes (that is, the parent node and child node of the red node are both black)
  • ⑤For each node, the simple path from the node to any leaf node contains the same number of black nodes.

Similar to the binary search tree and B-tree, in order to facilitate the implementation and understanding of the red-black tree, n +1 external leaf nodes are introduced to ensure that the left and right of each node (internal node) in the red-black tree Neither child is null. The picture below shows a red-black tree:

The total number of black nodes on any simple path starting from a certain node (excluding the node) and reaching a leaf node is called the black height of the node ( recorded as bh). The concept of black height is determined by property ⑤ definite. The black height of the root node is called the black height of the red-black tree.

Conclusion 1: The longest path from the root to the leaf node is no more than twice the shortest path.

  1. According to property ⑤, when the simple path from the root to any leaf node is the shortest, this path must be entirely composed of black nodes;
  2. According to property ④, when a certain path is the longest, this path must be composed of black nodes and red nodes. At this time, the number of red nodes and black nodes is the same;
  3. 13-8-1 and 13-17-25-27 in the picture above are two such paths.

Conclusion 2: The height of a red-black tree with n internal nodes is h <=2log(N+1)

It can be seen that the "moderate balance" of the red-black tree is reduced from the "height balance" of the AVL tree to "the height of the left and right subtrees of any node differs by no more than 2 times", which also reduces the need for adjustment during dynamic operations. frequency. For a dynamic search tree, if there are fewer insertion and deletion operations and more search operations, it is more appropriate to use an AVL tree, otherwise a red-black tree is more appropriate. However, since the cost of maintaining this high degree of balance is much greater than the benefits obtained, the actual application of red-black trees is more widespread. Map and set in C++ (TreeMap and TreeSet in Java) are implemented using red-black trees. of.
 


(2) Operation of red-black trees

1. Definition of red-black tree nodes

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

Thinking: In the definition of nodes, why should the default color of nodes be set to red?

A red-black tree is a self-balancing binary search tree that maintains balanced properties through a series of rotation and recolor operations when nodes are inserted and deleted. Depending on the nature of a red-black tree, each node is either red or black.

In the insertion and deletion operations of the red-black tree, in order to maintain the balance of the tree, the nodes need to be rotated and colored. There are several reasons why newly inserted nodes are colored red by default:

  1. Red nodes have more room for adjustment: Coloring nodes red by default allows new nodes to have more room for adjustment, which helps maintain the balance of the tree. Insertion and deletion operations of red nodes have less impact on the balance of the tree because red nodes can be rotated and colored if needed.

  2. Simplify the correction process of insertion operations: New nodes are red by default, which can simplify the correction process of insertion operations. According to the nature of the red-black tree, after inserting the red node, you only need to consider the relationship with the parent node, and do not need to consider more adjustments like the black node. This reduces the complexity and overhead of remediation operations.

  3. Reduce the number of overall balancing operations: Setting new nodes to red by default can reduce the number of overall balancing operations. Since insertion and deletion operations of red nodes have less impact on the balance of the tree, setting new nodes to red by default can reduce the frequency and overhead of balancing operations compared to setting them to black by default.

In general, setting the default color of nodes to red is to simplify the correction process of insertion and deletion operations in self-balancing data structures such as red-black trees, while reducing the number of overall balancing operations and improving the efficiency and performance of the algorithm.

2. Insertion operation of red-black tree

1️⃣ Ideas

The red-black tree is based on the binary search tree with its balance constraints, so the insertion of the red-black tree can be divided into two steps:

  • 1. Insert new nodes according to the tree rules of binary search (we have said this before, so we won’t explain it too much here)
  • 2. Check whether the properties of the red-black tree cause damage after the new node is inserted ( key explanation )

Because the default color of a new node is red, therefore: if the color of its parent node is black, it does not violate any
properties of the red-black tree, and no adjustment is needed; but when the color of the parent node of the newly inserted node is red, it violates the properties Three cannot have
red nodes connected together. At this time, we need to discuss the situation of red and black trees:
 

Convention: cur is the current node, p is the parent node, g is the grandparent node, and u is the uncle node.
 

  • Case 1 : cur is red, p is red, g is black, u exists and is red

 Solution: Change p and u to black, g to red, then treat g as cur, and continue to adjust upward.

  •  Case 2: cur is red, p is red, g is black, u does not exist/u exists and is black

1. p is the left child of g, and cur is the left child of p, then a right single rotation is performed; on the contrary,

2. p is the right child of g, and cur is the right child of p, then perform left single rotation

3. p and g change color - p turns black and g turns red

  • Case 3: cur is red, p is red, g is black, u does not exist/u exists and is black
     

 Just handle each situation accordingly.


2️⃣ Code implementation

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);
		if (parent->_kv.first > kv.first)
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		cur->_parent = parent;

		//进行调整操作
		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			if (grandfather->_left == parent)
			{
				Node* uncle = grandfather->_right;
				// 情况1:u存在且为红,变色处理,并继续往上处理
				if (uncle && uncle->_col == RED)
				{
					parent->_col = BLACK;
					uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续往上调整
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 情况2+3:u不存在/u存在且为黑,旋转+变色
				{
					//     g
					//   p   u
					// c 
					if (cur == parent->_left)
					{
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//     g
						//   p   u
						//     c
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						//parent->_col = RED;
						grandfather->_col = RED;
					}

					break;
				}
			}
			else // (grandfather->_right == parent)
			{
				//    g
				//  u   p
				//        c
				Node* uncle = grandfather->_left;
				// 情况1:u存在且为红,变色处理,并继续往上处理
				if (uncle && uncle->_col == RED)
				{
					parent->_col = BLACK;
					uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续往上调整
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 情况2+3:u不存在/u存在且为黑,旋转+变色
				{
					//    g
					//  u   p
					//        c
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						grandfather->_col = RED;
						parent->_col = BLACK;
					}
					else
					{
						//    g
						//  u   p
						//    c
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
		}

		_root->_col = BLACK;

		return true;
	}

3. Deletion operation of red-black tree (understand)

The deletion of red-black trees will not be explained in this section. Interested students can refer to: "Introduction to Algorithms" or "STL Source Code Analysis"
Deletion of Red-Black Trees


4. Comparison between red-black trees and AVL trees

Both red-black trees and AVL trees are efficient balanced binary trees. The time complexity of adding, deleting, modifying, and checking is O(log_2 N). Red-black trees do not pursue absolute balance. They only need to ensure that the longest path does not exceed twice the shortest path. , relatively speaking, it reduces the number of insertions and rotations, so the performance is better than AVL trees in structures where additions and deletions are often performed, and the implementation of red-black trees is relatively simple, so there are more red-black trees in actual use.

5. Verification of red-black trees

The detection of red-black trees is divided into two steps:

  • 1. Check whether it satisfies the binary search tree (whether the in-order traversal is an ordered sequence)
  • 2. Check whether it meets the properties of a red-black tree
     
	void Inorder()
	{
		_Inorder(_root);
	}

	bool IsBalance()
	{
		if (_root && _root->_col == RED)
		{
			cout << "根节点颜色是红色" << endl;
			return false;
		}

		int benchmark = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
				++benchmark;
			cur = cur->_left;
		}
		// 连续红色节点
		return _Judge(_root, 0, benchmark);
	}

bool _Judge(Node* root, int blackNum, int benchmark)
	{
		if (root == nullptr)
		{
			if (blackNum != benchmark)
			{
				cout << "某条路径黑色节点的数量不相等" << endl;
				return false;
			}
			return true;
		}

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

		if (root->_col == RED
			&& root->_parent
			&& root->_parent->_col == RED)
		{
			cout << "存在连续的红色节点" << endl;
			return false;
		}

		return _Judge(root->_left, blackNum, benchmark)
			&& _Judge(root->_right, blackNum, benchmark);
	}

Summarize

The above is an introduction to red-black trees. Thank you for watching and supporting! ! !

 

Guess you like

Origin blog.csdn.net/m0_56069910/article/details/132384365