目录
一、概念和规则:
- 红黑树是一颗搜索二叉树,满足左小右大(或者左小右大)的规则;(前提规则)
- 红黑树每一个节点不是黑色就是红色;(规则一)
- 根节点是黑色的;(规则二)
- 不能出现连续的红色节点,若一个节点是红色的,那么它的两个孩子都是黑色的;(规则三)
- 对于任何一条简单路径(从根到空),其上的黑色节点的数量都是相同的;(规则四)
- 红黑树的最长路径不超过最短路径的二倍(规则推论)

1、思考为什么最长路径不超过最短路径的二倍?
由于每条路径的黑色节点的数量的个数相同,极端情况下最短路径的长度就是全是黑色节点的数量,最长路径长度就是红色黑色相间的路径,那么恰好就是最短路径的二倍;那么其他的路径长度都在最长与最短之间,那么最长路径就不会超过最短路径的二倍。
2、红黑树的效率?
假设红黑树最短路径的高度为H,那么最长路径的长度最长为 2*H ,其他的都在这两者之间,那么节点的数量N(根据等比求和)就在 2^H-1到2^(2*H)-1之间,那么红黑树增删查的时间复杂度还是O(logN),和AVL属于同一量级;
AVL是通过左右子树高度差来控制平衡;而红黑树是通过规则和颜色来达到近似平衡。
二、红黑树的代码实现
1、红黑树的节点结构
enum Color
{
RED,
BLACK
};
template<class K,class V>
struct RBTreeNode
{
Color _col;
pair<K, V> _kv;
RBTreeNode<K, V>* _parent;
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode(const pair<K,V> kv)
:_kv(kv)
,_parent(nullptr)
,_left(nullptr)
,_right(nullptr)
{}
};
这里用枚举定义颜色,方便观察;
2、红黑树的插入
1、大致过程:
- 先按照二叉搜索树进行插入;
- 若是空树,就将新插入的节点认作根节点,并且把颜色赋为黑色;若不是空树,那么插入之后,这个新插入的节点必须赋为红色,否则会破坏所有路径黑色节点数量相同的规则;但是连续的两个红色也会破坏,但是连续的两个红色更好维护;
- 插入红色的,若插入的父节点是黑色的,那么就是正常的,无需维护,插入完成直接退出;
- 插入红色的,若插入的父节点是红色的,那就需要维护,因为此时破坏了规则;而维护有三种情况,维护的关键是看叔节点,也就是父亲的兄弟节点;
2、维护的三种情况:
1、情况一:变色
此种情况出现适用于:当uncle节点存在并且是红色时;
首先grandfather节点(parent和uncle的父节点)时黑色的,parent和uncle节点是红色的,这是确定的条件;此时给parent插入红色的新节点,要维护规则;进行变色:将grandfather变红,parent和uncle变黑;

一次变完之后,因为grandfather变红了,但是其上可能还有节点,当其父节点为红时,还需要继续调整,将cur赋值为grandfather,parent跟着向上调,uncle随之变化;直到父节点的颜色为黑或者父节点为根节点时结束循环;最后会单独处理根节点,无论根节是哪种颜色,都赋为黑;
2、情况二:单旋+变色
适用情形,uncle为空或者为黑时;此时只是单纯的变色解决不了问题,因为每条简单路径上面的黑色节点数量会不同;
当uncle不存在时,那么cur一定是新插入进来的,若不是新插入的那么cur下面还有黑色节点,那么parent的左右子树的黑色节点数量就不同;若uncle存在且为黑色,那么cur一定不是新插入的红色节点,而是cur下面的节点通过变色变上来的红色节点,否则parent的左右子树黑色节点的数量就不同,违反规则;
那么就要将grandfather作为旋转基准进行左单旋或者右单旋,旋转之后parent成了cur和grandfather的根节点,此时再把parent变黑,grandfather变红 ;无论是uncle存在与否,最后都能达到维护的目的,并且每条简单路径上面的黑色节点的数量都相同。
旋转并且变色之后这颗红黑树整体上就已经满足规则了,无需像情况一一样向上调整。

3、情况三:双旋+变色
首先和情况二前提相同:若uncle不存在,则cur一定为新插入的;若uncle存在且为黑,则cur一定不是新插入的,而是调整上来的;
双旋的原因:不同于单旋,当parent是grandfather的左节点但是cur是father的右节点时,单旋不能解决问题,若是单旋,只是交换了左右位置,本质上还是没有完成规则的维护;此时需要双旋之后再变色;
第一次循环以parent为基准旋转,第二次以grandfather为基准旋转;最后将cur变为黑,grandfather变为红;

3、红黑树的验证
红黑树的验证要严格按照四条规则来,验证每一条规则是否被满足,只有当所有的规则都被满足才说明这棵树是红黑树;
- 直接判断根节点是否是黑色
- 每个节点不是黑就是红,这个天然而成无需判断
- 检查是否有红色连续的情况,通过递归每次检查当前节点的父亲,当前节点为红色并且当前节点的父节点也是红色时,就返回false;检查父节点更方便;若是每次检查当前节点的左右子树节点相较麻烦一点
- 检查每条简单路径上面是否含有相同黑色节点数量时,先计算一条路径上面的黑色节点数量作为一个比较的基准值,再在递归的过程中计算每条路径的黑色节点数量,当递归的根为空时说明一条路径走完了,此时比较,若不想等就返回false;整体的返回是左右子树通过&&连接来返回,也就是有一条不满足的这棵树就不是一颗红黑树的意思
4、整体代码
#include<iostream>
using namespace std;
enum Color
{
RED,
BLACK
};
template<class K,class V>
struct RBTreeNode
{
Color _col;
pair<K, V> _kv;
RBTreeNode<K, V>* _parent;
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode(const pair<K,V> kv)
:_kv(kv)
,_parent(nullptr)
,_left(nullptr)
,_right(nullptr)
{}
};
template<class K,class V>
class RBTree
{
using Node = RBTreeNode<K, V>;
public:
void Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = BLACK;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else
{
return;
}
}
cur = new Node(kv);
if (kv.first < parent->_kv.first)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
cur->_col = RED;
//parent为空或者 parent颜色为黑色,插入了满足条件,不用进循环
//进循环说明 parent为红并且其父节点为黑,新插入的只能是红
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
//两个大方向:parent在 grandfather左边或者右边
if (grandfather->_left == parent)
{
Node* uncle = grandfather->_right;
//首先是不用旋转的情况,此时无论 cur在 parent哪边都一样
//将 grandfather变红,father和 uncle变黑
//这种情况就是 uncle存在并且为红时
if (uncle && uncle->_col == RED)
{
grandfather->_col = RED;
parent->_col = uncle->_col = BLACK;
cur = grandfather;
parent = cur->_parent;
}
//这里的 else含义是:uncle为空或者不为空但是颜色是黑色
//要旋转+变色
else
{
//uncle若是为空,cur就必是新插入的节点,否则每一条路径的黑色节点数量不同
//uncle若不为空,cur就必不是新插入的节点,这样每条路径的黑色节点数量才可能相同
//无论为空为否,都要进行旋转再变色
if (parent->_left == cur)
{
//右单旋
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
break;//旋转完之后满足规则
}
else
{
//先左旋再右旋
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
break;
}
}
}
//parent为 grandfather的右孩子
else
{
Node* uncle = grandfather->_left;
if (uncle && uncle->_col == RED)
{
grandfather->_col = RED;
parent->_col = uncle->_col = BLACK;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (parent->_right == cur)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
break;
}
else
{
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
break;
}
}
}
}
//将根节点置为黑色
_root->_col = BLACK;
}
Node* Find(const K& k)
{
Node* cur = _root;
while (cur)
{
if (k < cur->_kv.first)
{
cur = cur->_left;
}
else if (k > cur->_kv.first)
{
cur = cur->_right;
}
else
{
return cur;
}
}
}
bool IsRBTree()
{
if (!_root)
return true;
if (_root->_col == RED)
return false;
int mode_count = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
mode_count++;
cur = cur->_left;
}
return Check(_root, 0, mode_count);
}
void Print()
{
_Print(_root);
cout << endl;
}
private:
Node* _root = nullptr;
//右单旋
void RotateR(Node* sub)
{
Node* subl = sub->_left;
Node* sublr = subl->_right;
sub->_left = sublr;
if (sublr)
sublr->_parent = sub;
Node* sub_P = sub->_parent;
subl->_right = sub;
sub->_parent = subl;
if (!sub_P)
{
_root = subl;
subl->_parent = nullptr;
}
else
{
if (sub_P->_left == sub)
{
sub_P->_left = subl;
}
else
{
sub_P->_right = subl;
}
subl->_parent = sub_P;
}
}
//左单旋
void RotateL(Node* sub)
{
Node* subr = sub->_right;
Node* subrl = subr->_left;
sub->_right = subrl;
if (subrl)
{
subrl->_parent = sub;
}
Node* sub_P = sub->_parent;
subr->_left = sub;
sub->_parent = subr;
if (!sub_P)
{
_root = subr;
subr->_parent = nullptr;
}
else
{
if (sub_P->_left == sub)
{
sub_P->_left = subr;
}
else
{
sub_P->_right = subr;
}
subr->_parent = sub_P;
}
}
void _Print(Node* root)
{
if (!root)
{
return;
}
_Print(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_Print(root->_right);
}
bool Check(Node* root, int count, const int mode_count)
{
if (root == nullptr)
{
if (count != mode_count)
{
cout << "存在不同路径的黑色节点数量不同";
return false;
}
return true;
}
if (root->_col == RED && root->_parent && root->_parent->_col == RED)
{
cout << "存在连续的红色节点";
return false;
}
if (root->_col == BLACK)
count++;
return Check(root->_left, count, mode_count) && Check(root->_right, count, mode_count);
}
};
2328

被折叠的 条评论
为什么被折叠?



