红黑树和AVL树类似,是对搜索树的优化。不同于AVL树的绝对平衡,红黑树是近似平衡,即对于每个节点的以它为根的所有路径,其中最长路径的节点数不大于最短路径节点数的2倍。
以上特性,就是红黑树近似平衡的原因:
为什么要有红黑树?下面通过红黑树和搜索树、平衡树的比较来回答这个问题。
当插入有序数据时,搜索树会退化为单枝树从而导致查找效率降为线性。红黑树通过对各个节点颜色的控制和位置的调整,使得树永远处于近似平衡状态,保证了O(logN)的查找效率。
二者都是对搜索树进行了平衡处理
红黑树不追求绝对平衡,相对平衡树而言降低了插入和删除时旋转的次数,频繁的插入删除红黑树的性能更优,并且红黑树的实现也较为简单,所以实际运用中红黑树更多。如果插入删除的操作较少且需要频繁的查找,那么平衡树性能更优。
主要定义里两个类:节点类 + 树的本体,其中前者存储节点节点的相关信息,后者主要实现红黑树的功能,如插入、删除、查找等。
数据域:颜色(默认为红)、pair键值对
指针域:左孩子和右孩子、父亲
// 枚举定义颜色
enum COLOR
{
RED,
BLACK
};
// 节点的定义
template<class k, class v>
struct RBTreeNode
{
// 构造函数,颜色默认给为红色
RBTreeNode(const pair<k, v>& kv)
:_color(RED)
,_kv(kv)
,_parent(nullptr)
,_left(nullptr)
,_right(nullptr)
{}
COLOR _color;
pair<k, v> _kv;
RBTreeNode<k, v>* _parent;
RBTreeNode<k, v>* _left;
RBTreeNode<k, v>* _right;
};
为什么颜色默认为红色?
因为红色处理起来更简单,对其他路径的波及较小。如果为黑色,想想看这条路径的黑色节点数量加了一个,那么其他路径都要调整来保证所有路径的黑色节点个数一致;但是如果置为红色,这时只有插入位置的父亲也为红才需要调整,即使调整也只需调整一条路径,比较容易。
只有一个成员变量就是一个节点的指针,代表树的根节点。也为只有一个成员变量,可以不用写构造函数,我们直接声明时给缺省值即可,就是把它置为nullptr。
// 树的定义
template<class k, class v>
class RBTree
{
public:
typedef RBTreeNode<k, v> Node;
private:
Node* _root = nullptr;// 声明时给缺省值
};
按搜索树的性质来找到插入的位置,然后直接插入:
template<class k, class v>
class RBTree
{
public:
typedef RBTreeNode<k, v> Node;
bool Insert(const pair<k, v>& kv)
{
// 第一步:按搜索树规则插入一个节点
// 1、空树的话插入节点作为根节点
// 2、不空的话,按搜索树规则找到插入的位置,然后插入
if(!_root)
{
_root=new Node(kv);
_root->_color=BLACK;
return true;
}
// 寻找插入的位置
Node* parent=nullptr;
Node* cur=_root;
while(cur)
{
parent=cur;
if(cur->_kv.first == kv.first)
{
return false;
}
else if(cur->_kv.first < kv.first)
{
cur=cur->_right;
}
else if(cur->_kv.first > kv.first)
{
cur=cur->_left;
}
}
// 找到后插入
Node* newNode=new Node(kv);
newNode->_parent=parent;
if(parent->_kv.first > kv.first)
{
parent->_left=newNode;
}
else if(parent->_kv.first < kv.first)
{
parent->_right=newNode;
}
// 未完待续.....
private:
Node* _root=nullptr;
};
走到了这一步,那么插入节点肯定不是整棵树的根了,因为空树情况在第一步里面已经处理好了,也就是说现在插入的新节点一定有父亲。根据父亲的颜色分两种情况处理:
针对情况二,我们需要知道,这时一定是有祖父节点的,即父亲的父亲一定存在且为黑色,因为父亲的颜色为红不可能是根节点,又因为红色不连续所以祖父为黑。这时我们的调整取决于叔叔,当然叔叔可能存在也可能不存在,我们分情况处理。
约定插入节点为cur,父亲为parent,祖父为gfather,叔叔为uncle,此时:
// 拼接到第一步代码后面
// 第二步:调整节点的颜色
// 1、如果父亲颜色为黑就不用调整
// 2、如果父亲节点颜色为红,调整方式由叔叔决定
// a、叔叔存在且为红 --> 把叔叔和父亲的颜色调整为黑,爷爷颜色调整为红,然后继续往上调整
// b、叔叔不存在或存在且为黑 --> 看爷爷、父亲、和cur的相对位置来决定用什么旋转,旋转后交换父亲和爷爷的颜色
cur=newNode;
while(parent && parent->_color == RED)
{
// 既然进来了这个循环内部,说明父亲一定是红色,那么一定有爷爷且爷爷颜色为黑
Node* grandfather = parent->_parent;
// 确定叔叔的位置
if(grandfather->_left == parent)
{
Node* uncle=grandfather->_right;
if(uncle && uncle->_color == RED)// 叔叔存在且为红
{
parent->_color=uncle->_color=BLACK;
grandfather->_color=RED;
cur=grandfather;
parent=cur->_parent;
}
else // 叔叔不存在或者存在且为黑
{
if(cur == parent->_right)
{
RotateL(parent);
std::swap(parent,cur);
}
RotateR(grandfather);
parent->_color=BLACK;
grandfather->_color=RED;
break;
}
}
else if(grandfather->_right == parent)
{
Node* uncle=grandfather->_left;
if(uncle && uncle->_color==RED)// 叔叔存在且为红
{
uncle->_color=parent->_color=BLACK;
grandfather->_color=RED;
cur=grandfather;
parent=cur->_parent;
}
else // 叔叔不存在或存在且为黑
{
if(cur == parent->_left)
{
RotateR(parent);
std::swap(parent,cur);
}
RotateL(grandfather);
parent->_color=BLACK;
grandfather->_color=RED;
break;
}
}
}
_root->_color=BLACK;
}
// 左单旋
void RotateL(Node* parent)
{
Node* subR=parent->_right;
Node* subRL=subR->_left;
parent->_right=subRL;
if(subRL != nullptr)
{
subRL->_parent=parent;
}
subR->_left=parent;
Node* pparent=parent->_parent;
parent->_parent=subR;
if(pparent == nullptr)
{
_root=subR;
subR->_parent=nullptr;
}
else
{
subR->_parent=pparent;
if(pparent->_left == parent)
{
pparent->_left=subR;
}
else if(pparent->_right == parent)
{
pparent->_right=subR;
}
}
}
// 右单旋
void RotateR(Node* parent)
{
Node* subL=parent->_left;
Node* subLR=subL->_right;
parent->_left=subLR;
if(subLR != nullptr)
{
subLR->_parent=parent;
}
subL->_right=parent;
Node* pparent=parent->_parent;
parent->_parent=subL;
if(pparent == nullptr)
{
_root=subL;
subL->_parent=nullptr;
}
else
{
subL->_parent=pparent;
if(pparent->_left == parent)
{
pparent->_left=subL;
}
else if(pparent->_right == parent)
{
pparent->_right=subL;
}
}
#pragma once
#include
using std::cin;
using std::cout;
using std::endl;
using std::pair;
// 枚举定义颜色
enum COLOR
{
RED,
BLACK
};
// 节点的定义
template<class k, class v>
struct RBTreeNode
{
// 构造函数,颜色默认给为红色
RBTreeNode(const pair<k, v>& kv)
:_color(RED)
,_kv(kv)
,_parent(nullptr)
,_left(nullptr)
,_right(nullptr)
{}
COLOR _color;
pair<k, v> _kv;
RBTreeNode<k, v>* _parent;
RBTreeNode<k, v>* _left;
RBTreeNode<k, v>* _right;
};
// 树的定义
template<class k, class v>
class RBTree
{
public:
typedef RBTreeNode<k, v> Node;
// 插入一个节点
bool Insert(const pair<k, v>& kv)
{
// 第一步:按搜索树规则插入一个节点
// 1、空树的话插入节点作为根节点
// 2、不空的话,按搜索树规则找到插入的位置,然后插入
if(!_root)
{
_root=new Node(kv);
_root->_color=BLACK;
return true;
}
// 寻找插入的位置
Node* parent=nullptr;
Node* cur=_root;
while(cur)
{
parent=cur;
if(cur->_kv.first == kv.first)
{
return false;
}
else if(cur->_kv.first < kv.first)
{
cur=cur->_right;
}
else if(cur->_kv.first > kv.first)
{
cur=cur->_left;
}
}
// 找到后插入
Node* newNode=new Node(kv);
newNode->_parent=parent;
if(parent->_kv.first > kv.first)
{
parent->_left=newNode;
}
else if(parent->_kv.first < kv.first)
{
parent->_right=newNode;
}
// 第二步:调整节点的颜色
// 1、如果父亲颜色为黑就不用调整
// 2、如果父亲节点颜色为红,调整方式由叔叔决定
// a、叔叔存在且为红 --> 把叔叔和父亲的颜色调整为黑,爷爷颜色调整为红,然后继续往上调整
// b、叔叔不存在或存在且为黑 --> 看爷爷、父亲、和cur的相对位置来决定用什么旋转,旋转后交换父亲和爷爷的颜色
cur=newNode;
while(parent && parent->_color == RED)
{
// 既然进来了这个循环内部,说明父亲一定是红色,那么一定有爷爷且爷爷颜色为黑
Node* grandfather = parent->_parent;
// 确定叔叔的位置
if(grandfather->_left == parent)
{
Node* uncle=grandfather->_right;
if(uncle && uncle->_color == RED)// 叔叔存在且为红
{
parent->_color=uncle->_color=BLACK;
grandfather->_color=RED;
cur=grandfather;
parent=cur->_parent;
}
else // 叔叔不存在或者存在且为黑
{
if(cur == parent->_right)
{
RotateL(parent);
std::swap(parent,cur);
}
RotateR(grandfather);
parent->_color=BLACK;
grandfather->_color=RED;
break;
}
}
else if(grandfather->_right == parent)
{
Node* uncle=grandfather->_left;
if(uncle && uncle->_color==RED)// 叔叔存在且为红
{
uncle->_color=parent->_color=BLACK;
grandfather->_color=RED;
cur=grandfather;
parent=cur->_parent;
}
else // 叔叔不存在或存在且为黑
{
if(cur == parent->_left)
{
RotateR(parent);
std::swap(parent,cur);
}
RotateL(grandfather);
parent->_color=BLACK;
grandfather->_color=RED;
break;
}
}
}
_root->_color=BLACK;
}
private:
// 左单旋
void RotateL(Node* parent)
{
Node* subR=parent->_right;
Node* subRL=subR->_left;
parent->_right=subRL;
if(subRL != nullptr)
{
subRL->_parent=parent;
}
subR->_left=parent;
Node* pparent=parent->_parent;
parent->_parent=subR;
if(pparent == nullptr)
{
_root=subR;
subR->_parent=nullptr;
}
else
{
subR->_parent=pparent;
if(pparent->_left == parent)
{
pparent->_left=subR;
}
else if(pparent->_right == parent)
{
pparent->_right=subR;
}
}
}
// 右单旋
void RotateR(Node* parent)
{
Node* subL=parent->_left;
Node* subLR=subL->_right;
parent->_left=subLR;
if(subLR != nullptr)
{
subLR->_parent=parent;
}
subL->_right=parent;
Node* pparent=parent->_parent;
parent->_parent=subL;
if(pparent == nullptr)
{
_root=subL;
subL->_parent=nullptr;
}
else
{
subL->_parent=pparent;
if(pparent->_left == parent)
{
pparent->_left=subL;
}
else if(pparent->_right == parent)
{
pparent->_right=subL;
}
}
}
Node* _root=nullptr;
};