前言
日常啰嗦几句。先上红黑树特性:
(1)每个节点或者是黑色,或者是红色。
(2)根节点是黑色。
(3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
(4)如果一个节点是红色的,则它的子节点必须是黑色的。
(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
RBTree的应用
自己去看看c++的map和set源码吧(当然不是unordered那一类)
时间复杂度logN
证明方式还蛮有趣的,不过我不是很关心这东西,所以我过了。
左旋和右旋
这是RBT的基础操作了。
这里我就偷懒只讲一个好了。左旋,就是说,当前节点被自己的右孩子替换,自己成为右孩子的左孩子,同时继承之前右孩子的左孩子,作为自己的左孩子。(绕)
给张图:
这里涉及到父子关系,那么肯定不能简单的用左孩子和右孩子+节点颜色+节点数据来描述节点,肯定得加上父亲节点这个东西对吧,于是节点的描述就出来了。
接着,要定义一个RBTree类,类中有一个根节点,还有很多操作函数。基本上就这些了,咱们现在实现一下。
插入操作
插入操作其实还是很简单的,先将节点设置成黑色,然后按照传统的搜索树插入方式插入数据,如果此插入节点是rootNode就完了,如果不是,则将插入节点颜色改为红色。
接着,如果插入节点的父亲是黑色,完美,结束。
如果插入节点的父亲也是红色,那么看看这个父亲节点的兄弟节点。
1.如果兄弟是红色,完美,直接俩兄弟翻转,老父亲翻转,结束;
2.如果兄弟是黑色,难受,继续分类讨论。
2.1如果插入节点是右节点,那么将插入节点的父亲进行左旋,然后将插入节点颜色变成黑色,插入节点的父节点颜色变成红色,再对插入节点的父节点进行右旋;
2.2如果插入节点是左节点,那么直接将插入节点的父节点颜色改为黑色,插入节点的父节点的父节点颜色改为红色,并对其进行右旋操作。
完成!
删除操作
删除操作第一步是利用二叉搜索树的性质找到待删除的节点,没找到的话就退出。
找到以后就要开始循环判断几种情况了:
1.如果待删除节点是左孩子:
1.1如果兄弟节点是红色
首先将兄弟节点设置成黑色,再将兄弟节点的左孩子设置成红色,再对待删除节点的父亲进行左旋操作,好了,退出。
1.2如果兄弟节点是黑色,且兄弟有个右孩子(孩子肯定红色)
那么首先将父亲节点的颜色赋值给兄弟节点,再将兄弟的右孩子设置为黑色,再将父亲节点也设置成黑色,最后对父亲节点左旋操作(还没完)
1.3如果兄弟节点是黑色,且兄弟有个左孩子(孩子肯定红色)
那么首先将这个左孩子设置成黑色,然后将兄弟节点设置成红色,再对其进行右旋操作(还没完)
1.4如果兄弟节点是黑色,且有俩孩子(俩孩子肯定红色)
操作方法和1.2一样
1.5如果兄弟节点是黑色,且没有孩子
将兄弟颜色设置成红色,然后将当前节点改为父亲节点。一直循环再循环下去(要删除了还是没变的,所以要提前保留),直到成为rootNode,或者当前节点是黑色,就退出。退出以后第一件事情就是删除找到的节点。
2.如果待删除节点是右孩子:
那就是上面的镜像呀,不写了。
是不是觉得很简单呢?
代码部分实现
因为删除部分实在是不想写了,咱们知道原理就行,能行不?
#pragma once
#ifndef _RBTREE_DEFINES_H_
#define _RBTREE_DEFINES_H_
//1.先定义一个节点类型
template<typename T>
struct RBTreeNode {
enum nodeColor {node_red = 0, node_black = 1};
struct RBTreeNode* m_parent;
struct RBTreeNode* m_left;
struct RBTreeNode* m_right;
nodeColor m_color;
T m_nodeValue;
//以初始化列表方式写几个构造函数
RBTreeNode() :m_parent(nullptr), m_left(nullptr), m_right(nullptr), m_color(1) {}
RBTreeNode(RBTreeNode* p, RBTreeNode* left, RBTreeNode* right, nodeColor color, T nodeValue):m_parent(p), m_left(left), m_right(right), m_color(color), m_nodeValue(nodeValue) {}
RBTreeNode(T nodeValue):m_parent(nullptr), m_left(nullptr), m_right(nullptr), m_color(1), m_nodeValue(nodeValue) {}
};
//2.再定义红黑树类,里面放一个根节点
template <class T>
class RBTree {
private:
struct RBTreeNode<T>* rootNode;
private: //定义一些基本的操作
bool isRootNode(struct RBTreeNode<T>* node) { return (node == rootNode && node != nullptr) ? true : false; }
struct RBTreeNode<T>* parentLeftNode(struct RBTreeNode<T>* node) { return (node != nullptr && node->m_parent != nullptr) ? node->m_parent->m_left : nullptr; }
struct RBTreeNode<T>* parentRightNode(struct RBTreeNode<T>* node) { return (node != nullptr && node->m_parent != nullptr) ? node->m_parent->m_right : nullptr; }
struct RBTreeNode<T>* brotherNode(struct RBTreeNode<T>* node) { return (node->m_parent == nullptr) ? nullptr : ((node == parentLeftNode(node)) ? parentRightNode(node) : parentLeftNode(node)); }
private:
void leftTurn(struct RBTreeNode<T>* node)//节点左旋操作
{
//此处暂时不考虑X节点的右孩子Y不存在的情况
struct RBTreeNode<T>* nodeY = node->m_right;
nodeY->m_left->m_parent = node;
node->m_right = nodeY->m_left;
nodeY->m_left = node;
nodeY->m_parent = node->m_parent;
if (isRootNode(node))
{
node->m_parent = nodeY;
nodeY->m_left = node;
rootNode = nodeY;
}
else if (parentLeftNode(node) == node)
{
node->m_parent->m_left = nodeY;
node->m_parent = nodeY;
}
else
{
node->m_parent->m_right = nodeY;
node->m_parent = nodeY;
}
}
void rightTurn(struct RBTreeNode<T>* node)//节点右旋操作
{
//此处暂时不考虑X节点的左孩子Y不存在的情况
struct RBTreeNode<T>* nodeY = node->m_left;
nodeY->m_right->m_parent = node;
node->m_left = nodeY->m_right;
nodeY->m_right = node;
nodeY->m_parent = node->m_parent;
if (isRootNode(node))
{
node->m_parent = nodeY;
nodeY->m_right = node;
rootNode = nodeY;
}
else if (parentLeftNode(node) == node)
{
node->m_parent->m_left = nodeY;
node->m_parent = nodeY;
}
else
{
node->m_parent->m_right = nodeY;
node->m_parent = nodeY;
}
}
public:
void nodeInsert(T value) //插入操作,包括申请内存,构建node结构体
{
//1.先构造节点
struct RBTreeNode<T>* p_newNode = new struct RBTreeNode<T>(value);
//2.按照正常的二叉搜索树插入
if (rootNode == nullptr)
{
rootNode = p_newNode;
return;
}
else
{
struct RBTreeNode<T>* cmpNode = rootNode;
while (1)
{
if (p_newNode->m_nodeValue >= cmpNode->m_nodeValue && cmpNode->m_right == nullptr)
{
cmpNode->m_right = p_newNode;
p_newNode->m_parent = cmpNode;
break;
}
else if (p_newNode->m_nodeValue < cmpNode->m_nodeValue && cmpNode->m_left == nullptr)
{
cmpNode->m_left = p_newNode;
p_newNode->m_parent = cmpNode;
break;
}
else
cmpNode = (p_newNode->m_nodeValue >= cmpNode->m_nodeValue) ? cmpNode->m_right : cmpNode->m_left;
}
}
//3.将插入的节点颜色设置为红色,保证插入时不改变黑色数量
p_newNode->m_color = 0;
//4.插入的操作肯定是新增一个叶子结点(不考虑根节点情况),所以新节点是没有孩子的!
if (p_newNode->m_parent->m_color == 1)
return;
else
{
if (brotherNode(p_newNode->m_parent)->m_color == 0)
{
p_newNode->m_parent->m_color = 1;
brotherNode(p_newNode->m_parent)->m_color = 1;
p_newNode->m_parent->m_parent->m_color = 0;
return;
}
else if (p_newNode == parentRightNode(p_newNode))
{
leftTurn(p_newNode->m_parent);
p_newNode->m_color = 1;
p_newNode->m_parent->m_color = 0;
rightTurn(p_newNode->m_parent);
return;
}
else
{
p_newNode->m_parent->m_color = 1;
p_newNode->m_parent->m_parent->m_color = 0;
rightTurn(p_newNode->m_parent->m_parent);
return;
}
}
}
void nodeDelete(T value)
{
//1.先利用二叉搜索树性质找到
struct RBTreeNode<T>* cmpNode = rootNode;
while (1)
{
if (cmpNode == nullptr)
return;
if (cmpNode->m_nodeValue == value)
break;
else
cmpNode = (value > cmpNode->m_nodeValue) ? cmpNode->m_right : cmpNode->m_left;
}
//2.对于删除操作,按照其待删除节点的子节点数量可以分成三类:没孩子、一个孩子、两个孩子,但是最终我们都要转化成没孩子的情况
struct RBTreeNode<T>* targetNode = cmpNode;
while (targetNode->m_left != nullptr || targetNode->m_right != nullptr)
{
if (targetNode->m_left != nullptr && targetNode->m_right != nullptr)
{
targetNode = targetNode->m_left;
while (targetNode->m_right != nullptr)
targetNode = targetNode->m_right;
}
else
targetNode = (targetNode->m_left != nullptr) ? targetNode->m_left : targetNode->m_right;
}
//3.交换两个点的数据
T tempExchange = cmpNode->m_nodeValue;
cmpNode->m_nodeValue = targetNode->m_nodeValue;
targetNode->m_nodeValue = tempExchange;
//4.待删除点targetNode已经没有孩子了
if (targetNode->m_color == 0) //红色直接删除
{
if (parentLeftNode(targetNode) == targetNode)
targetNode->m_parent->m_left = nullptr;
else
targetNode->m_parent->m_right = nullptr;
delete targetNode;
}
else
{ //黑色需要分情况讨论
struct RBTreeNode<T>* circleNode = targetNode;
while (circleNode != rootNode && circleNode->m_color == 1)
{
if (parentLeftNode(circleNode) == circleNode)
{
//因为懒惰而不想写了
}
else
{
//因为懒惰而不想写了
}
}
if (isRootNode(targetNode) == targetNode)
rootNode = nullptr;
delete(targetNode);
}
}
};
#endif