Why Red-Black Trees Are So Popular

There are many balanced binary search trees, but when we mention balanced binary search trees, red-black trees are often mentioned, and its "appearance rate" is even higher than that of balanced binary search trees.

The red-black tree is a relatively balanced binary search tree, which does not meet the strict definition of a balanced binary search tree.

Table of contents

Red-black tree insertion

 Verification of red-black tree


Properties of red-black trees

  • Each node is either red or black
  • the root node is black 
  • Any adjacent node above and below cannot be red at the same time. If a node is red, its two child nodes are black 
  •  For each node, all paths from the node to all its descendant leaf nodes contain the same number of black nodes 
  • Each leaf node is a black empty node, that is, the leaf node does not store data.

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.

Performance analysis of red-black tree

Assuming that the number of black nodes on each path is N, the shortest path is all black nodes, and the longest path node is one black node and one red node alternately, N<=arbitrary path length<=2N.

The balanced binary search tree is proposed to solve the performance degradation problem of the binary search tree due to dynamic updates. Therefore, "balanced" can be equivalent to no performance degradation, and "approximately balanced" can be equivalent to less severe performance degradation. The time complexity of many operations on a binary search tree is proportional to the height of the tree. The height of an extremely balanced binary tree (full binary tree or complete binary tree) is about log n. If you want to prove that the red-black tree is approximately balanced, you only need to prove that the height of the red-black tree is approximately log n (such as the same magnitude).

prove:

 The longest path of the red-black tree will not exceed 2log n, that is, the height of the red-black tree will not exceed 2log n. A red-black tree is only twice as tall as a height-balanced AVL tree, so the performance loss is not much. Compared with the AVL tree, the maintenance cost of the red-black tree is lower, so the performance is not worse than the AVL tree.

So why are red-black trees so popular? 

The AVL tree is a highly balanced binary tree, which is very efficient in finding data. However, in order to maintain this high balance, the AVL tree needs to pay more. In order to maintain balance, the distribution of nodes in the tree must be adjusted every time data is inserted or deleted, which is complex and time-consuming.
The red-black tree is only approximately balanced, but not strictly defined. Therefore, the cost of maintaining balance is lower than that of the AVL tree, but the performance loss is not large. For engineering applications, we prefer red-black trees with relatively compromised maintenance cost and performance. More importantly, most programming languages ​​provide classes that encapsulate the implementation of the red-black tree, which we can use directly without starting from scratch, which greatly saves development time. A red-black tree is an approximately balanced binary search tree. It was created to solve the performance degradation problem caused by dynamic data update of binary search tree. The height of the red-black tree is approximately logn, and the time complexity of insertion, deletion and search operations is O(logn).
 

Red-black tree node definition 

   // node color
    enum Color { RED, BLACK };


    // Red-black tree node definition
    template<class T>
    struct RBTreeNode
    {         RBTreeNode(const T& data = T(), Color color = RED)             : _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)             , _data(data ), _color(color)         {}         RBTreeNode<T>* _pLeft; // the left child of the node         RBTreeNode<T>* _pRight; // the right child of the node         RBTreeNode<T>* _pParent; // the parent of the node         T_data; // The value field of the node         Color _color; // the color of the node     };









When inserting a node, we give the default color of the node red, because the black nodes on each path are the same, if the node is given black, it will affect every path, and the new node is given red if it does not meet the rules , you only need to adjust this or the adjacent path, and the cost of giving the new node red is much smaller than giving the new node black.

Red-black tree insertion

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

2. After detecting the insertion of a new node, whether the nature of the red-black tree is damaged

Because the default color of a new node is red, if the color of its parent node is black, it does not violate any properties of the red-black tree, and no adjustment is required; but when the color of the parent node of the newly inserted node is red, it violates the red-black tree properties, there cannot be red nodes connected together. At this time, it is necessary to discuss the red-black tree according to the situation:

cur is the current node, p is the parent node, g is the grandparent node, u is the uncle node

The cur node is not necessarily a newly added node, it may be a node pointed to after adjustment.

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

Solution: change p, u to black, g to red, then take g as cur, and continue to adjust upwards.

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

There are two cases of u
: 1. If the u node does not exist, then cur must be a newly inserted node, because if cur is not a newly inserted node, then cur and p must have a node whose color is black, which does not satisfy the property: each The number of black nodes in the path is the same.
2. If the u node exists, it must be black. Then the original color of the cur node must be black. The reason why it is red now is because the cur subtree changes the color of the cur node from Black changed to red.

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

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

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 converted to Case 2

Code:

bool Insert(const T& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			return true;
		}
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (data > cur->_data)
			{
				parent = cur;
				cur = cur->_right
			}
			else if (data < >> cur->_data)
			{
				parent = cur;
				cur = cur->_left
			}
			else 
			{
				return false;
			}
		}
		//插入节点
		cur = new Node(data);
		cur->_col(RED);
		if (parent->data < data)
		{
			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 = parent->_right;
				//1.uncle存在且为红
				if (uncle && uncle->_col == RED)
				{
					//变色+继续向上处理
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					cur = grandfather;
					parent = cur->_parent;
				}
				else//2.uncle不存在/uncle存在且为黑
				{
					//        g
					//     p
					//  c

					//        g
					//     p
					//        c
					if (cur == parent->_left)
					{
						//右旋
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;

					}
					if (cur == parent->_right)
					{
						//先左旋再右旋
						RotateL(parent);
						RotateR(grandfather);
						parent->_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 // 2 + 3、uncle不存在/ 存在且为黑
				{
					//  g    
					//     p
					//        c

					//  g
					//     p
					//  c
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
		}
		_root->_col = BLACK;
		return true;
	}

 Verification of red-black tree

Red-black tree detection 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 satisfies the properties of red-black tree

bool IsBalance()
	{
		if (_root && _root->_col == RED)
		{
			cout << "根节点不是黑色" << endl;
			return false;
		}
		
		// 最左路径黑色节点数量做基准值
		int banchmark = 0;
		Node* left = _root;
		while (left)
		{
			if (left->_col == BLACK)
				++banchmark;
			
			left = left->_left;
		}

		int blackNum = 0;
		return _IsBalance(_root, banchmark, blackNum);
	}

	bool _IsBalance(Node* root, int banchmark, int blackNum)
	{
		if (root == nullptr)
		{
			if (banchmark != blackNum)
			{
				cout << "存在路径黑色节点的数量不相等" << endl;
				return false;
			}

			return true;
		}

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

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

		return _IsBalance(root->_left, banchmark, blackNum)
			&& _IsBalance(root->_right, banchmark, blackNum);
	}

Guess you like

Origin blog.csdn.net/m0_55752775/article/details/129412112