红黑树简析
创始人
2024-05-23 17:27:12
0

一.   概念

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


 二.   性质

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

2. 根节点是黑色的

3. 如果一个节点是红色的,则它的两个孩子结点是黑色的

4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点

5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

由于从一个节点起的每条路径中的黑色节点数目相同,因此最短为全黑,同时由于红节点不连续,因此最长为一半红一半黑,因此不会超出两倍


三.   节点定义

三叉链,key-value模型、额外的标明颜色的变量(颜色可以使用枚举常量)

同时,为了在插入时遵循第4条准则,我们将颜色变量的默认值设为红色

enum Colour
{RED,BLACK
};template
struct RBTreeNode
{RBTreeNode* _left;RBTreeNode* _right;RBTreeNode* _parent;pair _kv;Colour _col;RBTreeNode(const pair& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _col(RED), _kv(kv){}
};

四.   基本框架

template
struct RBTree
{typedef RBTreeNode Node;
public:RBTree():_root(nullptr){}
private:Node* _root;
};

五.   插入

首先,一开始与AVL树是一致的,都是通过比较key来寻找合适的位置进行插入

bool Insert(const pair& kv)
{if (!_root){_root = new Node(kv);_root->_col = BLACK;return false;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first == kv.first)return false;else if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else{parent = cur;cur = cur->_left;}}cur = new Node(kv);if (parent->_kv.first < kv.first)parent->_right = cur;elseparent->_left = cur;cur->_parent = parent;
}

之后,虽然我们插入红色节点会使得每条路径的黑节点数目相同,但还要注意在红节点连续时进行相应的调整

在调整时,依旧是从插入节点(cur)向上迭代来观察,而我们需要进行调整的情况为cur和parent的颜色都为红,主要分为这两种情况

同样,和AVL树一样,也有对称的情况,但操作方法也都是类似的,不多说了

而这两种情况,我们对子树4的情况进行细分,首先当节点1的右节点为红色时

 处理方法都是一样的,只需要将节点2、4的颜色改为黑色,同时,我们在改变结构时,为了减小对上面的影响,应该保证前后的路径中黑节点的数目相同,改色之前,黑节点数目为n+1,因此,我们还需要将节点1改为红色

而由于我们将节点1的颜色改为了红色,我们需要继续向上判断

while (parent&&parent->_col == RED)
{Node* par_parent = parent->_parent;Node* uncle = nullptr;if (parent == par_parent->_left)uncle = par_parent->_right;elseuncle = par_parent->_left;if (uncle && uncle->_col == RED){uncle->_col = BLACK;parent->_col = BLACK;par_parent->_col = RED;cur = par_parent;parent = cur->_parent;}
}

 

还有两种情况,是节点4不存在或节点4为黑色,这两种情况的处理情况相同,由于不需要对节点4进行处理,我们就简化一下

首先是情况1

 首先若是我们想要改变颜色,首先对于节点2,右侧路径黑节点为n个,因此左侧也应该为n个,所以节点3的颜色不能改变,因此我们只能改变节点2,这样对于节点1,两侧路径的黑节点数目就不可能相同了,因此,我们可以使用在AVL树中使用到的旋转,很容易看出,我们需要使用右旋(对称情况使用左旋)

 之后,便可以来改变颜色,首先,有两种方案,节点3改为黑色或节点1改为红色、节点2改为黑色,原本路径中黑节点数为n+1,这两种方案改变后都符合,但由于若是我们将节点2颜色改为黑色,与改变前类似,也就不需要继续向上判断了,因此采用第二种方法

else
{if (cur == parent->_left && parent == par_parent->_left){RotateR(par_parent);parent->_col = BLACK;par_parent->_col = RED;}else if (cur == parent->_right && parent == par_parent->_right){RotateL(par_parent);parent->_col = BLACK;par_parent->_col = RED;}break;
}

而左旋和右旋和AVL树是一样的 

void RotateL(Node* parent)
{Node* par_parent = parent->_parent;Node* right = parent->_right;Node* right_left = right->_left;if (par_parent){if (parent == par_parent->_left)par_parent->_left = right;elsepar_parent->_right = right;}else{_root = right;}right->_parent = par_parent;if (right_left)right_left->_parent = parent;parent->_right = right_left;right->_left = parent;parent->_parent = right;
}void RotateR(Node* parent)
{Node* par_parent = parent->_parent;Node* left = parent->_left;Node* left_right = left->_right;if (par_parent){if (parent == par_parent->_left)par_parent->_left = left;elsepar_parent->_right = left;}else{_root = left;}left->_parent = par_parent;if (left_right)left_right->_parent = parent;parent->_left = left_right;left->_right = parent;parent->_parent = left;
}

 

之后便是情况2

同样只是改变颜色无法实现平衡 

我们就需要进行双旋

之后就需要改变颜色

还是将节点3变黑,节点1变红,不需要向上调整

else
{if (cur == parent->_left && parent == par_parent->_left){RotateR(par_parent);parent->_col = BLACK;par_parent->_col = RED;}else if (cur == parent->_left && parent == par_parent->_right){RotateR(parent);RotateL(par_parent);cur->_col = BLACK;par_parent->_col = RED;}else if (cur == parent->_right && parent == par_parent->_right){RotateL(par_parent);parent->_col = BLACK;par_parent->_col = RED;}else{RotateL(parent);RotateR(par_parent);cur->_col = BLACK;par_parent->_col = RED;}break;
}

而在最后,头结点可能会被我们改变为红色,我们只需要将其手动变为黑就好了,不会对下面产生影响

总览

bool Insert(const pair& kv)
{if (!_root){_root = new Node(kv);_root->_col = BLACK;return false;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first == kv.first)return false;else if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else{parent = cur;cur = cur->_left;}}cur = new Node(kv);if (parent->_kv.first < kv.first)parent->_right = cur;elseparent->_left = cur;cur->_parent = parent;while (parent&&parent->_col == RED){Node* par_parent = parent->_parent;Node* uncle = nullptr;if (parent == par_parent->_left)uncle = par_parent->_right;elseuncle = par_parent->_left;if (uncle && uncle->_col == RED){uncle->_col = BLACK;parent->_col = BLACK;par_parent->_col = RED;cur = par_parent;parent = cur->_parent;}else{if (cur == parent->_left && parent == par_parent->_left){RotateR(par_parent);parent->_col = BLACK;par_parent->_col = RED;}else if (cur == parent->_left && parent == par_parent->_right){RotateR(parent);RotateL(par_parent);cur->_col = BLACK;par_parent->_col = RED;}else if (cur == parent->_right && parent == par_parent->_right){RotateL(par_parent);parent->_col = BLACK;par_parent->_col = RED;}else{RotateL(parent);RotateR(par_parent);cur->_col = BLACK;par_parent->_col = RED;}break;}}_root->_col = BLACK;return true;
}void RotateL(Node* parent)
{Node* par_parent = parent->_parent;Node* right = parent->_right;Node* right_left = right->_left;if (par_parent){if (parent == par_parent->_left)par_parent->_left = right;elsepar_parent->_right = right;}else{_root = right;}right->_parent = par_parent;if (right_left)right_left->_parent = parent;parent->_right = right_left;right->_left = parent;parent->_parent = right;
}void RotateR(Node* parent)
{Node* par_parent = parent->_parent;Node* left = parent->_left;Node* left_right = left->_right;if (par_parent){if (parent == par_parent->_left)par_parent->_left = left;elsepar_parent->_right = left;}else{_root = left;}left->_parent = par_parent;if (left_right)left_right->_parent = parent;parent->_left = left_right;left->_right = parent;parent->_parent = left;
}

六.   验证 

首先就是验证是否为搜索树,和AVL树的一致

void InOrder()
{_InOrder(_root);cout << endl;
}void _InOrder(Node* root)
{if (root == NULL)return;_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second <<':' << root->_col << "  ";_InOrder(root->_right);
}

之后,还需要验证三个问题:根节点是否为黑,路径中的黑节点数目是否相同、红节点是否不连续

首先第一点很好验证,若是验证第二点,我们可以先得到一条路径的黑节点,之后以它为基准,对每条路径进行判断,最后在遍历路径的时候顺便对红节点是否连续做一下判断

bool IsBalance()
{if (_root && _root->_col == RED){cout << "根节点不是黑色" << endl;return false;}int banchmark = 0;Node* left = _root;while (left){if (left->_col == BLACK)banchmark++;left = left->_left;}int blackNum = 0;return _IsBalance(_root, banchmark, blackNum);
}bool _IsBalance(Node* root, int& banchmark, int blackNum)
{if (!root){if (banchmark != blackNum){cout << "存在路径黑色节点的数量不相等" << endl;return false;}return true;}if (root->_col == RED && root->_parent->_col == RED){cout << "出现连续红色节点" << endl;return false;}if (root->_col == BLACK){++blackNum;}return _IsBalance(root->_left, banchmark, blackNum)&& _IsBalance(root->_right, banchmark, blackNum);
}

end


 


 

相关内容

热门资讯

【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
AsusVivobook无法开... 首先,我们可以尝试重置BIOS(Basic Input/Output System)来解决这个问题。...
ASM贪吃蛇游戏-解决错误的问... 要解决ASM贪吃蛇游戏中的错误问题,你可以按照以下步骤进行:首先,确定错误的具体表现和问题所在。在贪...
月入8000+的steam搬砖... 大家好,我是阿阳 今天要给大家介绍的是 steam 游戏搬砖项目,目前...