红黑树的实现和打印

1. 基本概念

1.1 性质和规则:

红黑树,顾名思义即所有的节点都是通过红黑两种颜色来标识的。保证了从根到任意叶子的任意路径,最长路径(节点红黑交错)<=最短路径(节点全黑)的2倍。

  • 红黑树的根节点必须是黑色的。
  • 红节点的孩子必须是黑色的,没有连续的红色节点(对黑节点没有要求,父节点和孩子节点都可以是黑色的)。
  • 任意节点的子路径上,黑色节点数相同
  • 所有叶子节点为黑色(NIL)。

1.1 红黑树实现原理

首先对于新增的节点必须是节点,然后根据uncle存在情况和颜色分成两种情况进行调整:

情况一:cur为红,parent为红,grandparent为黑,uncle存在且为红,则进行向上调整。

调整规则:将parent和uncle变黑,grandparent变红。若grandparent的父亲为黑或为空(根节点),则调整结束;否则继续向上调整直到根节点,最后把根节点变黑,程序结束。

在这里插入图片描述

情况二:cur为红,parent为红,grandparent为黑,uncle不存在或者uncle存在且为黑。这种情况又分为cur、parent、grandparent三者一条直线上和不在一条直线上。在一条直线上旋转一次,否则旋转两次。另外若直线向左,则右旋;直线向右,则左旋。


case1:若cur、parent、grandparent三个节点在一条直线上。

  • 若parent为grandparent的节点,uncle不存在,则以grandparent进行单旋,然后让parent变黑,grandparent变红:

在这里插入图片描述

  • 若parent为grandparent的节点,uncle存在且为黑。则以grandparent进行单旋,然后让parent变黑,grandparent变红:

在这里插入图片描述

  • 若parent为grandparent的节点,则进行单旋。调整过程和上面两种类似。

case2:若cur、parent、grandparent三个节点在一条折线上。

  • 若parent为grandparent的节点,uncle不存在,则先以parent进行单旋,然后以grandparent进行单旋,让cur变黑,grandparent变红:

在这里插入图片描述

  • 若parent为grandparent的节点,uncle存在且为黑,则先以parent进行单旋,然后以grandparent进行单旋,让cur变黑,grandparent变红:

在这里插入图片描述

  • 若parent为grandparent的节点,则先以parent进行单旋,然后以grandparent进行单旋。调整过程和上面两种类似。

2. 代码实现

2.1 红黑树节点定义

enum Colour {
	Red,
	Black,
};

template<class K, class V>
struct RBNode {
	RBNode<K, V>* _left;
	RBNode<K, V>* _right;
	RBNode<K, V>* _parent;

	std::pair<K, V> _kv;
	Colour _colour;
	
	RBNode(const std::pair<K, V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
	{}
};

2.2 红黑树插入代码

关于左旋和右旋参看平衡二叉树

template<class K, class V>
class RBTree 
{
	typedef RBNode<K, V> Node;
public:
	RBTree()
		:_root(nullptr)
	{}
	bool Insert(const std::pair<K, V>& kv)
	{
		Node* newnode = new Node(kv);
		if (_root == nullptr)
		{
			_root = newnode;
			_root->_colour = Black;
			return true;
		}
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur) // 找插入的位置
		{
			if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;  //找不到返回,找到了执行插入操作;等于的情况有冗余,不执行插入
			}
		}

		cur = newnode;
		cur->_colour = Red; //插入红色的节点

		if (parent->_kv.first > kv.first)
		{
			parent->_left = newnode;
			newnode->_parent = parent;
		}
		else  //不存在等于情况,等于上面已经返回了
		{
			parent->_right = newnode;
			newnode->_parent = parent;
		}
		// 调整部分
		while (parent && parent->_colour == Red) //父节点存在且为红,有连续的红节点,继续向上调整
		{
			Node* grandparent = parent->_parent;
			if (grandparent->_left == parent)  // 先考虑父节点在左的情况
			{
				Node* uncle = grandparent->_right;
				if (uncle && uncle->_colour == Red)  //叔叔存在且为红,继续向上调整。
				{
					parent->_colour = Black;
					uncle->_colour = Black;
					grandparent->_colour = Red;
				}
				else //叔叔存在且为黑 或者 叔叔不存在
				{
					if (cur == parent->_left) //cur、parent、grandparent三个节点在一条直线上,右单旋
					{
						RotateR(grandparent);
						grandparent->_colour = Red;
						parent->_colour = Black;
					}
                    // cur、parent、grandparent三个节点在折直线上,先以parent进行左单旋,然后以grandparent进行右单旋
					else 
					{
						RotateL(parent);
						RotateR(grandparent);
						cur->_colour = Black;
						grandparent->_colour = Red;
					}
					break; //调整后
				}
			}
			else  //父节点在grandparent的右
			{
				Node* uncle = grandparent->_left;
				if (uncle && uncle->_colour == Red)  //叔叔存在且为红,继续向上调整。
				{
					parent->_colour = Black;
					uncle->_colour = Black;
					grandparent->_colour = Red;
				}
				else //叔叔存在且为黑 或者 叔叔不存在
				{
					if (cur == parent->_right) //cur、parent、grandparent三个节点在一条直线上,左单旋
					{
						RotateL(grandparent);
						grandparent->_colour = Red;
						parent->_colour = Black;
					}
                     // cur、parent、grandparent三个节点在折直线上,先以parent进行右单旋,然后以grandparent进行左单旋
					else
					{
						RotateR(parent);
						RotateL(grandparent);
						cur->_colour = Black;
						grandparent->_colour = Red;
					}
					break; 
				}

			}
			_root->_colour = Black;  //将根节点置黑
			return true;
		}
	}
    
	void RotateR(Node* parent); //左旋和右旋
	void RotateL(Node* parent);

private:
	Node* _root;
};

2.3 使用Graphviz打印红黑树

void PrintSingleNode(FILE* pFile, RBNode<int, int>* root)
{
	if (root->_colour == Black)
	{
		fprintf(pFile, "\tnode[shape=record,style=\"rounded, filled\",color=black,fontcolor=white, weight = 20];\n");
	}
	else
	{
		fprintf(pFile, "\tnode[shape=record,style=\"rounded, filled\",color=red,fontcolor=white,  weight = 20];\n");
	}
	fprintf(pFile, "\t%d[label=\"<f0> | <f1> %d | <f2> \", labelfontsize = 20];\n", root->_kv.second, root->_kv.second);
}

void WriteTree2File(FILE* pFile, RBNode<int, int>* root)
{
	if (nullptr == root) return;
	if (root->_parent == nullptr)
	{
		PrintSingleNode(pFile, root);
	}

	if (root->_left != nullptr)
	{
		PrintSingleNode(pFile, root->_left);
		fprintf(pFile, "\t%d:f0:sw->%d:f1;\n", root->_kv.second, root->_left->_kv.second);
	}
	if (root->_right != nullptr)
	{
		PrintSingleNode(pFile, root->_right);
		fprintf(pFile, "\t%d:f2:se->%d:f1;\n", root->_kv.second, root->_right->_kv.second);
	}
	WriteTree2File(pFile, root->_left);
	WriteTree2File(pFile, root->_right);
}
int main()
{
	RBTree<int, int> t;
	int a[] = { 20,30,35,14,26, 24 ,28, 22 };
	for (auto e : a)
	{
		t.Insert(std::make_pair(e, e));
	}
	FILE* fp;
	fp = fopen("RBTree.dot", "w+");
	fprintf(fp, "digraph G{\n");
	WriteTree2File(fp, t.returenROOT());
	fprintf(fp, "}");
	fclose(fp);

	system("dot -Tpng -O RBTree.dot"); 
	system("RBTree.dot.png");
	return 0;
}

结果

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值