文章目录
红黑树
1. 特点
《算法》中对红黑树的定义(针对的是线的颜色):
- 红色的线永远是左侧链接。(强行的规定)
- 没有一个节点同时链了两条红色的线。(也就是没有4-node)
- 任何从根到叶子节点的路径上有相同颜色的黑线。(黑线节点完美平衡)
红黑树合并为2-3查找树:
右侧有红色的线,要先通过旋转变为左侧的线,再合并:
合并的过程实质上是把红色的线拉成平的,就能清晰的看出2-3树和红黑树的关系
我们会把红线的红色信息放在子节点上,让子节点记录红色
红黑树中的红节点是什么意思? 根据上述内容用自己的话表达
2. 操作
2.1 旋转
按照下图书写左旋和右旋函数
(注意书写q,p,b的关系)
(还要注意红线是如何转移的)
对应上图写出左旋和右旋操作
#include <iostream>
using namespace std;
enum COLOR{
RED,BLACK}; // 枚举红黑两种颜色
template <typename T>
class _Node{
public:
T val;
COLOR color = BLACK; // 定义颜色
_Node* left;
_Node* right;
_Node(const T& val):val(val),left(nullptr),right(nullptr){
}
_Node(const T& val,_Node* left,_Node* right):val(val),left(left),right(right){
}
};
typedef _Node<int> Node;
// 右旋操作
Node* RotateRight(Node* root){
Node* q = root;
Node* p = root->left;
Node* b = p->right;
/* 转后 */
p->right = q;
q->left = b;
/* 颜色变换 */
p->color = q->color;
q->color = RED;
return p;
}
// 左旋操作
Node* RotateLeft(Node* root){
Node* p = root;
Node* q = p->right;
Node* b = q->left;
/* 转后 */
q->left = p;
p->right = b;
/* 颜色变换 */
q->color = p->color;
p->color = RED;
return q;
}
// 画一下树
void Preorder(Node* root){
if(nullptr == root) return;
if(root->left){
cout << root->val << "--" << root->left->val;
if(root->left->color == RED) cout << "[color=red]";
cout << endl;
}
if(root->right){
cout << root->val << "--" << root->right->val;
if(root->right->color == RED) cout << "[color=red]";
cout << endl;
}
Preorder(root->left);
Preorder(root->right);
}
int main(){
// 插入顺序为1,2,3
Node a(1);
Node b(2);
Node c(3);
c.color = RED;
a.right = &b;
b.right = &c;
Preorder(&a);
a.right = RotateLeft(&b); // 以2为基准左旋
Preorder(&a);
a.right = RotateRight(&c); // 再以3为基准右旋,又旋转回去了
Preorder(&a);
}
结果为:
// 原树型
1--2
2--3[color=red]
// 以2为基准左旋
1--3
3--2[color=red]
// 再以3为基准右旋
1--2
2--3[color=red]
对应树的变换如图所示:
2.2 颜色翻转
颜色翻转:两个子节点由红色变为黑色,父节点由黑色变为红色,或者反过来
// 不为空且为红色
bool IsRed(Node* root){
return nullptr!=root && root->color==RED;
}
// 做颜色翻转
Node* FlipColor(Node* root){
if(IsRed(root->left) && IsRed(root->right)){
// 左子节点和右子节点都是红色,进行颜色翻转
root->left->color = BLACK;
root->right->color = BLACK;
root->color = RED;
}
return root;
}
2.3 插入
往一个2-node节点底部插入新的节点
按照BST遍历,新插入的节点标记为红色
如果新插入的节点在父节点的右子节点,则需要进行左旋操作
往一个3-node节点底部插入新的节点
-
插入的节点比现有的两个节点都大,新插入的节点连接到右边子树上,颜色反转
-
插入的节点比现有的两个节点都小,新插入的节点连接到最左侧,以c为基准右旋,颜色反转
-
插入的节点的值位于两个节点之间,新节点插入到左侧节点的右子节点,先以a为基准左旋,再以c为基准右旋,颜色反转
找到规律并获得插入方法
由此可找到规律,对下面的操作按顺序依次执行,就可以完成红黑树的插入:
- 如果右子节点为红,左子节点不为红,要做一次左旋;
- 如果左子节点为红,左子节点的左子节点也为红,要做一次右旋;
- 如果左子节点为红,右子节点也为红,要做颜色翻转。
// 形成正规的红黑树
Node* Blance(Node* root){
if(IsRed(root->right) && !IsRed(root->left)) root = RotateLeft(root);
if(IsRed(root->left) && IsRed(root->left->left)) root = RotateRight(root);