手把手教你实现红黑树

目录

一.红黑树介绍与优势

二.红黑树的特性

①所有节点不是黑色就是红色

②根节点为黑色

③红色节点的左右孩子节点必须为黑色

④每一条路径均含有相同的黑色节点数

⑤叶子节点为黑色

三.红黑树实现原理

(一).插入节点颜色选择

(二).插入后,父节点是黑色

(三).插入后,父节点是红色

(四).叔叔节点为红色

①父节点、叔叔节点变黑,祖父节点变红

②以祖父节点为“新插入节点”继续向上判断

(五).叔叔节点为黑色、没有叔叔节点

①当前节点为父节点左子树,且在根节点左侧

②当前节点为父节点右子树,且在根节点右侧

③当前节点为父节点右子树,且在根节点左侧

④当前节点为父节点左子树,且在根节点右侧

四.思维导图

五.实现代码


一.红黑树介绍与优势

有兴趣的小伙伴可以先看看这篇文章:手把手教你实现AVL树、平衡二叉树

红黑树是在1972年由Rudolf Bayer发明的。

其特点是从根到叶子节点的所有路径中,最长路径不大于最短路径的2倍。

可以说红黑树是AVL树的plus版,因此,红黑树查找时间与AVL树相同,都为O(log N)。

同时,红黑树没有做到AVL树那种绝对平衡,因此其旋转次数少于AVL树。当数据量大时,其效率要优于AVL树。

正因如此,在实际应用中往往采取红黑树而非AVL树。

二.红黑树的特性

可以说,红黑树的特性正是它的精华与结构依据。因此,想要学懂红黑树,熟练掌握特性是必不可少的一环。

红黑树特性:

①所有节点不是黑色就是红色

②根节点为黑色

③红色节点的左右孩子节点必须为黑色

意思就是不能出现两个红色节点相连的情况。

④每一条路径均含有相同的黑色节点数

从根节点到叶子节点的每一条路径中,黑色节点数量均相同。

⑤叶子节点为黑色

三.红黑树实现原理

实现红黑树,我们需要时刻谨记它的特性,尤其是特性3和4。

(一).插入节点颜色选择

当插入节点时,如果是插入根节点,那么节点颜色为黑色即可,然后插入完成。

如果是孩子节点,那么先将颜色变为红色,为什么?

如果插入节点颜色是黑色,那么势必打破特性4:所有路径黑色节点数相同。

而如果插入红色,可能会打破特性3:不能有相连的红色节点。

相比于特性4,打破特性3的代价更小:插入黑色势必打破特性4,插入红色则有可能打破特性3。

(二).插入后,父节点是黑色

当插入孩子节点后,如果父节点是黑色,那么此时没有打破任何一条特性,任务完成。

(三).插入后,父节点是红色

如果父节点是红色,那么问题就出来了,此时打破了特性3:不能有相连的红色节点

此时需要分类讨论,而依据就是叔叔节点

需要考虑的重点是达成每条路径下均含有相同数目的黑色节点(特性4)且没有相连的红色节点(特性3)。

由此,分成(四)、(五)两种情况

(四).叔叔节点为红色

 将父节点与叔叔节点变为黑色,祖父节点变为红色,然后以祖父节点为“新插入节点”继续向上判断。

①父节点、叔叔节点变黑,祖父节点变红

此时,以祖父为根的树已经满足红黑树的特性要求,因此需要以祖父为出发点继续向上判断,此时祖父的颜色是否依旧符合红黑树,相当于将祖父节点作为“新插入节点”,重新判断

②以祖父节点为“新插入节点”继续向上判断

(五).叔叔节点为黑色、没有叔叔节点

需要注意,此时节点并不是真正新插入的节点,因为新插入的节点父节点为红色,说明祖父节点为黑,如果此时叔叔节点为黑色,那么到父节点的这条路径的黑色节点数量就比到叔叔节点的黑色数量少一,说明插入前就已经不是红黑树,这是不成立的。因此,该情况只能出现在祖父节点向上判断时。

这种情况时,单纯的变色已经不能解决问题了,只能靠旋转+变色来解决。

这里不再具体讲解旋转的过程,与AVL树一致,可以参考这篇博客:手把手教你实现AVL树、平衡二叉树

同时,经过旋转+变色后,此时整颗树已经满足红黑树全部特性,即插入完成。 

这时情况可以分成四类,分别对应不同的旋转策略,

前两种均为单旋转,后两种均为双旋转:

①当前节点为父节点左子树,且在根节点左侧

父节点右旋转,父节点变黑,祖父变红。

        步骤一、父节点右旋转

         步骤二、变色

②当前节点为父节点右子树,且在根节点右侧

父节点左旋转,父节点变黑,祖父节点变红

此处类比情况① ,将父节点右旋即可,不再具体演示 

③当前节点为父节点右子树,且在根节点左侧

cur节点左旋转后右旋转,插入节点变黑,祖父节点变红

         步骤一、cur节点左旋转

        步骤二、cur节点右旋转

          步骤三、变色

④当前节点为父节点左子树,且在根节点右侧

cur节点右旋转后左旋转,插入节点变黑,祖父节点变红

此处类比情况③,cur节点右旋后左旋即可,不再具体演示 

四.思维导图

五.实现代码

#pragma once
using namespace std;
#include<iostream>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
#define BLACK false//用bool定义红黑颜色
#define RED true
template<class V>
class rb_tree_node//红黑树节点
{
public:
	rb_tree_node(V val)
		:_val(val)
	{}

	bool _col = RED;
	rb_tree_node* right = nullptr;
	rb_tree_node* left = nullptr;
	rb_tree_node* parent = nullptr;
	V _val;
};

template<class V>//红黑树
class rb_tree
{
	typedef rb_tree_node<V> Node;
public:
	void inorder()//中序遍历
	{
		_inorder(_root);//调用函数
	}

	bool Is_RB_Tree()//判断是否为红黑树
	{
		int blackNum = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK) blackNum++;
			cur = cur->left;
		}
		return _is_rb_tree(_root, 0, blackNum);//调用判断函数
	}

	bool insert(V val);
	
private:
	void _inorder(Node* node)//中序遍历函数
	{
		if (node == nullptr) return;
		_inorder(node->left);
		cout << node->_val << " ";
		_inorder(node->right);
	}
	bool _is_rb_tree(Node* node, int blackNow, int blackNum);//判断函数

	void Rotate_R(Node* cur);
	void Rotate_L(Node* cur);
	Node* _root = nullptr;
};

template<class V>
void rb_tree<V>::Rotate_R(Node* cur)//右旋转
{
	Node* parent = cur->parent, *node_r = cur->right, *grandfather = parent->parent;

	if (node_r)
	{
		node_r->parent = parent;
	}
	parent->left = node_r;

	cur->right = parent;
	cur->parent = grandfather;

	if (grandfather)
	{
		if (grandfather->left == parent) grandfather->left = cur;
		else grandfather->right = cur;
	}
	else _root = cur;
	parent->parent = cur;
}

template<class V>
void rb_tree<V>::Rotate_L(Node* cur)//左旋转
{
	Node* parent = cur->parent, *node_l = cur->left, *grandfather = parent->parent;

	if (node_l)
	{
		node_l->parent = parent;
	}
	parent->right = node_l;

	parent->parent = cur;
	cur->left = parent;

	cur->parent = grandfather;
	if (grandfather)
	{
		if (grandfather->left == parent) grandfather->left = cur;
		else grandfather->right = cur;
	}
	else _root = cur;
}

template<class V>
bool rb_tree<V>::_is_rb_tree(Node* node, int blackNow, int blackNum)
{
	if (node == nullptr) return true;
	if (node->_col == BLACK) blackNow++;
	if (node->left == nullptr && node->right == nullptr)
	{
		if (node->_col == RED && node->parent->_col == RED) return false;
		return blackNow == blackNum;
	}

	if (node != _root && node->_col == RED && node->parent->_col == RED) return false;

	return _is_rb_tree(node->left, blackNow, blackNum) &&
	_is_rb_tree(node->right, blackNow, blackNum);

}


template<class V>
bool rb_tree<V>::insert(V val)
{
	if (_root == nullptr)//插入为根节点
	{
		_root = new Node(val);
		_root->_col = BLACK;
		return true;
	}
	Node* parent = _root->parent, * cur = _root;
	while (cur)
	{
		if (cur->_val == val) return false;//已存在该节点
		if (cur->_val > val)
		{
			parent = cur;
			cur = cur->left;
		}
		else
		{
			parent = cur;
			cur = cur->right;
		}
	}
    //插入节点
	cur = new Node(val);
	cur->parent = parent;
	if (val < parent->_val)
	{
		parent->left = cur;
	}
	else
	{
		parent->right = cur;
	}
    //根据颜色判断是否红黑树
	while (parent && parent->_col == RED)//父节点为红
	{
		Node* grandfather = parent->parent;
		assert(grandfather);//进入循环,说明一定有祖父节点
		assert(grandfather->_col == BLACK);//进入循环,说明祖父节点一定是黑色
		Node* uncle = nullptr;
		if (parent == grandfather->left) uncle = grandfather->right;
		else uncle = grandfather->left;
		if (parent == grandfather->left)//左
		{
			if (uncle && uncle->_col == RED)//变色 + 向上调整
			{
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;
				cur = grandfather;
				parent = cur->parent;
			}
			else if (uncle == nullptr || uncle->_col == BLACK)//叔叔为空,或叔叔为黑
			{
				if (cur == parent->left)//cur为左子树,右单旋
				{
					Rotate_R(parent);
					grandfather->_col = RED;
					parent->_col = BLACK;
				}
				else//cur为右子树,左+右旋+变色
				{
					Rotate_L(cur);
					Rotate_R(cur);
					grandfather->_col = RED;
					cur->_col = BLACK;
				}
				break;
			}
			else
			{
				assert(false);
			}
		}
		else//右
		{
			if (uncle && uncle->_col == RED)//变色 + 向上调整
			{
				parent->_col = uncle->_col = BLACK;
				grandfather->_col = RED;
				cur = grandfather;
				parent = cur->parent;
			}
			else if (uncle == nullptr || uncle->_col == BLACK)//叔叔为空,或叔叔为黑
			{
				if (cur == parent->right)//cur为右子树,左单旋
				{
					Rotate_L(parent);
					grandfather->_col = RED;
					parent->_col = BLACK;
				}
				else//cur为左子树,右+左旋+变色
				{
					Rotate_R(cur);
					Rotate_L(cur);
					grandfather->_col = RED;
					cur->_col = BLACK;
				}
				break;
			}
			else
			{
				assert(false);
			}
		}
	}
	_root->_col = BLACK;//根节点一定为黑色
	return true;
}

人类精神必须置于技术之上——Albert Einstein


如有错误,敬请斧正

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

就要 宅在家

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

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

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

打赏作者

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

抵扣说明:

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

余额充值