红黑树的详解与实现

红黑树也是一种二叉搜索树,顾名思义,红黑树是节点为黑色或者红色的一棵二叉搜索树。通过对任何一条从根到叶子的路径上各个节点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
红黑树与AVL树的比较:
红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(log2N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。
红黑树常应用于:C++STL库(map、multimap、set、multiset),Linux内核等。

红黑树的性质:
1. 每个节点不是红色就是黑色。
2. 根节点是黑色。
3. 如果一个节点是红色的,则它的孩子节点是黑色的。
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点。
5. 每个叶子节点都是黑色的(此处叶子节点指的是空节点)。

红黑树节点的定义:
 

enum Color
{
	RED,
	BLACK,
};
template<class T>
struct RBNode
{
	RBNode<T>* left;
	RBNode<T>* right;
	RBNode<T>* parent;
	T val;
	Color color;
	RBNode(const T& value = T(), Color _color = RED)
		:left(nullptr)
		, right(nullptr)
		, parent(nullptr)
		, val(value)
		, color(_color)
	{}
};

红黑树的结构:

为了后面实现关联式容器,给红黑树中增加一个头节点,头节点的 parent 指针指向根节点,头节点的 left 指针指向红黑树中最小的节点,头节点的 right 指针指向红黑树中最大的节点。

红黑树中的迭代器:

因为红黑树要作为 map、set 等一些STL库的底层实现,我们需要给红黑树也封装一个迭代器。
STL明确规定,begin() 与 end() 代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,可以得到一个有序的序列,因此:begin() 可以放在红黑树中最小节点(即最左侧节点)的位置,end() 放在最大节点(最右侧节点)的下一个位置。那么最大节点的下一个位置在哪?可以是nullptr吗?答案是不可以,因为我们知道,当迭代器位置为 end()时,进行operator--() 操作是可以得到最后一个元素的,如果end()是nullptr,就行不通了,所以我们将 end()位置设置为头节点。
迭代器用法与指针类似,所以我们要重载operator*() 和 operator->(),还要重载前置--/++,后置--/++和==/!=。

对于++操作:我们分两种情况,一种是当前节点的 right 存在,一种是当前节点的 right 不存在。

如果当前节点右子树存在,则去找右子树的最小(最左)节点。
如果当前节点右子树不存在,又分为两种情况:
1. 如果父亲节点的右孩子等于当前节点,则向上查找,即将父亲节点赋给当前节点,直到当父亲节点的右孩子不等于当前节点,则将当前节点指向该父亲节点,并返回。
例如上图,假如节点为5,父亲节点为4,4的右孩子等于当前节点5,将4赋给当前节点,4的父亲节点为8,8的右孩子不为当前节点4,则将当前节点指向8,并返回8,则节点8即为节点5通过++操作得到的位置。
2. 如果当前节点为根结点,且没有右孩子,同样,父亲节点(即头节点)的右孩子等于当前节点(根节点)则将父亲节点赋给当前节点,此时,当前节点(头节点)的父亲节点(根节点)的右孩子不等于当前节点,而是nullptr,此时,直接返回当前节点。
例如上图,假如当前节点为8,父亲节点为头节点,父亲节点的右孩子为当前节点,则将当前节点指向父亲节点,此时当前节点为头节点,父亲节点为8(根节点),父亲节点的右孩子不等于当前节点,所以该当前节点就是通过++操作得到的位置。

对于--操作:我们分成三种情况:当前节点为头节点;当前节点的左孩子存在;当前节点的左孩子不存在。

1. 当前节点为头节点,通过--操作后即为整个红黑树最大的节点,即头节点的右孩子。
2. 当前节点有左子树,找到该节点左子树的最大(最右)节点。
3. 当前节点没有左子树,向上查找,若当前节点的父节点的左孩子等于当前节点,将父节点赋给当前节点,继续向上找,直到当前节点的父亲节点的左孩子不等于当前节点,则将该节点的父亲节点赋给当前节点,并返回。
例如上图,假如当前节点为7,7的父亲节点为8,父亲节点的左孩子等于当前节点7,则将父亲结点8赋给当前节点,此时当前节点为8,8的父亲结点为4,节点4的左孩子不等于当前节点8,则将当前节点指向父亲节点,即节点4就是通过--操作得到的位置。

迭代器实现代码:
 

template<class T>
class Iterator
{
	typedef RBNode<T> Node;
public:
	Iterator(Node* _node = nullptr)
		:pNode(_node)
	{}
	Iterator(const Iterator& it)
		:pNode(it.pNode)
	{}
	T& operator*()
	{
		return pNode->val;
	}
	T* operator->()
	{
		return &(operator*());
	}
	Iterator<T>& operator++()
	{
		increasement();
		return *this;
	}
	Iterator<T>& operator++(int)
	{
		Iterator<T> temp(*this);
		increasement();
		return temp;

	}
	Iterator<T>& operator--()
	{
		decreasement();
		return *this;
	}
	Iterator<T>& operator--(int)
	{
		Iterator<T> temp(*this);
		decreasement();
		return temp;
	}
	bool operator!=(const Iterator& it)
	{
		return pNode != it.pNode;
	}
	bool operator==(const Iterator& it)
	{
		return pNode == it.pNode;
	}
private:
	void increasement()
	{
		if (pNode->right)
		{
			pNode = pNode->right;
			while (pNode->left)
			{
				pNode = pNode->left;
			}
		}
		else
		{
			Node* pParent = pNode->parent;
			while (pParent->right == pNode)
			{
				pNode = pParent;
				pParent = pParent->parent;
			}
			if (pNode->right != pParent)
			{
				pNode = pParent;
			}
		}
	}
	void decreasement()
	{
		if (pNode->parent->parent == pNode&&pNode->color == RED)
		{
			pNode = pNode->right;
		}
		else if (pNode->right)
		{
			pNode = pNode->left;
			while (pNode->right)
			{
				pNode = pNode->right;
			}
		}
		else
		{
			Node* pParent = pNode->parent;
			while (pParent->left == pNode)
			{
				pNode = pParent;
				pParent = pParent->parent;
			}
			pNode = pParent;
		}
	}
	Node* pNode;
};


红黑树的插入操作

因为红黑树也是一颗二叉搜索树,所以红黑树的插入操作和二叉搜索树一样,只不过插入之后,需要检测是否破坏了红黑树的性质。所以红黑树的插入操作重点就在于红黑树插入节点之后,检测红黑树性质是否被破坏,并且做相应操作。
那么,如何判断红黑树性质是否遭到破坏呢?
因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论。

约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点。

情况一:cur为红,p为红,g为黑,u存在且为红
解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。

情况二:cur为红,p为红,g为黑,u存在且为黑或者u不存在
解决方法:p为g的左孩子,cur为p的左孩子,则进行右单旋转;p为g的右孩子,cur为p的右孩子,则进行左单旋转。然后将p变黑,g变红。

情况三:cur为红,p为红,g为黑,u存在且为黑或者u不存在
解决方式:p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;p为g的右孩子,cur为p的左孩子,则针对p做右单旋转。然后会发现,此时变成了情况二。然后再根据情况二的具体情况去处理。

完成插入新节点,并且对红黑树进行检测并处理之后,根节点颜色可能改变,需要将根节点颜色置为黑色,然后重置头节点的左孩子和右孩子(即最小值和最大值可能改变)。

上述操作用到了旋转,旋转操作之前我在讲解AVL树中详细介绍过(博客链接:https://blog.csdn.net/smx_dd/article/details/86581492),红黑树旋转与AVL旋转操作一样。

插入代码实现:

pair<_iterator,bool> insert(const T& value)
	{
		if (header->parent == nullptr)
		{
			Node* root = new Node(value,BLACK);
			root->parent = header;
			header->left = root;
			header->right = root;
			header->parent = root;
			return make_pair(_iterator(root), true);
		}

		Node* parent = nullptr;
		Node* root = header->parent;
		Node* cur = root;
		while (cur)
		{
			if (cur->val > value)
			{
				parent = cur;
				cur = cur->left;
			}
			else if (cur->val < value)
			{
				parent = cur;
				cur = cur->right;
			}
			else
			{
				return make_pair(_iterator(cur), false);
			}
		}
		cur = new Node(value, RED);
		Node* newNode = cur;
		if (parent->val > cur->val)
		{
			parent->left = cur;
			cur->parent = parent;
		}
		else
		{
			parent->right = cur;
			cur->parent = parent;
		}
		while (cur != root&&cur->parent->color == RED)
		{
			parent = cur->parent;
			Node* grandpa = parent->parent;
			if (grandpa->left == parent)
			{
				Node* uncle = grandpa->right;
				if (uncle&&uncle->color == RED)
				{
					parent->color = BLACK;
					uncle->color = BLACK;
					grandpa->color = RED;
					cur = grandpa;
				}
				else
				{
					if (cur == parent->right)
					{
						rotateL(parent);
						swap(parent, cur);
					}
					rotateR(grandpa);
					parent->color = BLACK;
					grandpa->color = RED;
					break;
				}
			}
			else
			{
				Node* uncle = grandpa->left;
				if (uncle&&uncle->color == RED)
				{
					parent->color = BLACK;
					uncle->color = BLACK;
					grandpa->color = RED;
					cur = grandpa;
				}
				else
				{
					if (cur == parent->left)
					{
						rotateR(parent);
						swap(parent, cur);
					}
					rotateL(grandpa);
					parent->color = BLACK;
					grandpa->color = RED;
					break;
				}
			}
		}

		header->parent->color = BLACK;
		header->left = LeftMost();
		header->right = RightMost();

		return make_pair(_iterator(newNode), true);
	}

红黑树验证:

红黑树的验证比较简单,原理就是围绕着几个性质来判断即可。

bool IsValidRBTree()
	{
		Node* root = header->parent;
		if (root == nullptr)
		{
			return true;
		}
		if (BLACK != root->color)
		{
			cout << "不满足根结点为黑色!" << endl;
			return false;
		}
        // 获取任意一条路径中黑色节点的个数
		size_t blackcount = 0;
		Node* cur = root;
		while (cur)
		{
			if (cur->color == BLACK)
			{
				++blackcount;
			}
			cur = cur->left;
		}
        // 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数
		size_t k = 0;
		_IsValidRBTree(root, k, blackcount);
	}
bool _IsValidRBTree(Node* root, size_t k, size_t blackcount)
	{
		if (nullptr == root)
		{
			return true;
		}
        // 统计黑色节点的个数
		if (BLACK == root->color)
		{
			++k;
		}
        // 检测当前节点与其双亲是否都为红色
		Node* pParent = root->parent;
		if (pParent&&pParent->color == RED&&root->color == RED)
		{
			cout << "不满足不存在两个连接的红色节点!" << endl;
			return false;
		}
        // 如果pRoot是因子节点,检测当前路径中黑色节点的个数是否有问题

		if (nullptr == root->left&&nullptr == root->right)
		{
			if (k != blackcount)
			{
				cout << "不满足每条路径上黑色节点个数相同!" << endl;
				return false;
			}
		}
		return _IsValidRBTree(root->left, k, blackcount) 
            && _IsValidRBTree(root->right, k, blackcount);
	}


红黑树完整实现代码:

#include<iostream>
using namespace std;
enum Color
{
	RED,
	BLACK,
};
template<class T>
struct RBNode
{
	RBNode<T>* left;
	RBNode<T>* right;
	RBNode<T>* parent;
	T val;
	Color color;
	RBNode(const T& value = T(), Color _color = RED)
		:left(nullptr)
		, right(nullptr)
		, parent(nullptr)
		, val(value)
		, color(_color)
	{}
};
template<class T>
class Iterator
{
	typedef RBNode<T> Node;
public:
	Iterator(Node* _node = nullptr)
		:pNode(_node)
	{}
	Iterator(const Iterator& it)
		:pNode(it.pNode)
	{}
	T& operator*()
	{
		return pNode->val;
	}
	T* operator->()
	{
		return &(operator*());
	}
	Iterator<T>& operator++()
	{
		increasement();
		return *this;
	}
	Iterator<T>& operator++(int)
	{
		Iterator<T> temp(*this);
		increasement();
		return temp;

	}
	Iterator<T>& operator--()
	{
		decreasement();
		return *this;
	}
	Iterator<T>& operator--(int)
	{
		Iterator<T> temp(*this);
		decreasement();
		return temp;
	}
	bool operator!=(const Iterator& it)
	{
		return pNode != it.pNode;
	}
	bool operator==(const Iterator& it)
	{
		return pNode == it.pNode;
	}
private:
	void increasement()
	{
		if (pNode->right)
		{
			pNode = pNode->right;
			while (pNode->left)
			{
				pNode = pNode->left;
			}
		}
		else
		{
			Node* pParent = pNode->parent;
			while (pParent->right == pNode)
			{
				pNode = pParent;
				pParent = pParent->parent;
			}
			if (pNode->right != pParent)
			{
				pNode = pParent;
			}
		}
	}
	void decreasement()
	{
		if (pNode->parent->parent == pNode&&pNode->color == RED)
		{
			pNode = pNode->right;
		}
		else if (pNode->right)
		{
			pNode = pNode->left;
			while (pNode->right)
			{
				pNode = pNode->right;
			}
		}
		else
		{
			Node* pParent = pNode->parent;
			while (pParent->left == pNode)
			{
				pNode = pParent;
				pParent = pParent->parent;
			}
			pNode = pParent;
		}
	}
	Node* pNode;
};

template<class K,class T>
class RBTree
{
public:
	typedef RBNode<T> Node;
	typedef Iterator<T> _iterator;
	_iterator begin()
	{
		return _iterator(header->left);
	}
	_iterator end()
	{
		return _iterator(header);
	}
	RBTree()
	{
		header = new Node;
		header->parent = nullptr;
		header->left = header;
		header->right = header;
	}
	pair<_iterator,bool> insert(const T& value)
	{
		if (header->parent == nullptr)
		{
			Node* root = new Node(value,BLACK);
			root->parent = header;
			header->left = root;
			header->right = root;
			header->parent = root;
			return make_pair(_iterator(root), true);
		}
		

		Node* parent = nullptr;
		Node* root = header->parent;
		Node* cur = root;
		while (cur)
		{
			if (cur->val > value)
			{
				parent = cur;
				cur = cur->left;
			}
			else if (cur->val < value)
			{
				parent = cur;
				cur = cur->right;
			}
			else
			{
				return make_pair(_iterator(cur), false);
			}
		}
		cur = new Node(value, RED);
		Node* newNode = cur;
		if (parent->val > cur->val)
		{
			parent->left = cur;
			cur->parent = parent;
		}
		else
		{
			parent->right = cur;
			cur->parent = parent;
		}
		while (cur != root&&cur->parent->color == RED)
		{
			parent = cur->parent;
			Node* grandpa = parent->parent;
			if (grandpa->left == parent)
			{
				Node* uncle = grandpa->right;
				if (uncle&&uncle->color == RED)
				{
					parent->color = BLACK;
					uncle->color = BLACK;
					grandpa->color = RED;
					cur = grandpa;
				}
				else
				{
					if (cur == parent->right)
					{
						rotateL(parent);
						swap(parent, cur);
					}
					rotateR(grandpa);
					parent->color = BLACK;
					grandpa->color = RED;
					break;
				}
			}
			else
			{
				Node* uncle = grandpa->left;
				if (uncle&&uncle->color == RED)
				{
					parent->color = BLACK;
					uncle->color = BLACK;
					grandpa->color = RED;
					cur = grandpa;
				}
				else
				{
					if (cur == parent->left)
					{
						rotateR(parent);
						swap(parent, cur);
					}
					rotateL(grandpa);
					parent->color = BLACK;
					grandpa->color = RED;
					break;
				}
			}
		}

		header->parent->color = BLACK;
		header->left = LeftMost();
		header->right = RightMost();

		return make_pair(_iterator(newNode), true);
	}
	void rotateR(Node* parent)
	{
		Node* subL = parent->left;
		Node* subLR = subL->right;
		parent->left = subLR;
		if (subLR)
		{
			subLR->parent = parent;
		}
		subL->right = parent;
		
		Node* grandpa = parent->parent;
		parent->parent = subL;
		if (header->parent==parent)
		{
			header->parent = subL;
			subL->parent = header;
		}
		else
		{
			if (grandpa->left == parent)
			{
				grandpa->left = subL;
			}
			else
			{
				grandpa->right = subL;
			}
			subL->parent = grandpa;
		}
	}
	void rotateL(Node* parent)
	{
		Node* subR = parent->right;
		Node* subRL = subR->left;

		parent->right = subRL;
		if (subRL)
		{
			subRL->parent = parent;
		}
		subR->left = parent;
		Node* grandpa = parent->parent;
		parent->parent = subR;
		if (header->parent == parent)
		{
			header->parent = subR;
			subR->parent = header;
		}
		else
		{
			if (grandpa->left == parent)
			{
				grandpa->left = subR;
			}
			else
			{
				grandpa->right = subR;
			}
			subR->parent = grandpa;
		}
	}
	Node* LeftMost()
	{
		Node* cur = header->parent;
		if (nullptr == cur)
		{
			return header;
		}
		while (cur->left)
		{
			cur = cur->left;
		}
		return cur;
	}
	Node* RightMost()
	{
		Node* cur = header->parent;
		if (nullptr == cur)
		{
			return header;
		}
		while (cur->right)
		{
			cur = cur->right;
		}
		return cur;
	}
	void inorder()
	{
		_inorder(header->parent);
		cout << endl;
	}
	bool empty()
	{
		return header->parent == nullptr;
	}
	bool IsValidRBTree()
	{
		Node* root = header->parent;
		if (root == nullptr)
		{
			return true;
		}
		if (BLACK != root->color)
		{
			cout << "不满足根结点为黑色!" << endl;
			return false;
		}
		size_t blackcount = 0;
		Node* cur = root;
		while (cur)
		{
			if (cur->color == BLACK)
			{
				++blackcount;
			}
			cur = cur->left;
		}
		size_t k = 0;
		_IsValidRBTree(root, k, blackcount);
	}
private:
	Node* header;
	void _inorder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_inorder(root->left);
		cout << root->val << " ";
		_inorder(root->right);
	}
	bool _IsValidRBTree(Node* root, size_t k, size_t blackcount)
	{
		if (nullptr == root)
		{
			return true;
		}
		if (BLACK == root->color)
		{
			++k;
		}
		Node* pParent = root->parent;
		if (pParent&&pParent->color == RED&&root->color == RED)
		{
			cout << "不满足不存在两个连接的红色节点!" << endl;
			return false;
		}
		if (nullptr == root->left&&nullptr == root->right)
		{
			if (k != blackcount)
			{
				cout << "不满足每条路径上黑色节点个数相同!" << endl;
				return false;
			}
		}
		return _IsValidRBTree(root->left, k, blackcount) && _IsValidRBTree(root->right, k, blackcount);
	}
};
int main()
{
	RBTree<int, int> tree;
	int a[] = { 8, 2, 3, 1, 9, 3, 2, 5, 7 };
	for (auto e : a)
	{
		tree.insert(e);
	}
	tree.inorder();
	cout << tree.IsValidRBTree() << endl;

	system("pause");
	return 0;
}

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值