【C++】红黑树的调整

红黑树(Red-Black Tree)是一种自平衡的二叉搜索树,通过颜色约束 + 旋转 + 变色 来保证树的高度始终为 O(logn)。插入和删除后可能破坏红黑性质,需进行调整(fixup)

红黑树要满足以下几个条件:

  1. 根节点为黑色
  2. 红色节点的孩子必须是黑色节点
  3. 任意一条路径上的黑色节点数量相等
  4. 最长路径上的节点数量不超过最短路径节点数量的两倍

接下来就来详细介绍红黑树旋转 + 变色的几种情况。

一、叔节点 u 是 红色

cpp 复制代码
       g(B)                g(R)
      /    \              /    \
   p(R)    u(R)   →    p(B)    u(B)
   /                   /
 z(R)               z(R)

这时只需要将grandpa变为红色,parent和uncle介蒂安变为黑色。由于grandpa节点变成红色,此时需要继续向上检查。

二、叔节点为黑色或为空

在这种情况下就会变得复杂得多,涉及到旋转和变色。

2.1、RR型

cpp 复制代码
   g(B)                     p(B)
  /    \                   /    \
u(B)  p(R)      →       g(R)   z(R)
        \               /
       z(R)          u(B)

叔节点为黑色或为空时,在RR型中需要将grandpa节点进行左旋,并且将parent节点变成黑色grandpa节点变成红色。注意这里由于parent节点已经变成黑色,不需要再向上进行调整。

2.2、RL型

cpp 复制代码
   g(B)                g(B)                   z(B)
  /    \              /    \                 /    \
u(B)  p(R)   →      u(B)  z(R)    →       g(R)   p(R)
      /                       \            /
    z(R)                     p(R)       u(B)

叔节点为黑色或为空时,在RL型中需要先将parent节点进行右旋转化为RR型,再对grandpa节点进行左旋。同时z节点变成黑色,grandpa节点变成红色。

2.3、LL型

cpp 复制代码
       g(B)                 p(B)
      /    \               /    \
   p(R)    u(B)   →     z(R)   g(R)
   /                             \
 z(R)                           u(B)

叔节点为黑色或为空时,在LL型中需要将grandpa节点进行右旋,并且将parent节点变成黑色grandpa节点变成红色。注意这里由于parent节点已经变成黑色,不需要再向上进行调整。

2.4、LR型

cpp 复制代码
       g(B)                  g(B)                 z(B)
      /    \                /    \               /    \
   p(R)    u(B)   →      z(R)    u(B)   →    p(R)   g(R)
      \                  /                          \
     z(R)             p(R)                        u(B)

叔节点为黑色或为空时,在LR型中需要先将parent节点进行左旋转化为LL型,再对grandpa节点进行右旋旋。同时z节点变成黑色,grandpa节点变成红色。

三、旋转与变色总结

叔叔颜色 当前节点位置 操作
Case 1 红色 任意 变色(p, u → 黑;g → 红),继续向上
Case 2 黑色 外侧(LL/RR) 单旋 + 变色(p 与 g 交换颜色)
Case 3 黑色 内侧(LR/RL) 双旋 + 变色(先转外侧,再单旋)

四、代码实现

cpp 复制代码
#include<iostream>

using namespace std;
enum color
{
	RED, BLACK
};

template<class T>
class RBNode {
public:
	RBNode(const T& val=T()):
		date(val),
		_parent(nullptr),
		_left(nullptr),
		_right(nullptr),
		col(RED)
	{

	}
	T date;
	RBNode<T>* _parent;
	RBNode<T>* _left;
	RBNode<T>* _right;
	color col;
};

template<class T>
class RBTree {
public:
	typedef RBNode<T> Node;
	RBTree():_root(nullptr){}



	void insert(const T& x)
	{
		//插入节点
		Node* cur = _root, * parent = nullptr, * nn = new Node(x);
		while (cur)
		{
			parent = cur;
			if (x < cur->date) { cur = cur->_left; }
			else
			{
				cur = cur->_right;
			}
		}
		if (parent == nullptr)//如果是空树
		{
			_root = nn;
		}
		else
		{
			nn->_parent = parent;
			if (nn->date < parent->date) parent->_left = nn;
			else
			{
				parent->_right = nn;
			}
			cur = nn;
		}

		//修善红黑树
		while (parent && parent->col == cur->col && cur->col == RED)
		{
			Node* uncle;
			Node* grandpa = parent->_parent;
			if (grandpa)
			{
				if (parent == grandpa->_left)
				{
					uncle = grandpa->_right;
					if (uncle && uncle->col == RED)//uncle是红色
					{
						grandpa->col = RED;
						parent->col = uncle->col = BLACK;
						cur = grandpa;
						parent = cur->_parent;
						if (parent) grandpa = parent->_parent;
						else
						{
							grandpa = nullptr;
						}
					}
					else
					{
						if (cur == parent->_left)
						{
							rotateRight(grandpa);
							parent->col = BLACK;
							grandpa->col = RED;
						}
						else
						{
							rotateLeft(parent);
							rotateRight(grandpa);
							cur->col = BLACK;
							grandpa->col = RED;
						}
						break;
					}
				}
				else
				{
					uncle = grandpa->_left;
					if (uncle && uncle->col == RED)//uncle是红色
					{
						grandpa->col = RED;
						grandpa->col = RED;
						parent->col = uncle->col = BLACK;
						cur = grandpa;
						parent = cur->_parent;
						if (parent) grandpa = parent->_parent;
						else
						{
							grandpa = nullptr;
						}
					}
					else
					{
						if (cur == parent->_right)
						{
							rotateLeft(grandpa);
							parent->col = BLACK;
							grandpa->col = RED;
						}
						else
						{
							rotateRight(parent);
							rotateLeft(grandpa);
							cur->col = BLACK;
							grandpa->col = RED;
						}
						break;
					}
				}

			}
		}
		_root->col = BLACK;//根节点常置为black
	}

	void rotateLeft(Node* node)
	{
		Node* parent = node->_parent, * rc = node->_right, * rlc = node->_right->_left;
		node->_right = rlc;
		if (rlc) rlc->_parent = node;
		rc->_left =node;
		node->_parent = rc;
		if (parent == nullptr)
		{
			rc->_parent = nullptr;
			_root = rc;
		}
		else
		{
			rc->_parent = parent;
			if (parent->_left == node)
			{
				parent->_left = rc;
			}
			else
			{
				parent->_right = rc;
			}
		}
	}

	void rotateRight(Node* node)
	{
		//      node
		//   lc
		//      lrc
		Node* parent = node->_parent, * lc = node->_left, * lrc = node->_left->_right;
		node->_left = lrc;
		if (lrc) lrc->_parent = node;
		lc->_right = node;
		node->_parent = lc;
		if (parent == nullptr)
		{
			lc->_parent = nullptr;
			_root = lc;
		}
		else
		{
			if (parent->_left == node)
			{
				parent->_left = lc;
				lc->_parent = parent;
			}
			else
			{
				parent->_right = lc;
				lc->_parent = parent;
			}
		}
	}

	void InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		InOrder(root->_left);
		//cout << root->_date << " ";
		cout << root->_date <<"##";

		InOrder(root->_right);
	}

	void PreOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		cout << root->_date << "##";
		PreOrder(root->_left);
		PreOrder(root->_right);
	}
	Node* getRoot()
	{
		return _root;
	}
	bool isRBTree()
	{
		return isRBTree(_root, 0);
	}


private:
	bool isRBTree(Node* node, int cb)
	{
		if (node == nullptr)
		{
			if (cb == countBlack()) return true;
			else
			{
				return false;
			}
		}
		if (node->col == RED && node->_parent->col == RED)
		{
			return false;
		}
		int i = 0;
		if (node->col == BLACK) i++;
		return isRBTree(node->_left, cb + i) && isRBTree(node->_right, cb + i);
	}
	int countBlack()
	{
		int count = 0;
		Node* tmp = _root;
		while (tmp)
		{
			if (tmp->col == BLACK)
			{
				count++;
			}
			tmp = tmp->_right;
		}
		return count;
	}
	Node* _root;

};
相关推荐
We་ct3 分钟前
深度剖析浏览器跨域问题
开发语言·前端·浏览器·跨域·cors·同源·浏览器跨域
风筝在晴天搁浅3 分钟前
字节高频题 小于n的最大数
算法
LabVIEW开发6 分钟前
LabVIEW水力机组空蚀在线监测
算法·labview·labview知识·labview功能·labview程序
AI科技星11 分钟前
科幻艺术书本封面:《全域数学》第一部·数术本源 第三卷 代数原本(P95-141)完整五级目录【乖乖数学】
算法·机器学习·数学建模·数据挖掘·量子计算
skywalk816311 分钟前
在考虑双轨制,即在中文语法的基础上,加上数学公式的支持,这样像很多计算将更加简单方便,就像现在的小学数学课本里面一样,比如:定x=2*x + 1
开发语言
风筝在晴天搁浅13 分钟前
LeetCode 92.反转链表Ⅱ
算法·leetcode·链表
小书房14 分钟前
Kotlin的by
android·开发语言·kotlin·委托·by
王老师青少年编程27 分钟前
csp信奥赛C++高频考点专项训练之贪心算法 --【贪心与二分判定】:数列分段 Section II
c++·算法·贪心·csp·信奥赛·二分判定·数列分段 section ii
zh_xuan29 分钟前
libcurl调用https接口
c++·libcurl
就叫飞六吧31 分钟前
QT写一个桌面程序exe并动态打包基本流程(c++)
开发语言·c++