这里只着重介绍插入操作的实现:)
一、红黑树的概念和性质
红黑树(Red Black Tree)是一种自平衡的二叉搜索树。红黑树最初在1972年由Rudolf Bayer发明,当时被称为平衡二叉B树(symmetric binary B-trees)。随后,在1978年,Leo J. Guibas和Robert Sedgewick对其进行了修改,并正式命名为红黑树。
具有以下性质的二叉搜索树是红黑树:
黑高度:从红黑树任意结点x出发(不包括结点x),到达一个外部结点(nil)的任一路径上黑结点的个数叫做结点x的黑高度(bh),也称为结点的阶(rank)。 一棵红黑树的黑高度指的是该树根节点的黑高度。上图和下图的红黑树黑高度都为2。
为什么红黑树具有以上5个性质就可以让二叉搜索树自平衡呢?下面我们来看这张图:
二、红黑树的插入操作
红黑树在插入一个结点时要不破坏红黑树的规则,根节点必须是黑色的,并且每条路径上的黑色结点个数都要相同,不能有相连的红色结点。所以我们在插入一个新的结点时都会选择插入红色的结点,因为这样不会影响这条路径上黑色结点的数目,但是新插入结点的双亲结点有可能也为红色,这就违反了红黑树不能有两个相连的红色结点的性质,这个时候就需要我们根据实际情况来调整红黑树的结构了。
(一)简单情况
下面的抽象图代表一颗完整的树或者一颗子树,圆形代表具体的结点,矩形代表结点的子树,如果cur为新插入的结点,那么子树(abcde)为空。
(二)只需变色的情况
(三)旋转加变色的情况
(四)对称情况的处理
三、红黑树插入的代码和测试代码
RBTree.h
#pragma once
#include <iostream>
using namespace std;
//红黑树结点定义
template<class T>
struct RBTreeNode
{
T _data;
RBTreeNode* _pLeft; //指向左孩子
RBTreeNode* _pRight; //指向右孩子
RBTreeNode* _pParent; //指向双亲结点
bool _isRed; //为false代表该结点为红,true代表该结点为黑
RBTreeNode(const T T()) :
_data(data),
_pLeft(nullptr), _pRight(nullptr), _pParent(nullptr),
_isRed(true)
{}
};
template<class T>
class RBTree
{
typedef RBTreeNode<T> Node;
public:
RBTree():_root(nullptr){}
bool insert(const T& data)
{
Node* pPos = nullptr; //pPos记录新插入结点的双亲节点的位置
Node* pCur = _root;
while (pCur)
{
pPos = pCur;
if (data > pCur->_data)
pCur = pCur->_pRight;
else if (data < pCur->_data)
pCur = pCur->_pLeft;
else
return false;
}
//两种简单的情况之一:插入的结点作为根节点插入
if (pPos == nullptr)
{
//新插入的结点作为根结点
_root = new Node(data);
_root->_isRed = false;
return true;
}
//插入新的结点
pCur = new Node(data);
if (data > pPos->_data)
pPos->_pRight = pCur;
else
pPos->_pLeft = pCur;
pCur->_pParent = pPos;
//向上调整
Node* pParent = pPos;
while (pParent && pParent->_isRed) //当parent为黑或者调整到根节点为止结束
{
Node* pGrand = pParent->_pParent;
if (pParent == pGrand->_pLeft) //其中的一种对称情况
{
// g
// p u
//c
Node* pUncle = pGrand->_pRight;
if (pUncle && pUncle->_isRed) //只需变色的情况:parent和uncle都为红色。 cur为parent的左孩子或为右孩子。
{
//这种情况我们只需将parent和uncle改为黑色,grand改为红色即可。
pParent->_isRed = pUncle->_isRed = false;
pGrand->_isRed = true;
//继续向上调整
pCur = pGrand;
pParent = pGrand->_pParent;
}
else //需要旋转+变色的情况:cur和parent为红,uncle结点不存在或uncle颜色为黑色。
{
if (pCur == pParent->_pLeft) //右单旋
{
//这种情况需要进行一个右单旋,旋转后将parent和grand的颜色互换。
// g
// p u
//c
rotateR(pGrand);
pParent->_isRed = false;
pGrand->_isRed = true;
break; //结束向上调整
}
else //左右双旋
{
//这个时候我们需要进行左右双旋加变色,然后交换cur和grand的颜色。
// g
// p u
// c
rotateL(pParent);
rotateR(pGrand);
pCur->_isRed = false;
pGrand->_isRed = true;
break; //结束向上调整
}
}
}
else //对称的情况
{
// g
// u p
// c
Node* pUncle = pGrand->_pLeft;
if (pUncle && pUncle->_isRed) //只需变色的情况:parent和uncle都为红色。 cur为parent的左孩子或为右孩子。
{
//将parent和uncle变为红色
pUncle->_isRed = pParent->_isRed = false;
pGrand->_isRed = true;
//继续向上调整
pCur = pGrand;
pParent = pGrand->_pParent;
}
else //需要旋转加变色的情况
{
if (pCur == pParent->_pRight) //cur为parent的右孩子,左单旋
{
// g
// u p
// c
rotateL(pGrand);
pParent->_isRed = false;
pGrand->_isRed = true;
break; //结束向上调整
}
else //右左单旋
{
// g
// u p
// c
rotateR(pParent);
rotateL(pGrand);
pCur->_isRed = false;
pGrand->_isRed = true;
break;
}
}
}
}
_root->_isRed = false; //将根节点置为黑
return true;
}
// 查找
Node* find(const T& data)
{
Node* pCur = _root;
while (pCur)
{
if (data > pCur->_data)
pCur = pCur->_pRight;
else if (data < pCur->_data)
pCur = pCur->_pLeft;
else
return pCur;
}
return nullptr;
}
// 检测红黑树是否为有效的红黑树
bool checkRBTree()
{
int pathBlack = 0;
Node* pCur = _root;
while (pCur) //找最左侧那条路径的黑色结点总数作为基准值
{
if (pCur->_isRed == false)
pathBlack++;
pCur = pCur->_pLeft;
}
return _checkRBTree(_root, 0, pathBlack);
}
//计算树的高度
int height()
{
return _height(_root);
}
//计算结点个数
int size()
{
return _size(_root);
}
//中序遍历并打印结点数据
void inOrder()
{
_inOrder(_root);
}
private:
//blackCount是根节点到该结点路径上的所有黑色结点的数量,pathBlack是基准值
bool _checkRBTree(Node* pRoot, size_t blackCount, size_t pathBlack)
{
if (pRoot == nullptr) //到达外部结点nil
{
if (blackCount == pathBlack)//检查该路径上的黑色结点数量和基准值是否一样
return true;
else
{
cout << "data:" << pRoot->_data;
cout << " blackCount = " << blackCount << "and pathBlack = " << pathBlack << endl;
return false;
}
}
if (pRoot->_isRed && pRoot->_pParent->_isRed) //如果该结点和其双亲结点都为红色
{
cout << "data:" << pRoot->_data;
cout << " 有相邻的红色" << endl;
return false;
}
if (pRoot->_isRed == false) //统计到该路径为止黑色结点的数目
blackCount++;
return _checkRBTree(pRoot->_pLeft, blackCount, pathBlack) && _checkRBTree(pRoot->_pRight, blackCount, pathBlack);
}
// 左单旋
void rotateL(Node* pParent)
{
// grandparent
// parent
// sub
// subL
Node* pGrandParent = pParent->_pParent;
Node* sub = pParent->_pRight;
Node* subL = sub->_pLeft;
pParent->_pRight = subL;
if (subL)
subL->_pParent = pParent;
sub->_pLeft = pParent;
pParent->_pParent = sub;
if (pGrandParent == nullptr)
_root = sub;
else if (pGrandParent->_pLeft == pParent)
pGrandParent->_pLeft = sub;
else
pGrandParent->_pRight = sub;
sub->_pParent = pGrandParent;
}
// 右单旋
void rotateR(Node* pParent)
{
// grandparent
// parent
// sub
// subR
Node* pGrandParent = pParent->_pParent;
Node* sub = pParent->_pLeft;
Node* subR = sub->_pRight;
pParent->_pLeft = subR;
if (subR)
subR->_pParent = pParent;
sub->_pRight = pParent;
pParent->_pParent = sub;
if (pGrandParent == nullptr)
_root = sub;
else if (pGrandParent->_pLeft == pParent)
pGrandParent->_pLeft = sub;
else
pGrandParent->_pRight = sub;
sub->_pParent = pGrandParent;
}
//中序遍历
void _inOrder(Node* root)
{
if (root == nullptr)
return;
_inOrder(root->_pLeft);
cout << root->_data << " ";
_inOrder(root->_pRight);
}
int _height(Node* root)//计算树的高度
{
if (root == nullptr)
return 0;
int leftHeight = _height(root->_pLeft);
int rightHeight = _height(root->_pRight);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
int _size(Node* root) //计算树的结点个数
{
if (root == nullptr)
return 0;
return _size(root->_pLeft) + _size(root->_pRight) + 1;
}
private:
Node* _root;
};
Main.c
#include "RBTree.h"
//压力测试
void performanceTest()
{
const int N = 10000;
RBTree<int> tree;
clock_t start1 = clock();
for (int i = 0; i < N; i++)
{
tree.insert(i); //插入有序序列或随机值都行
}
cout << tree.checkRBTree() << endl;
clock_t end1 = clock();
cout << "插入用时:" << (float)(end1 - start1) / CLOCKS_PER_SEC << "秒" << endl;
cout << "高度:" << tree.height() << endl;
cout << "结点个数" << tree.size() << endl;
int count = 0;
start1 = clock();
for (int i = 0; i < N; i++)
{
if (!tree.find(i))
count++;
}
end1 = clock();
cout << "查找所有值用时:" << (float)(end1 - start1) / CLOCKS_PER_SEC << "秒" << endl;
cout << count << "个找不到!" << endl;
//查找随机值
count = 0;
start1 = clock();
for (int i = 0; i < N; i++)
{
if (!tree.find(rand()))
count++;
}
end1 = clock();
cout << "查找随机值用时:" << (float)(end1 - start1) / CLOCKS_PER_SEC << "秒" << endl;
cout << count << "个找不到!" << endl;
cout << tree.size() << endl;
}
int main()
{
performanceTest();
return 0;
}