飞道的博客

【C++】STL关联式容器之红黑树

248人阅读  评论(0)

1、概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个保存颜色(红色或黑色)的存储位。

任何一条从根到叶子的路径上,红黑树确保最长的路径不会超过最短路径的两倍,因而是接近平衡的。

2、红黑树的特性

1)根节点是黑色的

2)每个节点不是红色就是黑色

3)没有连在一起的红色节点

4)每条路径上的黑色节点的个数相等。

3、红黑树的结构

红黑树的实现会增加一个头节点,为了和根节点避免,所以将头节点的颜色给成红色。
 
红黑树实现的时候为什么默认节点给成红色?
假设默认给出黑色节点,那么将节点添加到树中时,一定会破坏性质:每条路径上的黑色节点的个数相等,但如果是红色的,不一定会破坏性质:没有连在一起的红色节点。因为插入节点的父节点如果是黑色的,那就不会破坏这个性质。
 

4、红黑树的调整

插入节点之后,如果破坏了性质:没有连在一起的红色节点,那么就需要对节点进行调整。
cur:表示已经插入的节点
parent:表示插入节点的父节点
grandparent:表示parent的父节点
uncle:表示父节点的另一个节点

1)情况一:cur为红,parent为红,uncle存在且为红

解决方式:将parent节点颜色改为黑色,grandparent节点颜色改为红色,uncle节点颜色改为黑色。

 
2)情况二:uncle不存在或者uncle节点的颜色为黑色

cur是parent的左孩子,parent是grandparent的左孩子。

解决方式:将parent的颜色改成黑色,将grandparent的颜色改为红色,然后对grandparent节点进行右单旋。
 
3)情况三:

cur是parent的右孩子,parent是grandparent的左孩子。

解决方式:先对parent进行左单旋,将情况三转换成情况二,然后按照情况二的方式处理。

 
注意:这里我们要考虑一个问题,grandparent是子树还是根节点,如果是根节点,那么就直接放回;如果是子树的话,我们需要继续向上更新,因为grandparent的父节点很可能也是红色的。
 

5、红黑树的简单实现

这里的实现时为了之后能可以以红黑树为底层,对map和set的封装,所以这里的KOFD是为了提取set或map中的值域。

5.1 红黑树节点

#pragma once
#include <iostream>

// 节点颜色采用枚举类型
enum Color{
   RED, BLACK};
// 假设实现的红黑树的节点值是不能重复的
template<class T>
struct RBTreeNode
{
   
	RBTreeNode(const T& data = T(), Color c = RED)
	: m_pLeft(nullptr)
	, m_pRight(nullptr)
	, m_pParent(nullptr)
	, val(data)
	, color(c)
	{
   }

	RBTreeNode<T>* m_pLeft;
	RBTreeNode<T>* m_pRight;
	RBTreeNode<T>* m_pParent;
	T val;
	Color color;
};

5.2 红黑树的迭代器

template<class T>
class IteratorRBTree
{
   
	typedef RBTreeNode<T> Node;
	typedef IteratorRBTree<T> Self;

public:
	IteratorRBTree(Node* n = nullptr)
		: node(n)
	{
   }

	// 具有指针类似的行为
	T& operator*()
	{
   
		return node->val;
	}

	T* operator->()
	{
   
		return &(operator*());
	}

	Self& operator++()
	{
   
		Increment();
		return *this;
	}

	Self operator++(int)
	{
   
		Self temp(*this);
		Increment();
		return temp;
	}

	Self& operator--()
	{
   
		Decrement();
		return *this;
	}

	Self operator--(int)
	{
   
		Self temp(*this);
		Decrement();
		return temp;
	}

	void Increment()
	{
   
		if (node->m_pRight)
		{
   
			node = node->m_pRight;
			while (node->m_pLeft)
				node = node->m_pLeft;
		}
		else
		{
   
			Node* parent = node->m_pParent;
			while (parent->m_pRight == node)
			{
   
				node = parent;
				parent = node->m_pParent;
			}
			// 注意:根节点没有右子树的情况---而迭代器刚好处在根的位置
			if (node->m_pRight != parent)
				node = parent;
		}
	}

	void Decrement()
	{
   
		// node在end() 的位置
		if (node == node->m_pParent->m_pParent && RED == node->color)
		{
   
			node = node->m_pRight;
		}
		else if (node->m_pLeft)
		{
   
			node = node->m_pLeft;
			while (node->m_pRight)
				node = node->m_pRight;
		}
		else
		{
   
			Node* parent = node->m_pParent;
			while (parent->m_pLeft == node)
			{
   
				node = parent;
				parent = node->m_pParent;
			}

			node = parent;
		}
	}


	bool operator==(const Self& s)const 
	{
   
		return node == s.node;
	}

	bool operator!=(const Self& s)const 
	{
   
		return node != s.node;
	}
private:
	Node* node;
};

5.3 红黑树各功能实现

template<class T, class KOFD>
class RBTree
{
   
	typedef RBTreeNode<T> Node;
public:
	typedef IteratorRBTree<T> iterator;
public:
	RBTree()
		: _size(0)
	{
   
		head = new Node();
		head->m_pLeft = head;
		head->m_pRight = head;
		head->m_pParent = nullptr;
	}

	~RBTree()
	{
   
		Destory(GetRoot());
		_size = 0;
		delete head;
		head = nullptr;
	}

	// 迭代器的操作
	iterator begin()
	{
   
		return iterator(head->m_pLeft);
	}

	iterator end()
	{
   
		return iterator(head);
	}

	// 容量相关的操作
	size_t size()const
	{
   
		return _size;
	}

	bool empty()const 
	{
   
		return nullptr == head->m_pParent;
	}


	//pair<iterator, bool> 
	std::pair<iterator, bool> Insert(const T& data)
	{
   
		// 注意:root就是head->pParent的别名
		Node* & root = GetRoot();
		if (root == nullptr)
		{
   
			root = new Node(data, BLACK);
			root->m_pParent = head;
			head->m_pLeft = root;
			head->m_pRight = root;
			_size = 1;
			//return true;
			return std::make_pair(iterator(root), true);
		}
		// 1.找到插入的位置
		Node* cur = root;
		Node* parent = head;
		KOFD key;
		while (cur)
		{
   
			parent = cur;
			if (key(data) < key(cur->val))
				cur = cur->m_pLeft;
			else if (key(data) > key(cur->val))
				cur = cur->m_pRight;
			else
				//return false;
				return std::make_pair(iterator(cur), false);
		}
		// 2.插入该节点
		Node* newNode = new Node(data);
		cur = newNode;
		cur = new Node(data);
		if (key(data) < key(parent->val))
			parent->m_pLeft = cur;
		else if (key(data) > key(parent->val))
			parent->m_pRight = cur;

		// 别忘记更新父节点
		cur->m_pParent = parent;

		// 3.默认节点是红色的,如果parent的父节点也是红色,则需要更新
		while (head != parent && RED == parent->color)
		{
   
			// head != parent 说明parent一定是树中有效节点
			Node* grandFather = parent->m_pParent;
			if (grandFather->m_pLeft == parent)
			{
   
				// 情况一:父节点存在且为红
				Node* uncle = grandFather->m_pRight;
				if (uncle && uncle->color == RED)
				{
   
					parent->color = BLACK;
					uncle->color = BLACK;
					grandFather->color = RED;
					cur = grandFather;
					parent = cur->m_pParent;
				}
				else
				{
   
					// 情况三:cur插入在parent的右子树中
					if (cur == parent->m_pRight)
					{
   
						RotateLeft(parent);
						std::swap(cur, parent);
					}
					grandFather->color = RED;
					parent->color = BLACK;
					RotateRight(grandFather);
				}
			}
			else if (grandFather->m_pRight == parent)
			{
   
				Node* uncle = grandFather->m_pLeft;
				if (uncle && uncle->color == RED)
				{
   
					parent->color = BLACK;
					uncle->color = BLACK;
					grandFather->color = RED;
					cur = grandFather;
					parent = cur->m_pParent;
				}
				else
				{
   
					if (cur == parent->m_pLeft)
					{
   
						RotateRight(parent);
						std::swap(cur, parent);
					}

					grandFather->color = RED;
					parent->color = BLACK;
					RotateLeft(grandFather);
				}
			}
		}

		// 将根节点的颜色更新为黑色
		root->color = BLACK;
		head->m_pLeft = LeftMost();
		head->m_pRight = RightMost();
		++_size;
		//return true;
		return std::make_pair(iterator(newNode), true);
	}

	void InOrder()
	{
   
		InOrder(GetRoot());
	}

	void Swap(RBTree<T, KOFD>& t)
	{
   
		std::swap(head, t.head);
		std::swap(_size, t._size);
	}

	void Clear()
	{
   
		Destory(GetRoot());
		_size = 0;
	}

	iterator Find(const T& data)
	{
   
		Node* cur = GetRoot();
		KOFD key;
		while (cur)
		{
   
			if (key(cur->val) == key(data))
				return iterator(cur);
			else if (key(data) < key(cur->val))
				cur = cur->m_pLeft;
			else
				cur = cur->m_pRight;
		}
		return end();
	}

	bool isRBTree()
	{
   
		Node* root = GetRoot();
		if (root->color == BLACK)
		{
   
			std::cout << "违反 根节点不是黑色的!!!" << std::endl;
			return false;
		}

		// 检测路径黑色节点的个数
		int blackCount = 0;
		Node* cur = root;
		while (cur)
		{
   
			if (cur->color == BLACK)
				blackCount++;

			cur = cur->m_pLeft;
		}

		int pathBlackCount = 0;
		return _isRBTree(root, pathBlackCount, blackCount);
	}

private:
	bool _isRBTree(Node* root, size_t path, const size_t count)
	{
   
		if (root == nullptr)
			return true;

		if (root->color == BLACK)
			++path;

		Node* parent = root->m_pParent;
		if (parent != head && RED == parent->color && RED == root->color)
		{
   
			std::cout << "违反 连在一起的红色的节点!!!" << std::endl;
			return false;
		}


		if (root->m_pLeft == nullptr && root->m_pRight == nullptr)
		{
   
			if (path != count)
			{
   
				std::cout << "违反 每条路径的黑色节点个数不一样!!!" << std::endl;
				return false;
			}
		}
		return _isRBTree(root->m_pLeft, path, count) && 
			_isRBTree(root->m_pParent, path, count);
	}


	Node*& GetRoot()
	{
   
		return head->m_pParent;
	}

	Node* LeftMost()
	{
   
		Node* root = GetRoot();
		if (root == nullptr)
			return head;
		Node* cur = root;
		while (cur->m_pLeft != nullptr)
		{
   
			cur = cur->m_pLeft;
		}
		return cur;
	}

	Node* RightMost()
	{
   
		Node* root = GetRoot();
		if (root == nullptr)
			return head;
		Node* cur = root;
		while (cur->m_pRight != nullptr)
			cur = cur->m_pRight;
		return cur;
	}

	void RotateLeft(Node* parent)
	{
   
		Node* subR = parent->m_pRight;
		Node* subRL = subR->m_pLeft;

		parent->m_pRight = subRL;
		if (subRL)
			subRL->m_pParent = parent;
		subR->m_pLeft = parent;

		// parent可能是更节点,也可能是子树,所以要记录parent的根节点
		Node* pParent = parent->m_pParent;
		parent->m_pParent = subR;
		subR->m_pParent = pParent;

		if (pParent == head)
			head->m_pParent = subR;
		else
		{
   
			if (pParent->m_pLeft == parent)
				pParent->m_pLeft = subR;
			else if (pParent->m_pRight == parent)
				pParent->m_pRight = subR;
		}
	}

	void RotateRight(Node* parent)
	{
   
		Node* subL = parent->m_pLeft;
		Node* subLR = parent->m_pRight;

		parent->m_pLeft = subLR;
		if (subLR)
			subLR->m_pParent = parent;

		subL->m_pRight = parent;

		Node* pParent = parent->m_pParent;
		parent->m_pParent = subL;
		subL->m_pParent = pParent;

		if (head == pParent)
			head->m_pParent = subL;
		else
		{
   
			if (pParent->m_pLeft == parent)
				pParent->m_pLeft = subL;
			else if (pParent->m_pRight == parent)
				pParent->m_pRight = subL;
		}
	}

	void InOrder(Node* root)
	{
   
		if (root)
		{
   
			InOrder(root->m_pLeft);
			std::cout << root->val << " ";
			InOrder(root->m_pRight);
		}
	}

	void Destory(Node* & pRoot)
	{
   
		if (pRoot)
		{
   
			Destory(pRoot->m_pLeft);
			Destory(pRoot->m_pRight);
			delete pRoot;
			pRoot = nullptr;
		}
	}

private:
	Node* head;
	size_t _size;
}; 

5.4 红黑树的测试

struct KOFD
{
   
	int operator()(int data)
	{
   
		return data;
	}
};

void TestRBTree()
{
   
	int array[] = {
    5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };
	RBTree<int, KOFD> t;
	for (auto& e : array)
		t.Insert(e);

	std::cout << "迭代器遍历红黑树!!!" << std::endl;
	auto it = t.begin();
	while (it != t.end())
	{
   
		std::cout << *it << " ";
		++it;
	}
	std::cout << std::endl;

	for (auto & e : t)
	{
   
		std::cout << e << " ";
	}
	std::cout << std::endl;
	t.InOrder();
}

 

6、红黑树和AVL树的比较

1)红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(log2N)。

2)红黑树近似平衡,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优。

3)红黑树实现比较简单,所以实际运用中红黑树更多。


转载:https://blog.csdn.net/weixin_43967449/article/details/116745464
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场