一、红黑树的性质
1、红黑树的基本定义
红黑树(Red Black Tree),简称R-B Tree,是平衡二叉搜索树的一种变体,除二叉搜索树的强制要求外,红黑树还要满足以下要求:
1)结点是红色或黑色;
2)根节点是黑色;
3)每棵红黑树的叶子结点都是不存数据的空结点(每棵红黑树中所有结点只要左或右孩子为空,其指针指向NIL结点);
4)每个红色结点的两个子节点不能为红色
5)从根结点到NIL结点的任意路径包含相同数目的黑色结点
2、比较红黑树和AVL树
红黑树是平衡二叉搜索树的变体,是一种不严格的平衡二叉搜索树,它的左右子树高差有可能大于 1,所以红黑树不是严格意义上的平衡二叉搜索树(AVL)。
虽然AVL树查找的时间复杂度优于红黑树,但对之进行平衡的代价较低, 其平均统计性能要强于严格的AVL 。
二、红黑树结构
RBtree.h
#ifndef RBTREE_H
#define RBTREE_H
enum NodeColor { //结点颜色
BLACK,
RED
};
struct Node { //结点
int key;
Node* parent;
Node* left;
Node* right;
NodeColor color;
Node(); //构造NIL结点
Node(int x); //构造新结点
void show(); //中序遍历
};
class RBtree {
Node* root; //中序遍历
public:
RBtree();
void insert(int x);
void show(); //中序遍历输出
private:
void leftRotate(Node* p); //左旋
void rightRotate(Node* p); //右旋
void insertFixup(Node* node); //插入调整
};
#endif
三、实现红黑树
1、左旋和右旋
左旋
左旋就是围绕某一结点,提高左子树、降低右子树的高度
void RBtree::leftRotate(Node* p) {
Node* c = p->right;
p->right = c->left;
if (p->right != NIL) {
p->right->parent = p;
}
c->left = p;
c->parent = p->parent;
if (p->parent != nullptr) {
if (p->parent->left == p) {
p->parent->left = c;
}
else {
p->parent->right = c;
}
}
else root = c;
p->parent = c;
}
右旋
和左旋相反,右旋就是围绕某一结点,提高右子树、降低左子树的高度
void RBtree::rightRotate(Node* p) {
Node* c = p->left;
p->left = c->right;
if (p->left != NIL) {
p->left->parent = p;
}
c->right = p;
c->parent = p->parent;
if (p->parent != nullptr) {
if (p->parent->left == p) {
p->parent->left = c;
}
else {
p->parent->right = c;
}
}
else root = c;
p->parent = c;
}
2、插入新结点平衡调整
插入新节点
红黑树插入新结点和普通的二叉排序树插入新结点找插入位置的过程是一样的,新插入的结点都是放在叶子结点上,不同的是红黑树中因为有NIL结点的存在,新节点的父节点在插入之前指向新结点的指针不会指向NULL,而是指向NIL结点。
红黑树规定,插入结点的颜色必须是红色,且左右孩子初始化指向NIL结点。
插入新节点对红黑树结构的影响有以下几种情况:
1)根节点为NULL或NIL结点,那么我们只需要把根节点修改为插入结点,然后修改其颜色为黑色即可;
2)插入结点的父节点颜色是黑色,此时我们不需要调整,树的结构仍满足红黑树的定义;
3)插入结点的父节点颜色是红色,此时已经违背的红黑树的定义,需要对它的结构进行平衡调整。
void RBtree::insert(int x) {
if (root == NIL) {
root = new Node(x);
root->color = BLACK;
return;
}
Node* newNode = new Node(x);
Node* p = root;
while (p != NIL) {
if (p->key > x) {
if (p->left == NIL) {
p->left = newNode;
break;
}
p = p->left;
}
else {
if (p->right == NIL) {
p->right = newNode;
break;
}
p = p->right;
}
}
newNode->parent = p;
if (p->color == BLACK ) return;
insertFixup(newNode);
}
平衡调整
红黑树的平衡调整是一个向上迭代的过程,我们把正在处理的红色结点叫关注结点,随着关注结点的向上迭代调整,每次调整都会发生颜色变换或左右旋转。为了便于描述,我们把父结点的兄弟结点称为叔叔结点,父节点的父节点称为祖父结点。
插入结点是插入一个结点调整平衡后再插入下一个元素,所以在平衡调整向上迭代的过程中,关注结点的上层结构不会出现两个连续的红色结点。
因为根节点的颜色是黑色,这就保证了需要调整时树的高度h>=3,调整时关注结点的祖父结点必定不为NULL;再加上NIL结点的存在,保证了需要调整时关注结点的叔叔结点必定不为NULL。
当关注结点的父节点为红色时,要进行平衡调整。
需要调整时有两种情况:(1)关注节点的父节点是祖父结点的左孩子;(2)关注节点的父节点是祖父结点的右孩子;
两种情况的调整步骤一致,具体以第一种情况举例说明:
1)如下图,关注结点是node ,结点node 可以是P的左结点或右结点中的一个,当它的叔叔结点是红色时,
调整操作:把 祖父结点GP 改成红色,父节点P 和 叔叔结点uncle 改成黑色,关注结点node 变成GP,判断是否继续调整;
2)如下图, 关注结点node 的 叔叔结点uncle 不是红色,且node是父节点P的右孩子时,
调整操作: 围绕父节点P右旋,关注结点node 改为 父节点P,更新父节点P。跳到 第 3)步
3)如下图, 关注结点node 的 叔叔结点uncle 不是红色,且node是父节点P的左孩子时,
调整操作:围绕祖父结点GP右旋,父节点P的颜色改为黑色,祖父结点的颜色改为红色,平衡调整结束。
情况 1)可能执行很多次,情况2)、3)最多执行一次,情况2)执行情况3)必定执行。
最后需要把根结点更新为黑色,因为在情况 1)下调整,如果关注结点的祖父结点是根结点时,迭代结束前会把根结点变红色。
void RBtree::insertFixup(Node* node) {
Node* p;
while ((p = node->parent) != nullptr && p->color == RED) {
Node* gp = p->parent;
if (p == gp->left) {
Node* uncle = gp->right;
if (uncle->color == RED) {
gp->color = RED;
p->color = uncle->color = BLACK;
node = gp;
}
else {
if (node == p->right) {
leftRotate(p);
node = p;
p = node->parent;
}
rightRotate(gp);
p->color = BLACK;
gp->color = RED;
break;
}
}
else {
Node* uncle = gp->left;
if (uncle->color == RED) {
gp->color = RED;
p->color = uncle->color = BLACK;
node = gp;
}
else {
if (node == p->left) {
rightRotate(p);
node = p;
p = node->parent;
}
leftRotate(gp);
p->color = BLACK;
gp->color = RED;
break;
}
}
}
root->color = BLACK;
}
代码测试
RBtree.h
#ifndef RBTREE_H
#define RBTREE_H
enum NodeColor {
BLACK,
RED
};
struct Node {
int key;
Node* parent;
Node* left;
Node* right;
NodeColor color;
Node(); //构造NIL结点
Node(int x); //构造新结点
void show(); //中序遍历
};
class RBtree {
Node* root;
public:
RBtree();
void insert(int x);
void show();
private:
void leftRotate(Node* p);
void rightRotate(Node* p);
void insertFixup(Node* node);
};
#endif
RBtree.cpp
#include "RBtree.h"
#include <iostream>
using namespace std;
Node::Node():key(-1),color(BLACK) {
parent = left = right = nullptr;
}
Node nil;
Node* NIL = &nil;
Node::Node(int x) :key(x), color(RED) {
left = right = NIL;
parent = nullptr;
}
void Node::show() {
if (this != NIL) {
left->show();
cout << key << " ";
right->show();
}
}
RBtree::RBtree() :root(NIL) { }
void RBtree::show() {
root->show();
cout << endl;
}
void RBtree::insert(int x) {
if (root == NIL) {
root = new Node(x);
root->color = BLACK;
return;
}
Node* newNode = new Node(x);
Node* p = root;
while (p != NIL) {
if (p->key > x) {
if (p->left == NIL) {
p->left = newNode;
break;
}
p = p->left;
}
else {
if (p->right == NIL) {
p->right = newNode;
break;
}
p = p->right;
}
}
newNode->parent = p;
if (p->color == BLACK ) return;
insertFixup(newNode);
}
void RBtree::leftRotate(Node* p) {
Node* c = p->right;
p->right = c->left;
if (p->right != NIL) {
p->right->parent = p;
}
c->left = p;
c->parent = p->parent;
if (p->parent != nullptr) {
if (p->parent->left == p) {
p->parent->left = c;
}
else {
p->parent->right = c;
}
}
else root = c;
p->parent = c;
}
void RBtree::rightRotate(Node* p) {
Node* c = p->left;
p->left = c->right;
if (p->left != NIL) {
p->left->parent = p;
}
c->right = p;
c->parent = p->parent;
if (p->parent != nullptr) {
if (p->parent->left == p) {
p->parent->left = c;
}
else {
p->parent->right = c;
}
}
else root = c;
p->parent = c;
}
void RBtree::insertFixup(Node* node) {
Node* p;
while ((p = node->parent) != nullptr && p->color == RED) {
Node* gp = p->parent;
if (p == gp->left) {
Node* uncle = gp->right;
if (uncle->color == RED) {
gp->color = RED;
p->color = uncle->color = BLACK;
node = gp;
}
else {
if (node == p->right) {
leftRotate(p);
node = p;
p = node->parent;
}
rightRotate(gp);
p->color = BLACK;
gp->color = RED;
break;
}
}
else {
Node* uncle = gp->left;
if (uncle->color == RED) {
gp->color = RED;
p->color = uncle->color = BLACK;
node = gp;
}
else {
if (node == p->left) {
rightRotate(p);
node = p;
p = node->parent;
}
leftRotate(gp);
p->color = BLACK;
gp->color = RED;
break;
}
}
}
root->color = BLACK;
}
main.cpp
#include "RBtree.h"
#include <vector>
#include <time.h>
#include <iostream>
void test(std::vector<int>& v) {
time_t t;
time(&t);
srand((unsigned int)t);
for (int i = 0; i < 100; i++) {
v.push_back(rand() % 100);
}
}
int main() {
RBtree t;
std::vector<int> v;
test(v);
for (int i = 0; i < 100; i++) {
t.insert(v[i]);
}
t.show();
}
测试结果