[Data Structure] red-black tree


0. introduced red-black tree

To find efficiency in AVL tree is very high, but if you want to do some structural modification of the operation of the AVL tree, the performance is very low, such as: to maintain absolute balance when inserted, more rotation frequency is worse when removed, it is possible to make the rotation has been continued to the root position.
Therefore: If the need for an efficient and orderly query data structure, and the number of data is static (ie, does not change), you can consider AVL tree, but a structure is often modified, it is not suitable.

  • AVL tree: strict balance
  • Red-black tree: approximately balanced, less than the difference in efficiency, multiple applications black trees, red-black trees better overall relatively small because the rotation of the code and less

1. red-black tree concept

Red-black tree, a binary search tree, but adds a bit stored at each node of the node indicates a color, may be Red or Black. By limiting each node will be rendered on any one path from root to leaf, red-black tree path will ensure that no more than two times to grow other paths <这是实现红黑树的目的> , so it is near equilibrium.
Here Insert Picture Description

1.1 Properties of red-black tree

  1. Each node is black instead of red
  2. The root node is black
  3. If a node is red, then its two children nodes are black (the tree does not appear in consecutive red nodes)
  4. For each node, a simple path from the node to which all descendant leaf nodes, contain the same number of black nodes (number of nodes on each path black are equal)
  5. Each leaf node is black (leaf node herein refers to the empty node)

Question: Why do meet the above properties, red-black tree can guarantee: the number of its longest path will not be more than twice the number of nodes in the shortest path node
  a red-black tree, on the assumption that each path has a black X node

  • Shortest Path: X
  • The longest path: 2X
    Here Insert Picture Description

Red-black tree insertion 1.3

Convention: CUR is the current node, a parent node P, g is the grandparent node, u is uncle node
insertion manner:

  1. Insert according to the rules of the search tree
  2. 插入节点选择红色, 若选择黑色会破坏第四点并且会影响所有路径. 选择红色只是有可能会破坏第三点并且破坏第三点容易解决
  3. 插入后, 如果其父节点为黑, 则无需任何处理

插入后可能的情况:

  • 情况一: cur为红,p为红,g为黑,u存在且为红
    解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。
    Here Insert Picture Description
  • 情况二: cur为红,p为红,g为黑,u不存在/u为黑, 并且cur在外侧
    p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反(即镜像图),
    p为g的右孩子,cur为p的右孩子,则进行左单旋转
    事后: p、g变色–p变黑,g变红

    Here Insert Picture Description
  • 情况三: cur为红,p为红,g为黑,u不存在/u为黑, 并且cur在内侧
    p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,
    p为g的右孩子,cur为p的左孩子,则针对p做右单旋转
    则转换成了情况2
    , 也可直接视为双旋, 左右双旋, 右左双旋

Here Insert Picture Description

  • : 情况二三在做完旋转后无需再向上继续判断, 因为条件都已满足

1.4 红黑树与AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O( ),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多

2. 代码及注释

#pragma once
enum Colour
{
	BLACK,
	RED,
};

template <class T>
struct RBTreeNode
{
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;

	T _data;
	Colour _col;

	RBTreeNode(const T& data)
		: _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _data(data)
		, _col(BLACK)
	{ }
};

template <class K, class V>
class RBTree
{
	typedef RBTreeNode<pair<K, V>> Node;
public:
	RBTree()
		: _root(nullptr)
	{ }

	pair<Node*, bool> insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = BLACK;
			return make_pair(_root, true);
		}
		
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			// 一. 像搜索树一样先找到应该插入的位置
			if (cur->_data.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_data.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return make_pair(cur, false);
			}
		}
		cur = new Node(kv);
		cur->_col = RED;
		Node* ret = cur; // 用于保存返回值

		if (parent->_data.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;
			// parent在randfather左时
			if (parent == grandfather->_left)
			{
				Node* uncle = grandfather->_right;
				// 情况1, 只需改变颜色然后向上继续调整
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					cur = grandfather;
					parent = cur->_parent;
				}
				// 情况2 3, cur为红,p为红,g为黑,u不存在/u为黑 无需向上继续调整
				else
				{
					// 判断是情况2还是3
					// 情况2
					//     g
					//   p
					// c
					if (cur == parent->_left)
					{
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					// 情况3
					//     g
					//   p
					//    c
					else
					{
						RotateLR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
			else // parent在grandfather右时
			{
				Node* uncle = grandfather->_left;
				// 情况1, 只需改变颜色然后向上继续调整
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					cur = grandfather;
					parent = cur->_parent;
				}
				// 情况2 3, cur为红,p为红,g为黑,u不存在/u为黑 无需向上继续调整
				else
				{
					// 判断是情况2还是3
					// 情况2
					// g
					//   p
					//     c
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					// 情况3
					//   g
					//     p
					//    c
					else
					{
						RotateRL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
		}
		_root->_col = BLACK;

		return make_pair(ret, true);
	}

	void RotateR(Node* parent)
	{
		Node* pSubL = parent->_left;
		Node* pSubLR = pSubL->_right;

		pSubL->_right = parent;
		Node* pParent = parent->_parent;
		parent->_parent = pSubL;

		if (pSubLR != nullptr)
		{
			pSubLR->_parent = parent;
		}
		parent->_left = pSubLR;

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

	void RotateL(Node* parent)
	{
		Node* pSubR = parent->_right;
		Node* pSubRL = pSubR->_left;

		pSubR->_left = parent;
		Node* pParent = parent->_parent;
		parent->_parent = pSubR;

		parent->_right = pSubRL;
		if (pSubRL != nullptr)
			pSubRL->_parent = parent;

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

	void RotateLR(Node* parent)
	{
		RotateL(parent->_left);
		RotateR(parent);
	}

	void RotateRL(Node* parent)
	{
		RotateR(parent->_right);
		RotateL(parent);
	}

private:
	Node* _root;
};

Published 53 original articles · won praise 46 · views 7157

Guess you like

Origin blog.csdn.net/new_bee_01/article/details/104640376