当前位置: 首页 > news >正文

【C++】红黑树详解

本文是小编巩固自身而作,如有错误,欢迎指出!


目录

一、红黑树的概念

二、红黑树的基本规则

三、红黑树的实现

3.1红黑树的基本结构

3.2红黑树的调整方法

(1)parent为黑

(2)uncle为红

(3)uncle不存在,或者uncle为黑色

(4)uncle不存在或为黑,且c不在g和p的一条线上

3.3红黑树平衡检测等功能


一、红黑树的概念

红⿊树是⼀棵⼆叉搜索树,他的每个结点增加⼀个存储位来表⽰结点的颜⾊,可以是红⾊或者⿊⾊。通过对任何⼀条从根到叶⼦的路径上各个结点的颜⾊进⾏约束红⿊树确保没有⼀条路径会⽐其他路 径⻓出2倍,因⽽是接近平衡的。

二、红黑树的基本规则

1. 每个结点不是红色就是黑色

2. 根结点是黑色的

3. 如果⼀个结点是红色的,则它的两个孩子结点必须是黑色的,也就是说任意⼀条路径不会有连续的 红色结点

4. 对于任意⼀个结点,从该结点到其所有NULL结点的简单路径上,均包含相同数量的黑色结点

根据上述规则,也就实现了我们提到的红黑树的关键概念红⿊树确保没有⼀条路径会⽐其他路 径⻓出2倍

三、红黑树的实现

3.1红黑树的基本结构

和普通的key/value二叉树相比,红黑树在结构上会多出一个颜色选项。

// 枚举值表⽰颜⾊ 
enum Colour
{RED,BLACK
};
// 这⾥我们默认按key/value结构实现 
template<class K, class V>
struct RBTreeNode
{// 这⾥更新控制平衡也要加⼊parent指针 pair<K, V> _kv;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;Colour _col;RBTreeNode(const pair<K, V>& kv):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr){}
};
template<class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public:
private:Node* _root = nullptr;
};

3.2红黑树的调整方法

通过前面的四大规则我们就可以知道,如果要对红黑树调整,一定是一个复杂的过程,不仅要考虑两边的高度,还要考虑节点的颜色。

我们首先思考一下,红黑树新插入的节点应该是什么颜色

很显然,为了符合上述的四大规则,只能是红色。为什么呢?

因为,插入黑色必然会导致当前路径上的黑色节点数量和其他路径不同了。(插入都是找找到NULL位置插入)

但是如果在红色节点后插入红色节点,不也违反规则三、连续出现了红色节点了吗?因此,我们在插入后,就涉及到一个新的问题——变色

下面我们一个个分析,不同情况下的调整方法。

首先,我们导入一个概念

下图中假设我们把新增结点标识为c(cur),c的⽗亲标识为p(parent),p的⽗亲标识为 g(grandfather),p的兄弟标识为u(uncle)

(1)parent为黑

当parent为黑色时,不用做额外的调整,直接插入即可。

(2)uncle为红

在上图所示的情况下我们只进行变色操作就可以了。

  1. P变黑
  2. G变红
  3. U变黑
         Node* uncle = grandfather->_right;//叔叔存在且为红if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;//继续向上处理cur = grandfather;parent = cur->_parent;}

(3)uncle不存在,或者uncle为黑色

当parent和uncle都存在且不同色的时候,我们发现,如果只是简单的变色已经不足以满足上述规则了,因此我们在这一类情况的时候,我们采用的方法是旋转+变色

  1. P变黑
  2. G变红
  3. 单旋转

而关于旋转,我们之前在AVL树的章节进行了详细解释,对其不熟悉的同学可以看看以下链接

AVL树详解https://blog.csdn.net/2401_85487070/article/details/152172361?fromshare=blogdetail&sharetype=blogdetail&sharerId=152172361&sharerefer=PC&sharesource=2401_85487070&sharefrom=from_link

else//叔叔不存在或叔叔为黑,旋转+变色{if (cur == parent->_left){//     g//  p    u//c //右单旋RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//     g//  p     u//    c // 左右单旋RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}

(4)uncle不存在或为黑,且c不在g和p的一条线上

就如同我们之前学习的AVL树的双旋情况相似,红黑树与其最大的区别就在于红黑树在调整完一层后,因为颜色的变化需要继续向上不断调整。

else//grandfather->_right==parent{//   g// u   pNode* uncle = grandfather->_left;// 叔叔存在且为红,变色即可if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续往上处理cur = grandfather;parent = cur->_parent;}else // 叔叔不存在,或者存在且为黑{// 情况二:叔叔不存在或者存在且为黑// 旋转+变色//   g// u   p//       cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else//双旋+变色{	//    g// u     p//     cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}

3.3红黑树平衡检测等功能

下面给大家带来计数,求高,验证平衡的函数,原理和先前的AVL树相似,不过多赘述

求节点数

int _Size(Node* root){return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;}

求高度

int _Height(Node* root){if (root == nullptr)return 0;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;}

验证是否平衡

bool IsBalance(){if (_root == nullptr)return true;if (_root->_col == RED)return false;// 黑色节点数量参考值Node* leftMost = _root;int blackRef = 0;while (leftMost){if (leftMost->_col == BLACK)++blackRef;leftMost = leftMost->_left;}return Check(_root, 0, blackRef);}
bool Check(Node* cur, int blackNum, const int blackNumRef){if (cur == nullptr){if (blackNum != blackNumRef){cout << "黑色节点的数量不相等" << endl;return false;}return true;}if (cur->_col == RED && cur->_parent && cur->_parent->_col == RED){cout << cur->_kv.first << "->" << "连续的红色节点" << endl;return false;}if (cur->_col == BLACK)++blackNum;return Check(cur->_left, blackNum, blackNumRef)&& Check(cur->_right, blackNum, blackNumRef);}

四、完整代码及其测试

.h文件

#pragma once
#include<iostream>
using namespace std;enum Colour
{RED, BLACK
};template<class K,class V>
struct BRTreeNode
{pair<K, V> _kv;BRTreeNode<K, V>* _left;BRTreeNode<K, V>* _right;BRTreeNode<K, V>* _parent;Colour _col;BRTreeNode(const pair<K, V>& kv):_kv(kv),_left(nullptr),_right(nullptr),_parent(nullptr){}};template<class K, class V>
class BRTree
{typedef BRTreeNode<K, V> Node;
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 < kv.first){parent->_right = cur;}if (parent->_kv.first > kv.first){parent->_left = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (!grandfather) break; // 如果grandfather为空,退出循环if (grandfather->_left == parent){Node* uncle = grandfather->_right;//叔叔存在且为红if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;grandfather->_col = RED;//继续向上处理cur = grandfather;parent = cur->_parent;}else//叔叔不存在或叔叔为黑,旋转+变色{if (cur == parent->_left){//     g//  p    u//c //右单旋RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//     g//  p     u//    c // 左右单旋RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else//grandfather->_right==parent{//   g// u   pNode* uncle = grandfather->_left;// 叔叔存在且为红,变色即可if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续往上处理cur = grandfather;parent = cur->_parent;}else // 叔叔不存在,或者存在且为黑{// 情况二:叔叔不存在或者存在且为黑// 旋转+变色//   g// u   p//       cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else//双旋+变色{	//    g// u     p//     cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;}void InOrder(){_InOrder(_root);}//bool IsBalance() {// if (_root == nullptr) return true;// if (_root->_col == RED) return false;// // 计算参考值(最左路径黑节点数)// Node* leftMost = _root;// int blackRef = 0;// while (leftMost) {//	 if (leftMost->_col == BLACK) ++blackRef;//	 leftMost = leftMost->_left;// }// return Check(_root, 0, blackRef);//}bool IsBalance(){if (_root == nullptr)return true;if (_root->_col == RED)return false;// 黑色节点数量参考值Node* leftMost = _root;int blackRef = 0;while (leftMost){if (leftMost->_col == BLACK)++blackRef;leftMost = leftMost->_left;}return Check(_root, 0, blackRef);}int Height(){return _Height(_root);}int Size(){return _Size(_root);}Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_kv.first < key){cur = cur->_right;}else if (cur->_kv.first > key){cur = cur->_left;}else{return cur;}}return nullptr;}private://bool Check(Node* cur, int blackNum, const int blackRef) {//	if (cur == nullptr)//		return blackNum == blackRef;  // 修正1:正确判断路径终点//	// 修正2:避免根节点访问_parent//	if (cur != _root && cur->_col == RED && cur->_parent->_col == RED) {//		cout << "连续红节点:" << cur->_kv.first << endl;//		return false;//	}//	// 更新黑节点计数//	int newBlackNum = (cur->_col == BLACK) ? blackNum + 1 : blackNum;//	// 修正3:独立传递左右子树计数//	int leftCount = newBlackNum;//	int rightCount = newBlackNum;//	bool leftValid = Check(cur->_left, leftCount, blackRef);//	bool rightValid = Check(cur->_right, rightCount, blackRef);//	return leftValid && rightValid;//}//	 void _InOrder(Node* root)//	 {//		 if (root == nullptr)//			 return;//		 _InOrder(root->_left);//		 cout << root->_kv.first<<" ";//		 _InOrder(root->_right);//	 }int _Size(Node* root){return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;}int _Height(Node* root){if (root == nullptr)return 0;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;}bool Check(Node* cur, int blackNum, const int blackNumRef){if (cur == nullptr){if (blackNum != blackNumRef){cout << "黑色节点的数量不相等" << endl;return false;}return true;}if (cur->_col == RED && cur->_parent && cur->_parent->_col == RED){cout << cur->_kv.first << "->" << "连续的红色节点" << endl;return false;}if (cur->_col == BLACK)++blackNum;return Check(cur->_left, blackNum, blackNumRef)&& Check(cur->_right, blackNum, blackNumRef);}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* parentParent = parent->_parent;subL->_right = parent;parent->_parent = subL;if (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (parentParent->_left == parent){parentParent->_left = subL;}else{parentParent->_right = subL;}subL->_parent = parentParent;}}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* parentParent = parent->_parent;subR->_left = parent;parent->_parent = subR;if (parent == _root){_root = subR;subR->_parent = nullptr;}else{if (parentParent->_left == parent){parentParent->_left = subR;}else{parentParent->_right = subR;}subR->_parent = parentParent;}}Node* _root = nullptr;};

测试文件

#define _CRT_SECURE_NO_WARNINGS
#include"BRTree.h"
#include<vector>void TestBRTree1()
{BRTree<int, int> t;// 常规的测试用例//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };// 特殊的带有双旋场景的测试用例int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };for (auto e : a){if (e == 14){int x = 0;}t.Insert({ e, e });//t.InOrder();//cout << "Insert:" << e << "->" << t.IsBalance() << endl;cout << t.IsBalance();}//t.InOrder();}
void TestBRTree2()
{const int N = 1000000;vector<int> v;v.reserve(N);srand(time(0));for (size_t i = 0; i < N; i++){v.push_back(rand() + i);}size_t begin2 = clock();BRTree<int, int> t;for (auto e : v){t.Insert(make_pair(e, e));}size_t end2 = clock();cout << "Insert:" << end2 - begin2 << endl;cout << t.IsBalance() << endl;cout << "Height:" << t.Height() << endl;cout << "Size:" << t.Size() << endl;size_t begin1 = clock();// 确定在的值/*for (auto e : v){t.Find(e);}*/// 随机值for (size_t i = 0; i < N; i++){t.Find((rand() + i));}size_t end1 = clock();cout << "Find:" << end1 - begin1 << endl;
}int main()
{TestBRTree2();
}

本次分享就到这里结束了,感谢阅读!

http://www.dtcms.com/a/472058.html

相关文章:

  • 专做网站网站备案怎么做超链接
  • 【异常处理——上】
  • 莱芜区网站内网安装wordpress
  • 肥乡企业做网站推广百度网盘网页版官网
  • 好看的网站颜色搭配做网站色弱可以吗
  • 网站建设资金报告wordpress 在线人数
  • 新手怎么学代码编程网站关键词优化的步骤和过程
  • 漏惹网站做知科网站
  • 长沙娱乐网站开发免费制作网页网站
  • 昂瑞微:射频前端的“破局者”,迈向中高端模组新纪元
  • 网站建设费会计分录网站建设背景及意义
  • 湘西网站建设花垣做网站用什么语言高效
  • 南京网站设计案例外贸网站设计师
  • 网站设计网资讯类网站开发文档
  • 简单大气的网站模板好听罕见绝不重名的公司名称
  • 一个空间做2个网站吗深圳微信公众号
  • 昆明网站设计制作公司哪家电商平台质量最好
  • 【数据结构与算法-Day 41】分治之王:深入解析稳定高效的归并排序
  • 网站备案接入商变更企业为何要建设网站
  • 多产品的网站怎么做seo七牛wordpress后台无法登录
  • 为什么说“只会写页面的前端,永远成不了高级工程师“?
  • 个人网站可以做淘宝店铺名翠峦网站建设
  • JDBC与MyBatis核心攻略
  • 网站培训费用宿迁网站建设宿迁
  • 做的成功的地方网站中国外贸网站排名
  • 会宁县建设局网站一个thinkphp搭建的微网站
  • 自注意力机制、交叉注意力、向量填充、填充掩码、位置编码、多头注意力、层归一化的基本原理
  • 全网网站建设维护网站建设信用卡分期手续费
  • 网站建设的活动方案apache wordpress 优化
  • 微信 网站应用开发天河区进一步