目录
红黑树的概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,一般是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,从而确保没有一条路径会比其他路径长出2倍,因而是接近平衡的。
红黑树的性质
- 每个结点不是红色就是黑色,根节点是黑色的。
- 如果一个节点是红色的,则它的两个孩子结点是黑色的,也就是 红色节点不能连续,黑色节点可以连续。
- 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点。
- 每个叶子结点都是黑色的(此处的叶子结点指的是空结点),为了让路径划分更加清晰。
一棵红黑树 : 最长路径:红黑相间 最短路径:全黑
红黑树实现
红黑树结构
为了后续实现关联式容器简单,红黑树的实现中增加一个头结点,因为跟节点必须为黑色,为了与根节点进行区分,将头结点给成黑色,并且让头结点的 _parent 域指向红黑树的根节点,_left域指向红黑树中最小的节点,_right域指向红黑树中最大的节点,如下:
红黑树结点定义
enum COLOR
{
BLACK,
RED
};
//节点存放<k,v>数据
template <class K,class V>
struct RBNode
{
RBNode<K, V>* _left;
RBNode<K, V>* _right;
RBNode<K, V>* _parent;
pair<K, V> _value;
COLOR _color;//颜色
RBNode(const pair<K, V>& value = pair<K, V>())
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _value(value)
, _color(RED)
{}
};
红黑树的插入
相比于AVL树,插入比较简单,效率比较高,红黑树比AVL树的调整次数要少。
1.按照二叉搜索的树规则插入新节点
2.检测新节点插入后,红黑树的性质是否造到破坏,判断有没有连续的红色结点
因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了不能有连续的红色节点的性质,因此就需要进行讨论调整。
约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点
情况一: cur为红,p为红,g为黑,u存在且为红
情况二: cur为红,p为红,g为黑,u不存在/u为黑
情况三: cur为红,p为红,g为黑,u不存在/u为黑
bool insert(const pair<K, V>& value)
{
//搜索树的插入
if (_header->_parent == nullptr)
{
//空树,创建根节点
pNode root = new Node(value);
root->_color = BLACK;
root->_parent = _header;
_header->_parent = root;
_header->_left = root;
_header->_right = root;
return true;
}
//从根开始搜索
pNode cur = _header->_parent;
pNode parent = nullptr;
while (cur)
{
parent = cur;
//按照key值确定位置, key不能重复
if (cur->_value.first == value.first)
return false;
else if (cur->_value.first > value.first)
cur = cur->_left;
else
cur = cur->_right;
}
cur = new Node(value);
if (parent->_value.first > cur->_value.first)
parent->_left = cur;
else
parent->_right = cur;
cur->_parent = parent;
//调整和更新(颜色): 连续的红色时需要调整
while (cur != _header->_parent && cur->_parent->_color == RED)
{
// cur, parent, gfather, uncle
parent = cur->_parent;
pNode gfather = parent->_parent;
if (gfather->_left == parent)
{
pNode uncle = gfather->_right;
//uncle 存在且为红
if (uncle && uncle->_color == RED)
{
//修改颜色
parent->_color = uncle->_color = BLACK;
gfather->_color = RED;
//继续向上更新
cur = gfather;
}
else{
//如果存在双旋的场景,可以先进行一次单旋,使它变成单旋的场景
if (cur == parent->_right)
{
RotateL(parent);
swap(cur, parent);
}
//右旋
RotateR(gfather);
//修改颜色
parent->_color = BLACK;
gfather->_color = RED;
//停止调整
break;
}
}
else
{
pNode uncle = gfather->_left;
if (uncle && uncle->_color == RED)
{
//修改颜色
uncle->_color = parent->_color = BLACK;
gfather->_color = RED;
cur = gfather;
}
else{
//判断是否有双旋的场景
if (cur == parent->_left)
{
RotateR(parent);
swap(cur, parent);
}
RotateL(gfather);
parent->_color = BLACK;
gfather->_color = RED;
break;
}
}
}
//根的颜色始终是黑的: _header->_parent
_header->_parent->_color = BLACK;
//更新 _header->_left, _header->_right
_header->_left = leftMost();
_header->_right = rightMost();
return true;
}
判断是否是红黑树
红黑树的检测分为两步:
- 1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
- 2. 检测其是否满足红黑树的性质
bool isRBTree()
{
pNode root = _header->_parent;
if (root == nullptr)
return true;
if (root->_color == RED)
{
cout << "根节点必须是黑色的!" << endl;
return false;
}
//根节点是黑色
//需要判断每条路径上黑色个数相同
//可以先任意遍历一条路径 比如走最右路径。查找black数量
pNode cur = root;
int blackCount = 0;
while (cur)
{
if (cur->_color == BLACK)
++blackCount;
cur = cur->_right;
}
int k = 0;
return _isRBTree(root, k ,blackCount);
}
bool _isRBTree(pNode root, int curBlackCount, int totalBlackCout)//curBlackCount:走到当前节点黑色个数
{
//每条路径上黑色个数相同//没有连续红色结点
//一条路径走完
if (root == nullptr)
{
if (curBlackCount != totalBlackCout)
{
cout << "每条路径黑色结点个数不同" << endl;
return false;
}
return true;
}
红黑树与AVL树的比较,应用
红黑树与AVL树对比
红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O( log2 N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。
红黑树的应用
- 1. C++ STL库 -- map/set、mutil_map/mutil_set
- 2. Java 库
- 3. linux内核
- 4. 其他一些库
整体代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include<time.h>
#include<utility>
#include<iostream>
using namespace std;
enum COLOR
{
BLACK,
RED
};
//节点存放<k,v>数据
template <class K,class V>
struct RBNode
{
RBNode<K, V>* _left;
RBNode<K, V>* _right;
RBNode<K, V>* _parent;
pair<K, V> _value;
COLOR _color;//颜色
RBNode(const pair<K, V>& value = pair<K, V>())
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _value(value)
, _color(RED)
{}
};
template <class K, class V>
class RBTree
{
public:
typedef RBNode<K, V> Node;
typedef Node* pNode;
RBTree()
{
//构建空的红黑树 空树--》带头的红黑树,头不是根
_header = new Node();
_header->_left = _header;
_header->_right = _header;
}
/*
红黑树插入:
1.相对于AVL树,插入比较简单,且效率高,红黑树比AVL树调整次数要少
2.二叉树进行插入
3.判断有没有连续的红色结点
如果有:
a:只需要修改颜色: uncle为红色
b:修改颜色,旋转:u不存在、存在且为黑
单旋:cur,parent在gfather的同一边
双旋:cur,parent不在gfather的同一边,首先经过一次单璇,交换指针,转化为上面单璇场景,
没有:
不需要做任何操作,插入结束。
*/
bool insert(const pair<K, V>& value)
{
//搜索树的插入
if (_header->_parent == nullptr)
{
//空树,创建根节点
pNode root = new Node(value);
root->_color = BLACK;
root->_parent = _header;
_header->_parent = root;
_header->_left = root;
_header->_right = root;
return true;
}
//从根开始搜索
pNode cur = _header->_parent;
pNode parent = nullptr;
//查找插入的位置
while (cur)
{
parent = cur;
//按照key值确定位置, key不能重复
if (cur->_value.first == value.first)
return false;
else if (cur->_value.first > value.first)
cur = cur->_left;
else
cur = cur->_right;
}
//节点创建
cur = new Node(value);
//节点插入
if (parent->_value.first > cur->_value.first)
parent->_left = cur;
else
parent->_right = cur;
//节点连接
cur->_parent = parent;
//调整和更新(颜色):连续红色需要调整
while (cur != _header->_parent && cur->_parent->_color == RED)//当前不是根,并且你的父亲是红色
{
//cur:当前节点,parent:父亲节点, gfather:祖父节点,uncle:叔叔节点
parent = cur->_parent;
pNode gfather = parent->_parent;
if (gfather->_left == parent)
{
pNode uncle = gfather->_right;
//uncle 存在且为红
if (uncle && uncle->_color == RED)
{
//修改颜色
parent->_color = uncle->_color = BLACK;
gfather->_color = RED;
//继续向上更新
cur = gfather;
}
else
{
//如果存在双旋的场景,可以先进行一次单旋,使它变成单旋的场景
if (cur == parent->_right)
{
RotateL(parent);
swap(cur, parent);
}
//右旋
RotateR(gfather);
//修改颜色
parent->_color = BLACK;
gfather->_color = RED;
//停止调整
break;
}
}
//gfather->_right == parent
else
{
pNode uncle = gfather->_left;
if (uncle && uncle->_color == RED)
{
//修改颜色
uncle->_color = parent->_color = BLACK;
gfather->_color = RED;
cur = gfather;
}
else
{
//判断是否有双旋的场景
if (cur == parent->_left)
{
//以parent右旋
RotateR(parent);
//交换指针
swap(cur, parent);
}
//以gfather 左旋
RotateL(gfather);
//修改颜色
parent->_color = BLACK;
gfather->_color = RED;
//停止调整
break;
}
}
}
//根的颜色始终是黑的 根:_header->_parent
_header->_parent->_color = BLACK;
//更新 _header->_left, _header->_right
_header->_left = leftMost();
_header->_right = rightMost();
return true;
}
pNode leftMost()
{
pNode cur = _header->_parent;
while (cur && cur->_left != nullptr)
{
cur = cur->_left;
}
return cur;
}
pNode rightMost()
{
pNode cur = _header->_parent;
while (cur && cur->_right != nullptr)
{
cur = cur->_right;
}
return cur;
}
void RotateR(pNode parent)
{
pNode subL = parent->_left;
pNode subLR = subL->_right;
// 1
subL->_right = parent;
// 2
parent->_left = subLR;
// 3
if (subLR)
subLR->_parent = parent;
// 4, 5
if (parent != _header->_parent)
{
// subL <---> parent->parent
pNode gParent = parent->_parent;
if (gParent->_left == parent)
gParent->_left = subL;
else
gParent->_right = subL;
subL->_parent = gParent;
}
else
{
//更新根节点
_header->_parent = subL;
//subL->_parent = nullptr;
subL->_parent = _header;
}
// 6
parent->_parent = subL;
}
void RotateL(pNode parent)
{
pNode subR = parent->_right;
pNode subRL = subR->_left;
subR->_left = parent;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
if (parent != _header->_parent){
pNode gParent = parent->_parent;
if (gParent->_left == parent)
gParent->_left = subR;
else
gParent->_right = subR;
subR->_parent = gParent;
}
else
{
_header->_parent = subR;
//根的父节点不是nullptr
//subR->_parent = nullptr;
subR->_parent = _header;
}
parent->_parent = subR;
}
void inOrder()
{
_inOrder(_header->_parent);
cout << endl;
}
void _inOrder(pNode root)
{
if (root){
_inOrder(root->_left);
cout << "< " << root->_value.first << "--->" << root->_value.second << "> ";
_inOrder(root->_right);
}
}
bool isRBTree()
{
pNode root = _header->_parent;
if (root == nullptr)
return true;
if (root->_color == RED)
{
cout << "根节点必须是黑色的!" << endl;
return false;
}
//根节点是黑色
//需要判断每条路径上黑色个数相同
//可以先任意遍历一条路径 比如走最右路径。查找black数量
pNode cur = root;
int blackCount = 0;
while (cur)
{
if (cur->_color == BLACK)
++blackCount;
cur = cur->_right;
}
int k = 0;
return _isRBTree(root, k ,blackCount);
}
bool _isRBTree(pNode root, int curBlackCount, int totalBlackCout)//curBlackCount:走到当前节点黑色个数
{
//每条路径上黑色个数相同//没有连续红色结点
//一条路径走完
if (root == nullptr)
{
if (curBlackCount != totalBlackCout)
{
cout << "每条路径黑色结点个数不同" << endl;
return false;
}
return true;
}
if (root->_color == BLACK)
++curBlackCount;
//没有红色连续
pNode parent = root->_parent;
if ( parent->_color == RED && root->_color == RED)
{
cout << "有红色连续的结点" << endl;
return false;
}
return _isRBTree(root->_left, curBlackCount, totalBlackCout) && _isRBTree(root->_right, curBlackCount, totalBlackCout);
}
private:
pNode _header;
};
void testRBTree()
{
RBTree<int, int> rbt;
rbt.insert(make_pair(1, 1));
rbt.insert(make_pair(10, 1));
rbt.insert(make_pair(-1, 1));
rbt.insert(make_pair(-2, 1));
rbt.insert(make_pair(100, 1));
rbt.insert(make_pair(19, 1));
rbt.insert(make_pair(21, 1));
rbt.inOrder();
cout << rbt.isRBTree() << endl;
}
void test()
{
srand(time(nullptr));
int n;
cin >> n;
RBTree<int, int> rbt;
for (int i = 0; i < n;++i)
{
int key = rand();
rbt.insert(make_pair(key, key));
//cout << key << " ";
}
cout << endl;
cout << "isREDTree: " << rbt.isRBTree() << endl;
}
int main()
{
//testRBTree();
test();
system("pause");
return 0;
}