红黑树
红黑树
1. 红黑树定义
红黑树是一种自平衡二叉搜索树,每个节点增加一个存储位表示颜色(红/黑)。通过对任何一条从根到叶子的路径上节点颜色的约束,确保没有一条路径比其他路径长出2倍,因此是近似平衡的。
2. 五大性质
-
每个节点是红色或黑色
-
根节点是黑色
-
所有叶子节点(NIL)是黑色 —— 这里的叶子指空节点
-
不能有两个连续的红节点
-
从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点 —— 黑高一致
3. 黑高定义
-
从某个节点(不含自身)到其叶子节点路径上的黑色节点数
-
根的黑高 = 树的黑高
4. 平衡性保证
-
最长路径(红黑交替) ≤ 2 × 最短路径(全黑)
-
操作时间复杂度:查找、插入、删除均为 O(log n)
5.插入操作
对于要插入的数据,默认已经通过二叉搜索树的性质找到了插入位置。
首先判断是否为根节点,若是,则染黑并结束插入;若不是则染为红色并判断是否满足“不红红”?满足则直接插入;不满足则需要调整平衡。
调整平衡:对于导致失衡的结点,判断其叔叔结点的颜色,若为黑色则需要根据对应情况进行旋转操作(LL,RR,LR,RL)结束插入;若为红色则将叔叔,父亲,爷爷结点染为各自相反的颜色,并将爷爷结点作为新的节点从头开始判断是否为根节点,一直到结束插入为止。

为什么每次插入的新结点需要先染为红色?
在每次插入数据时,若插入节点为黑色,那么不仅会破坏“黑高一致”这个特性,还可能破坏“最长路径(红黑交替) ≤ 2 × 最短路径(全黑)”这一基本平衡条件;但若插入节点为红色,那么只有可能破坏“不红红”这一特性,其余性质都不会被破坏,只需围绕这一特性进行调整即可。
插入操作:
void insertNode(const int& val) {
Node* node = new Node(val);
node->left = node->right = NIL;
Node* cur = root;
Node* prev = nullptr;
while (cur != NIL) {
prev = cur;//prev用于标记父亲位置
if (cur->val < node->val) {
cur = cur->right;
}
else if (node->val < cur->val) {
cur = cur->left;
}
else {
delete node;//不插重复数
return;
}
}
node->parent = prev;
if (prev == nullptr) {
root = node;
}
else if (prev->val > node->val) {
prev->left = node;
}
else {
prev->right = node;
}
insertFixup(node);
}
};
该操作成功将新结点node添加到对应位置,满足二叉搜索树的性质,最后通过insertFixup(node)进行平衡调整。
插入修复操作:
void insertFixup(Node* node) {
while (node->parent && node->parent->color == RED) {//违反“不红红”需要调整
if (node->parent == node->parent->parent->left) {//父节点在爷节点左侧,则叔节点在右侧
Node* uncle = node->parent->parent->right;//叔叔节点
//得到叔结点,开始判断颜色;
if (uncle->color == RED) {
//case 1:叔为红色,则叔父爷三个节点染色,爷节点作为新结点
node->parent->color = BLACK;//新结点违反“不红红”,父节点一定红,变黑
node->parent->parent->color = RED;//已知叔为红,那么爷一定黑,所以变红
uncle->color = BLACK;
node = node->parent->parent;//爷节点作为新结点继续调整
}
else {//叔叔为黑,则判断相对位置进行旋转并将叔父爷染色;(叔原本为黑所以不写)
//case 2:node为右孩子,那么是LR旋转,先进行左旋,再右旋
if (node == node->parent->right) {
node = node->parent;
leftRotate(node);
}//此时node处于最左下角
//case 3:node为左孩子,是LL旋转,直接右旋并染色即可
node->parent->color = BLACK;
node->parent->parent->color = RED;
rightRotate(node->parent->parent);
//此操作是以父节点为轴,将爷节点拉到父节点的右侧,与node成为兄弟节点
}
}
else if(node->parent == node->parent->parent->right){//对称情况,叔结点在左侧
Node* uncle = node->parent->parent->left;//叔结点
if (uncle->color == RED) {
node->parent->color = BLACK;
node->parent->parent->color = RED;
uncle->color = BLACK;
node = node->parent->parent;
}
else {//叔为黑,判断相对位置
if (node == node->parent->left) {
node = node->parent;
rightRotate(node);//先右旋
}
node->parent->color = BLACK;
node->parent->parent->color = RED;
leftRotate(node->parent->parent);
}
}
}
root->color = BLACK;
}
旋转操作:
//需要将node移到其左孩子的右侧,即绕着左孩子顺时针旋转(左孩子必须存在才能操作)
void rightRotate(Node* node) {
//child 是node左孩子,操作完成后child将取代node位置,node在child右侧
Node* child = node->left;
//将child的右子树挂在node的左子树上
//原因:node在操作完后将处于child的右子树,若child右子树不空则会冲突,提前处理右子树
node->left = child->right;
if (child->right != NIL) {
child->right->parent = node;
}
//处理child的父节点
child->parent = node->parent;
if (node->parent == nullptr) {
root = child;
}//将child连在node的父节点下面
else if (node == node->parent->left) {//判断node为哪个孩子
node->parent->left = child;
}
else if (node == node->parent->right) {
node->parent->right = child;
}
node->parent = child;
child->right = node;
}
//需要将node移到其右孩子的左侧,即绕着右孩子逆时针旋转(右孩子必须存在才能操作)
void leftRotate(Node* node) {
Node* child = node->right;
node->right = child->left;
if (child->left != NIL) {
child->left->parent = node;
}
child->parent = node->parent;
if (node->parent == nullptr) {
root = child;
}
else if (node == node->parent->left) {
node->parent->left = child;
}
else if (node == node->parent->right) {
node->parent->right = child;
}
child->left = node;
node->parent = child;
}
7.删除操作
void deleteNode(Node* node) {
Node* realdeletedNode = node;//实际要删除的节点
Color deletedcolor = realdeletedNode->color;//实际要删除的节点的颜色
Node* replaceNode = nullptr;//最终要替换删除节点位置的节点
if (node->left == NIL) {
replaceNode = node->right;
transplant(realdeletedNode, realdeletedNode->right);
}
else if (node->right == NIL) {
replaceNode = node->left;
transplant(realdeletedNode, realdeletedNode->left);
}
else {//有两个孩子节点, 则需找到直接后继节点代替原来要删除节点的位置,删除后继节点
realdeletedNode = findminNode(node->right);//直接后继节点
deletedcolor = realdeletedNode->color;//直接后继节点的颜色
//直接后继节点的右孩子(用于替换待删除节点位置)
//为什么是right?因为直接后继节点只可能是没有叶节点和只有右孩子两种情况
replaceNode = realdeletedNode->right;
//接下来需要让直接后继节点接管node的右子树,以保证二叉树的性质不变
//处理两种情况:1.直接后继节点本身就在node的右侧 2.直接后继节点在更深处
//case 1:就在node右侧,已经接管了右子树,只需要处理 替换节点 的父指针;
if (realdeletedNode->parent == node) {
if (replaceNode != NIL) {
replaceNode->parent = realdeletedNode;
}
}
else {//case 2:在更深处
transplant(realdeletedNode, realdeletedNode->right);//从原位置移出
realdeletedNode->right = node->right;
realdeletedNode->right->parent = realdeletedNode;
}
transplant(node, realdeletedNode);//用后继节点替换node位置
realdeletedNode->left = node->left;//将node的左子树连接
realdeletedNode->left->parent = realdeletedNode;
//至此,node已经被彻底隔离,可直接删掉
realdeletedNode->color = node->color;//替换node的颜色;
}
if (deletedcolor == BLACK) {//删除的是黑节点,需要调整
deleteFixup(replaceNode);
}
delete node;
}
删除修复:
//删除修复操作
//参数 currentNode: 替换了被删节点位置的节点
void deleteFixup(Node* currentNode) {
// 循环直到 currentNode 为根节点,或者 currentNode 变成红色(红色可以直接染黑解决)
while (currentNode != root && currentNode->color == BLACK) {
//此时节点为黑色,判断其兄弟节点的颜色情况,而兄弟则分左右两种情况;
//情况A:兄弟在右边(currentNode是左孩子)
if (currentNode == currentNode->parent->left) {
Node* bro = currentNode->parent->right;//兄弟节点
//判断兄弟节点的颜色
// Case 1: 兄弟节点是红色
//为红色则父兄变色,将父节点按照currentNode的方向旋转
if (bro->color == RED) {
bro->color = BLACK;
bro->parent->color = RED;
leftRotate(bro->parent);
bro = currentNode->parent->right;
}
//case 2:兄弟节点两个孩子都是黑的
if (bro->left->color == BLACK && bro->right->color == BLACK) {
bro->color = RED;
currentNode = currentNode->parent;
}
else {
// Case 3: 兄弟节点的右孩子是黑色,左孩子是红色
// 处理:将左孩子变黑,兄弟变红,右旋兄弟,重新获取兄弟节点
if (bro->right->color == BLACK) {
bro->left->color = BLACK;
bro->color = RED;
rightRotate(bro);
bro = currentNode->parent->right;
}
// Case 4: 兄弟节点的右孩子是红色(左孩子任意)
// 处理:兄弟继承父节点颜色,父节点变黑,右孩子变黑,左旋父节点
// 此时红黑树性质完全恢复,将 currentNode 指向根以退出循环
bro->color = bro->parent->color;
bro->parent->color = BLACK;
bro->right->color = BLACK;
leftRotate(bro->parent);
currentNode = root;
}
}
//情况B:兄弟在左边;以下为对称情况
else if (currentNode == currentNode->parent->right) {
Node* bro = currentNode->parent->left;
if (bro->color == RED) {
bro->color = BLACK;
bro->parent->color = RED;
rightRotate(bro->parent);
bro = currentNode->parent->left;
}
if (bro->left->color == BLACK && bro->right->color == BLACK) {
bro->color = RED;
currentNode = currentNode->parent;
}
else {
if (bro->left->color == BLACK) {
bro->right->color = BLACK;
bro->color = RED;
leftRotate(bro);
bro = currentNode->parent->left;
}
bro->color = bro->parent->color;
bro->parent->color = BLACK;
bro->left->color = BLACK;
rightRotate(bro->parent);
currentNode = root;
}
}
}
// 将 currentNode 染成黑色(解决根节点或红色节点的问题)
currentNode->color = BLACK;
}

浙公网安备 33010602011771号