红黑树——原理刨析

7 篇文章 0 订阅
4 篇文章 0 订阅

        众所周知,红黑树是从AVLTree树中衍变而来的,所以在学红黑树之前还是要好好的理解一下AVLTree树的原理,为理解红黑树减轻理解负担,好了进入正题。

红黑树原理:

        由名可知,红黑树——肯定是与颜色有关的一个树,又因为是从AVLTree树中衍化过来的,所以也是搜索树(不是平衡二叉树,后面讲解定义时会详细解释),通过对不同情况的处理,去调整红黑树节点的颜色或者红黑树的高度去使其满足,红黑树的定义规则。

红黑树的定义:

        红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或
Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,
红黑树确保没有一条路
径会比其他路径长出俩倍
,因而是接近平衡的,所以不是平衡二叉树。

如上图,就是红黑树。

红黑树的性质:

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

思考:为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍?

答:上述红黑树的性质第4条 说明每条路上面的黑色节点数量都是相等的,所以说该节点的左右子树可以有一棵子树全为黑色节点 另一个红黑节点交替(红节点数量与黑节点数量相等),这就能保证其最长路径中节点个数不会超过最短路径节点个数的两倍。

红黑树中重要的函数讲解:

        和AVLTree一样,插入函数是难点,但是掌握AVLTree之后,这里的插入就不怎么难了,AVLTree中提到左旋,右旋,这里不做讲解,如有疑惑,参考上篇文章,有流程图

        在我看来红黑树与AVLTree不同点就是规则不同,红黑树是靠颜色去调整高度差,而AVLTree是通过平衡因子去调节的。

情况一:整棵树或者子树为上上图,就只能进行颜色更新 将uncle更新为黑色 父亲更新为黑色 祖父更新为红色 再继续向上以同样的方式更新 直到更新到根节点或者进行了一次旋转调整 (旋转调整会将树的高度改变并将颜色确定为最终的颜色)就不再向上更新

情况二:uncle存在且为黑或者不存在

1,先进行情况一的颜色更新,出现了旋转的情况,再进行旋转 最后进行旋转的颜色更新

               

        2,刚开始整棵树就为要旋转的情况或者为整棵树的子树,如上图

单选和双旋在AVLTree中已经讲解过了,这里最重要的就是如何进行颜色更新,而不是旋转。

        

红黑树完整代码:

#pragma once
#include<iostream>
using namespace std;

enum color
{
	BLACK,
	RED
};

template<class K,class V>
struct RBTreeNode
{

	RBTreeNode<K, V>* _parent;
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	pair<K, V> _kv;
	color _col;

	RBTreeNode(const pair<K,V>& kv)
		:_kv(kv)
		,_parent(nullptr)
		,_left(nullptr)
		,_right(nullptr)
		,_col(RED)
	{

	}
};

template<class K,class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;
private:
	Node* _root = nullptr;

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

		while (parent && parent->_col == RED)
		{
			Node* grandfater = parent->_parent;
			assert(grandfater);

			//祖父颜色不为黑 说明红黑树在插入之前就是不平衡的
			assert(grandfater->_col == BLACK);
			if (parent == grandfater->_left)
			{
				Node* uncle = grandfater->_right;
				if (uncle && uncle->_col == RED)
				{
					grandfater->_col = RED;
					parent->_col = uncle->_col = BLACK;

					cur = grandfater;
					parent = cur->_parent;
				}
				else
				{
					if (parent->_left == cur)
					{
						//    g
						//  p   u
						//c
						RotateR(grandfater);
						parent->_col = BLACK;
						grandfater->_col = RED;
					}
					else
					{
						//    g
						//  p   u
						//    c
						RotateL(parent);
						RotateR(grandfater);
						cur->_col = BLACK;
						grandfater->_col = RED;
					}
					break;
				}
			}
			else
			{
				Node* uncle = grandfater->_left;
				if (uncle && uncle->_col == RED)
				{
					grandfater->_col = RED;
					parent->_col = uncle->_col = BLACK;

					cur = grandfater;
					parent = cur->_parent;
				}
				else
				{
					if (parent->_right == cur)
					{
						//    g
						//  u   p
						//        c
						RotateL(grandfater);
						parent->_col = BLACK;
						grandfater->_col = RED;
					}
					else
					{
						//    g
						//  u   p
						//     c
						RotateR(parent);
						RotateL(grandfater);
						cur->_col = BLACK;
						grandfater->_col = RED;
					}
					break;
				}
			}
		}
		_root->_col = BLACK;
		return true;
	}
	void Inorder()
	{
		_Inorder(_root);
	}
	bool IsBalance()
	{
		if (_root == nullptr)
		{
			return false;
		}
		if (_root->_col == RED)
		{
			cout << "根节点为红色,不是红黑树" << endl;
			return false;
		}
		int benchmark = 0;
		return PrevCheck(_root, 0, benchmark);
	}
	private:
		bool PrevCheck(Node* root, int blackNum, int& benchmark)
		{
			if (root == nullptr)
			{
				if (benchmark == 0)
				{
					blackNum = benchmark;
					return true;//第一次遍历到空 没有比较意义 将第一次的黑色节点作为参考去判断
				}
				if (blackNum != benchmark)
				{
					cout << "红黑树各路黑色节点数量不相同" << endl;
					return false;
				}
				else
				{
					return true;
				}
			}
			if (root->_col == BLACK)
			{
				blackNum++;
			}
			if (root->_col == RED && root->_parent->_col == RED)
			{
				cout << "出现连续红色节点,不是红黑树" << endl;
				return false;
			}

			return PrevCheck(root->_left,blackNum,benchmark) 
				&& PrevCheck(root->_right,blackNum,benchmark);
		}
		void _Inorder(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			_Inorder(root->_left);
			cout << root->_kv.first << ":" << root->_kv.second << endl;
			_Inorder(root->_right);
		}
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		if (subLR)
		{
			subLR->_parent = parent;
		}

		Node* ppNode = parent->_parent;
		subL->_right = parent;
		parent->_parent = subL;

		if (ppNode == nullptr)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = subL;
				subL->_parent = ppNode;
			}
			else
			{
				ppNode->_right = subL;
				subL->_parent = ppNode;
			}
		}
	}
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		if (subRL)
		{
			subRL->_parent = parent;
		}

		Node* ppNode = parent->_parent;
		subR->_left = parent;
		parent->_parent = subR;

		if (ppNode == nullptr)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = subR;
			}
			else
			{
				ppNode->_right = subR;
			}
			subR->_parent = ppNode;
		}
	}
};
void TestRBTree1()
{
	int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14, 0,5,30,25,20,4,13,30,28,27 };  // 测试双旋平衡因子调节
	//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	RBTree<int, int> t1;
	for (auto e : a)
	{
		t1.insert(make_pair(e, e));
	}

	t1.Inorder();
	cout << "IsBalance:" << t1.IsBalance() << endl;
}

void TestRBTree2()
{
	size_t N = 1000;
	srand(time(0));
	RBTree<int, int> t1;
	for (size_t i = 0; i < N; ++i)
	{
		int x = rand();
		cout << "Insert:" << x << ":" << i << endl;
		t1.insert(make_pair(x, i));
	}
	cout << "IsBalance:" << t1.IsBalance() << endl;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
红黑树是一种自平衡的二叉搜索树,它在插入和删除节点时通过一系列的操作来保持树的平衡。下面是红黑树的一些原理: 1. 节点颜色:每个节点被标记为红色或黑色。 2. 根节点和叶子节点:根节点是黑色的,叶子节点(空节点)是黑色的。 3. 节点关系:如果一个节点是红色的,则它的子节点必须是黑色的。 4. 黑色节点数量:从根节点到任意叶子节点的路径上,包括根节点和叶子节点,黑色节点数量相同。 5. 插入操作:插入新节点时,首先按照二叉搜索树的规则进行插入,然后根据红黑树的规则进行调整,使得树保持平衡。 - 如果插入节点的父节点是黑色的,那么不需要做任何操作。 - 如果插入节点的父节点是红色的,那么需要进行调整。 - 如果插入节点的叔叔节点也是红色的,将父节点和叔叔节点都设置为黑色,祖父节点设置为红色,并以祖父节点作为新插入节点进行进一步调整。 - 如果插入节点的叔叔节点是黑色的,需要进行旋转操作来保持平衡。根据具体情况,可以进行左旋、右旋或双旋操作。 6. 删除操作:删除节点时,首先按照二叉搜索树的规则进行删除,然后根据红黑树的规则进行调整,使得树保持平衡。 - 如果待删除节点有两个子节点,需要找到它的后继节点(比待删除节点大的最小节点)来替代待删除节点,然后再删除后继节点。 - 如果待删除节点有一个子节点或没有子节点,直接删除即可。 - 如果删除的节点是红色的,不会破坏红黑树的性质,不需要进行调整。 - 如果删除的节点是黑色的,需要进行调整来保持平衡。根据具体情况,可以进行旋转操作和改变颜色。 通过这些原理和调整操作,红黑树可以保持平衡,并且具有较好的查找、插入和删除性能。它被广泛应用于各种数据结构和算法中。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜鸡爱玩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值