在学习红黑树之前,先引入一个观点:红黑树的插入和删除效率高于AVL树,这是为什么?让我们带着疑问走进红黑树的世界
1.红黑树的概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的。
2.红黑树的性质
(1)每个节点不是红色就是黑色;
(2)根节点是黑色的;
(3)如果一个节点是红色的,则它的两个孩子节点是黑色的(红色节点不能连续);
(4)对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点 ;
(5) 每个叶子结点都是黑色的(此处的叶子结点指的是空结点);
思考:为什么红黑树的最长路径节点个数不超过最短路径节点个数的两倍?
因为红黑树每条路径黑色节点个数相同,最短路径全是黑色节点,最长路径是黑红相间的节点,所以不超过两倍。
3.红黑树节点的定义
红黑树的节点有五个成员,分别为:父节点指针,左孩子指针,右孩子指针,颜色,存储的数据
节点的颜色默认给红色,可以减少红黑树插入节点调整的次数。
节点的实现:
//红黑树
enum COLOR{
BLACK,
RED
};
//节点
template<class K,class V>
struct RBNode
{
RBNode<K, V>* _parent;
RBNode<K, V>* _left;
RBNode<K, V>* _right;
pair<K, V> _kv;//数据为键值对
COLOR _color;
//bool _color;//STL的实现
RBNode(const pair<K, V> kv=pair<K,V>())
:_parent(nullptr)
, _left(nullptr)
, _right(nullptr)
, _kv(kv)
, _color(RED)
{}
};
4.红黑树的实现
(1)红黑树的结构
红黑树中有一个头结点,因为根节点必须为黑色,为了与根节点进行区分,将头结点给成黑色,并且让头结点的父节点指针指向红黑树的根节点,左孩子指针指向红黑树中最小的节点,右孩子指针指向红黑树中最大的节点,结构图如下:
(2)红黑树的插入
第一步:找到合适的位置并插入节点;
第二步:进行节点颜色的调整或者结果的调整
因为新节点的默认颜色是红色,因此如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了不能有连在一起的红色节点的性质,此时需要对红黑树分情况来讨论:
约定:cur为当前节点,parent为父节点,gfather为祖父节点,uncle为叔叔节点
情况1
cur为红色,parent为红色,uncle存在也为红色,gfather为黑色
调整方法:
只需要将parent和uncle的颜色改为黑色,gfather颜色改为红色;
如果gfather节点为根节点,将gfather颜色改为黑色,
如果gfather节点不是根节点,cur更新到gfather,继续向上调整
演示图:
情况2
cur为红,parent为红,gfather为黑,uncle不存在/uncle为黑,cur和parent在gfather的同一侧
调整方法:
- parent为gfather的左孩子,cur为parent的左孩子,以parent为轴进行右旋;
- parent为gfather的右孩子,cur为parent的右孩子,以parent为轴进行左旋;
旋转之后parent调整为黑色,gfather调整为红色;
演示图:
左旋:
右旋:
情况3
cur为红,parent为红,gfather为黑,uncle不存在/uncle为黑,cur和parent不在gfather的同一侧
调整:
- parent为gfather的左孩子,cur为parent的右孩子
以cur为轴进行左旋,将parent调整到cur的左边;
交换cur和parent,此时就转换为情况2,以parent为轴进行右旋,将parent的颜色调整为黑色,将gfather的颜色调整为红色; - parent为gfather的右孩子,cur为parent的左孩子
以cur为轴进行右旋,将parent调整到cur的右边;
交换cur和parent,此时就转换成了情况2,以parent为轴进行左旋,将parent的颜色调整为黑色,将gfather的颜色调整为红色;
演示图:
1.parent为gfather的左孩子,cur为parent的右孩子,进行左右双旋
2.parent为gfather的右孩子,cur为parent的左孩子,进行右左双旋
(3)检验红黑树是否符合性质
红黑树的五条性质如下:
1. 节点颜色不是红色就是黑色;
2. 根节点颜色为黑色;
3. 红色节点不能连续;
4. 从每个节点出发到达叶子节点的所有路径的黑色节点个数相同;
5. 叶子节点的颜色必须为黑色(此处的叶子节点指的是空节点);
性质1一定满足,因为红黑树的数据结构的定义中,节点的颜色只有红色和黑色两种;
性质5不需要进行判断,因为空节点在红黑树中没有实际意义,只是为了表示路径的结束而存在的概念
因此只需要判断性质2、3、4
其中性质2只需要进行根节点颜色的判断,
性质3通过在扫描的过程中查看一下红色节点的父节点是否也是红色节点进行验证;
性质4的验证:
从根节点出发统计其中一条路径的黑色节点个数num,并判断从根节点出发的其它路径上的黑色节点个数是否和num相同
为什么只需要从从根结点出发验证黑色节点的个数就可以保证所有路径上的黑色节点个数相同?
因为从根节点出发的路径是整体,包含了局部,只有所有的局部都符合条件,整体才会符合条件,也就是说只有每个子树的根节点出发的路径上的黑色节点个数相同,父节点的路径上的黑色节点个数才会相同
(4)源码
1.0
RBTree.h:
#pragma once
#include <utility>
#include <stack>
#include <iostream>
using std::cout;
using std::endl;
using std::stack;
using std::pair;
//红黑树节点的颜色
enum Color
{
RED, //红色
BLACK //黑色
};
//红黑树节点
//节点是KV键值对
template<class K, class V>
struct TreeNode
{
pair<K, V> _val; //数据域
enum Color _color; //节点的颜色
TreeNode<K, V>* _parent; //父节点
TreeNode<K, V>* _left; //左孩子
TreeNode<K, V>* _right; //右孩子
TreeNode(const pair<K, V>& val = pair<K, V>(), enum Color color = RED)
:_val(val)
, _color(color)
, _parent(nullptr)
, _left(nullptr)
, _right(nullptr)
{}
};
template<class K, class V>
class RBTree
{
private:
typedef TreeNode<K, V> Node;
//头节点,它的父节点指向树的根节点,左孩子指向树中最小的节点,右孩子指向树中最大的节点
//这个头节点的作用类似于带头链表中的哑头节点
Node* _header;
public:
RBTree()
:_header(new Node())
{
_header->_parent = nullptr; //空树的父节点为空
_header->_left = _header->_right = _header; //空树的最大节点和最小节点指向自身
_header->_color = BLACK; //头节点的颜色设置为黑色
}
//插入节点
bool Insert(const pair<K, V>& val)
{
//1.寻找合适的插入位置并插入节点
Node* curNode = _header->_parent;
if (!curNode)
{
//根节点不存在,创建根节点
curNode = new Node(val);
curNode->_color = BLACK;
_header->_parent = curNode;
_header->_left = curNode;
_header->_right = curNode;
curNode->_parent = _header;
return true;
}
Node* parent = nullptr; //父节点
while (curNode)
{
parent = curNode;
//不允许存在重复节点,通过比较key值来进行判断
if (val.first == curNode->_val.first)
{
return false;
}
else if (val.first < curNode->_val.first)
{
curNode = curNode->_left;
}
else
{
curNode = curNode->_right;
}
}
curNode = new Node(val);
if (val.first < parent->_val.first)
{
parent->_left = curNode;
}
else
{
parent->_right = curNode;
}
curNode->_parent = parent;
//2.进行颜色或结构的调整
//调整的前提是当前节点的颜色和父节点的颜色都是红色,并且当前节点不是根节点
while (curNode != _header->_parent && curNode->_parent->_color == RED)
{
parent = curNode->_parent;
Node* gfather = parent->_parent; //祖父节点
Node* uncle = nullptr; //叔叔节点
if (parent == gfather->_left)
{
uncle = gfather->_right;
}
else
{
uncle = gfather->_left;
}
//2.1:父节点和叔叔节点的颜色都是红色,uncle节点是红色为调整提供了便利
//只需要进行颜色的调整,而不需要进行结构的调整
if (uncle && uncle->_color == RED)
{
//进行节点颜色的调整
//将父节点和叔叔节点的颜色调整为黑色,将祖父节点的颜色调整为红色
parent->_color = uncle->_color = BLACK;
gfather->_color = RED;
//更新到祖父节点,进一步判断是否需要继续向上调整
curNode = gfather;
}
//2.2:父节点颜色为红色,叔叔节点不存在或者颜色为黑色
else
{
//2.2.1:父节点和插入的节点在gfather的同一侧
if ((curNode == parent->_left && parent == gfather->_left)
|| (curNode == parent->_right && parent == gfather->_right))
{
//2.2.1.1:父节点和插入的节点都在gfather的左侧
if (curNode == parent->_left && parent == gfather->_left)
{
RotateR(parent); //右旋,以parent为轴
//进行颜色的调整
parent->_color = BLACK;
gfather->_color = RED;
}
//2.2.1.2:父节点和插入的节点都在gfather的右侧
else
{
RotateL(parent); //左旋,以parent为轴
//进行颜色的调整
parent->_color = BLACK;
gfather->_color = RED;
}
}
//2.2.2:父节点和插入的节点不在同一侧
else
{
//2.2.2.1:插入的节点在parent的右侧,先左旋再右旋
if (curNode == parent->_right)
{
//a.先进行左旋,将连续的红色节点调整到同一侧
RotateL(curNode);
//b.交换curNode和parent节点,便于后续的调整
Node* tmp = curNode;
curNode = parent;
parent = tmp;
//3.以parent为轴,进行右旋
RotateR(parent);
//进行颜色的调整
parent->_color = BLACK;
gfather->_color = RED;
}
//2.2.2.2:插入的节点在parent的左侧,先右旋再左旋
else
{
//a.先进行右旋,将连续的红色节点调整到同一侧
RotateR(curNode);
//b.交换curNode和parent节点,便于后续的调整
Node* tmp = curNode;
curNode = parent;
parent = tmp;
//3.以parent为轴,进行左旋
RotateL(parent);
//进行颜色的调整
parent->_color = BLACK;
gfather->_color = RED;
}
}
//调整之后,gfather的颜色为黑色,不需要继续向上调整,调整结束
break;
}
}
//调整根节点的颜色
if (_header->_parent->_color == RED)
{
_header->_parent->_color = BLACK; //将根节点颜色修改为黑色
}
_header->_left = LeftMost(); //头节点的左边指向最小的节点,也就是最左孩子
_header->_right = RightMost(); //头节点的右边指向最大的节点,也就是最右孩子
return true;
}
//中序遍历
void Inorder()
{
stack<Node*> st;
Node* curNode = _header->_parent;
while (curNode || !st.empty())
{
while (curNode)
{
st.push(curNode);
curNode = curNode->_left;
}
Node* top = st.top();
st.pop();
cout << top->_val.first << ": " << top->_val.second << "\t";
if (top->_color == RED)
{
cout << "Red";
}
else
{
cout << "Black";
}
cout << endl;
curNode = top->_right;
}
}
//判断红黑树的结构是否正确
//红黑树的五条性质如下:
/*
1.节点颜色不是红色就是黑色;
2.根节点颜色为黑色;
3.红色节点不能连续;
4.从每个节点出发到达叶子节点的所有路径的黑色节点个数相同;
5.叶子节点的颜色必须为黑色(此处的叶子节点指的是空节点);
性质1一定满足,因为红黑树的数据结构的定义中,节点的颜色只有红色和黑色两种;
性质5不需要进行判断,因为空节点在红黑树中没有实际意义,只是为了表示路径的结束而存在的概念
因此只需要判断性质2、3、4
*/
bool IsValidRBTree()
{
Node* root = _header->_parent; //根节点
//空树可以视为红黑树
if (!root)
{
return true;
}
//判断性质2是否满足
if (root->_color == RED)
{
return false;
}
//统计从根节点出发的一条路径上的黑色节点个数
int BlackNum = 0;
Node* curNode = root;
while (curNode)
{
if (curNode->_color == BLACK)
{
++BlackNum;
}
curNode = curNode->_left;
}
//判断是否符合性质3和性质4
int curNum = 1; //从根节点出发只有一个黑色节点
return IsValidRBTreeHelper(root->_left, BlackNum, curNum)
&& IsValidRBTreeHelper(root->_right, BlackNum, curNum);
}
private:
//左旋
//以node为轴进行旋转
void RotateL(Node* node)
{
Node* parent = node->_parent; //父节点,一定存在
Node* pparent = parent->_parent; //祖父节点,一定存在,但可能是哑头节点
Node* subL = node->_left; //左孩子,可能不存在
//修改节点指针的指向,有三组节点间指向需要修改
node->_parent = pparent; //第一组
if (pparent == _header)
{
//pparent为头节点,更新根节点
_header->_parent = node;
}
else
{
if (parent == pparent->_left)
{
pparent->_left = node;
}
else
{
pparent->_right = node;
}
}
node->_left = parent; //第二组
parent->_parent = node;
parent->_right = subL; //第三组
if (subL)
{
subL->_parent = parent;
}
/*
* //在这里我犯了一个错误,将旋转和颜色的调整放到了同一个模块,这导致旋转模块的代码无法复用
* //应该在旋转之后再添加颜色调整的操作
//修改节点的颜色
//将node改为黑色,parent改为红色
node->_color = BLACK;
parent->_color = RED;
*/
}
//右旋
//以node的左孩子为轴进行旋转
void RotateR(Node* node)
{
Node* parent = node->_parent; //父节点,一定存在
Node* pparent = parent->_parent; //祖父节点,一定存在,但有可能是哑头节点
Node* subR = node->_right; //右孩子
//进行节点指向的修改,有3组节点间指向需要修改
node->_right = parent; //1号位置
parent->_parent = node;
node->_parent = pparent; //2号位置
if (pparent == _header)
{
//如果pparent为头节点,更新根节点
_header->_parent = node;
}
else
{
if (parent == pparent->_left)
{
pparent->_left = node;
}
else
{
pparent->_right = node;
}
}
parent->_left = subR; //3号位置
if (subR)
{
subR->_parent = parent;
}
/*
//颜色的修改应该和旋转操作相分离
//修改节点的颜色
node->_color = BLACK;
parent->_color = RED;
*/
}
//查找最左节点
Node* LeftMost()
{
Node* node = _header->_parent;
while (node->_left)
{
node = node->_left;
}
return node;
}
//查找最右节点
Node* RightMost()
{
Node* node = _header->_parent;
while (node->_right)
{
node = node->_right;
}
return node;
}
//判断从根节点出发的路径上的黑色节点个数是否相同,并且是否存在红色节点连续的情况
bool IsValidRBTreeHelper(Node* root, const int& BlackNum, int curNum)
{
//走到空节点,说明一条路径走到了终点
if (!root)
{
if (curNum == BlackNum)
{
return true;
}
else
{
return false;
}
}
//判断是否存在红色节点连续的情况
if (root->_parent->_color == RED && root->_color == RED)
{
return false;
}
if (root->_color == BLACK)
{
++curNum;
}
return IsValidRBTreeHelper(root->_left, BlackNum, curNum)
&& IsValidRBTreeHelper(root->_right, BlackNum, curNum);
}
};
main.cpp:
#include <iostream>
#include "RBTree.h"
#include <cstdlib>
#include <ctime>
using namespace std;
void test01()
{
RBTree<int, int> tree;
tree.Insert(make_pair(10, 1));
tree.Insert(make_pair(9, 1));
tree.Insert(make_pair(8, 1));
tree.Insert(make_pair(7, 1));
tree.Insert(make_pair(0, 1));
tree.Insert(make_pair(1, 1));
tree.Insert(make_pair(3, 1));
tree.Insert(make_pair(4, 1));
tree.Inorder();
}
void test02()
{
RBTree<int, int> tree;
tree.Insert(make_pair(1, -1));
tree.Insert(make_pair(3, -1));
tree.Insert(make_pair(5, -1));
tree.Insert(make_pair(7, -1));
tree.Insert(make_pair(6, -1));
tree.Insert(make_pair(4, -1));
tree.Insert(make_pair(2, -1));
tree.Insert(make_pair(0, -1));
tree.Insert(make_pair(10, -1));
tree.Inorder();
}
void test03()
{
srand(time(NULL));
int num;
cout << "请输入要插入的节点个数: ";
cin >> num;
RBTree<int, int> tree;
for (int i = 0; i < num; ++i)
{
int val = rand() % 100;
cout << "插入的数据是 " << val << endl;
tree.Insert(make_pair(val, 1024));
}
tree.Inorder();
}
int main()
{
//test01();
//test02();
test03();
return 0;
}
2.0
#include<iostream>
#include<stack>
#include<utility>
using namespace std;
//红黑树
enum COLOR{
BLACK,
RED
};
//节点
template<class K,class V>
struct RBNode
{
RBNode<K, V>* _parent;
RBNode<K, V>* _left;
RBNode<K, V>* _right;
pair<K, V> _kv;//数据为键值对
COLOR _color;
//bool _color;//STL的实现
RBNode(const pair<K, V> kv=pair<K,V>())
:_parent(nullptr)
, _left(nullptr)
, _right(nullptr)
, _kv(kv)
, _color(RED)
{}
};
template<class K,class V>
class RBTree
{
public:
typedef RBNode<K, V> Node;
RBTree()
:_header(new Node)
{
//带头的空的红黑树
_header->_left = _header->_right = _header;
}
/*
parent
subR
subRL
左旋后:
subR
parent
subRL
*/
//以parent的右孩子为轴进行左旋
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
subR->_left = parent;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
//判断根
if (parent == _header->_parent)
{
_header->_parent = subR;
subR->_parent = _header;
}
else
{
Node* pparent = parent->_parent;
if (parent == pparent->_left)
pparent->_left = subR;
else
pparent->_right = subR;
subR->_parent = pparent;
}
parent->_parent = subR;
}
/*
parent
subL
subLR
右旋后:
subL
parent
subLR
*/
//以parent的左孩子为轴进行右旋
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
subL->_right = parent;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
if (parent == _header->_parent)
{
_header->_parent = subL;
subL->_parent = _header;
}
else
{
Node* pparent = parent->_parent;
if (parent == pparent->_left)
pparent->_left = subL;
else
pparent->_right = subL;
subL->_parent = pparent;
}
parent->_parent = subL;
}
Node* leftMost()
{
Node* cur = _header->_parent;
while (cur && cur->_left)
cur = cur->_left;
return cur;
}
Node* rightMost()
{
Node* cur = _header->_parent;
while (cur && cur->_right)
cur = cur->_right;
return cur;
}
bool insert(const pair<K,V>& kv)
{
//1.搜索树的插入
//a.空树
if (_header->_parent == nullptr)
{
//创建根节点
Node* root = new Node(kv);
//根节点颜色为黑色
root->_color = BLACK;
_header->_parent = root;
root->_parent = _header;
_header->_left = _header->_right = root;
return true;
}
//b.非空树
Node* parent = nullptr;
//从根节点开始搜索
Node* cur = _header->_parent;
while (cur)
{
parent = cur;
if (cur->_kv.first == kv.first)
return false;//键值重复,插入失败
else if (cur->_kv.first > kv.first)
cur = cur->_left;
else
cur = cur->_right;
}
//新插入结点的颜色为红色
cur = new Node(kv);
if (parent->_kv.first > cur->_kv.first)
parent->_left = cur;
else
parent->_right = cur;
cur->_parent = parent;
//2.修改颜色或者调整结构,判断是否有红色连续的节点
while (cur != _header->_parent && cur->_parent->_color == RED)
{
//当前节点不为根,并且当前节点的父节点是红色
parent = cur->_parent;
Node* gfather = parent->_parent;//父节点颜色为红色,说明父节点不是根节点,存在祖父节点
if (gfather->_left == parent)
{
Node* uncle = gfather->_right;
//uncle节点存在,并且颜色是红色的
if (uncle && uncle->_color == RED)
{
parent->_color = uncle->_color = BLACK;
gfather->_color = RED;
//继续更新
cur = gfather;
}
else
{
//cout << "Rotate---------" << endl;
//判断是否是双旋的场景
if (parent->_right == cur)
{
RotateL(parent);//左旋
swap(parent, cur);//交换parent和cur的指向,退化为右旋的场景
}
//uncle节点不存在或者uncle节点存在,但是颜色为黑色
RotateR(gfather);//右旋----左边的左边是连续的红色
//修改颜色
parent->_color = BLACK;
gfather->_color = RED;
break;//调整结束
}
}
else
{
Node* uncle = gfather->_left;
//uncle节点存在,并且颜色是红色的
if (uncle && uncle->_color == RED)
{
parent->_color = uncle->_color = BLACK;
gfather->_color = RED;
//继续更新
cur = gfather;
}
else
{
//cout << "Rotate---------" << endl;
//判断是否是双旋的场景
if (parent->_left == cur)
{
RotateR(parent);//右旋
swap(parent, cur);//交换parent和cur的指向,退化为左旋的场景
}
//uncle节点不存在或者uncle节点存在,但是颜色为黑色
RotateL(gfather);//左旋----右边的右边是连续的红色
//修改颜色
parent->_color = BLACK;
gfather->_color = RED;
break;//调整结束
}
}
}
//3.将根节点颜色置为黑色
if(_header->_parent->_color == RED)
{
_header->_parent->_color = BLACK;
}
//4.更新_header的左右指向
_header->_left = leftMost();
_header->_right = rightMost();
return true;
}
void inorder()
{
Node* cur = _header->_parent;
stack<Node*> st;
while (cur || !st.empty())
{
while (cur)
{
st.push(cur);
cur = cur->_left;
}
Node* top = st.top();
cout << top->_kv.first << " ";
st.pop();
cur = top->_right;
}
cout << endl;
}
/*
红黑树的三个性质:
1.根节点的颜色是黑色;
2.每条路径黑色节点个数个数相同;
3.红色不能连续;
*/
bool isValidTree()
{
if (_header->_parent == nullptr)
return true;//空树也默认为红黑树
Node* root = _header->_parent;
if (root->_color == RED)
return false;
int bCount = 0;
Node* cur = root;
while (cur)
{
if (cur->_color == BLACK)
bCount++;
cur = cur->_left;
}
//遍历每一条路径的黑色节点数,并检查是否有红色节点连续的情况
int curCount = 0;
return isBalance(root, bCount, curCount);
}
bool isBalance(Node* root,int& bCount,int curCount)
{
//当节点走到空处,说明一条路径遍历结束
if (root == nullptr)
{
//判断黑色节点个数是否相同
if (bCount == curCount)
return true;
else
return false;
}
if (root->_color == BLACK)
curCount++;//累加黑色节点个数
//存在红色连续,返回false
if (root->_parent && root->_color == RED
&& root->_parent->_color == RED)
{
cout << "false data:" << root->_kv.first << endl;
return false;
}
return isBalance(root->_left, bCount, curCount)
&& isBalance(root->_right, bCount, curCount);
}
private:
Node* _header;
};
//void test()
//{
// RBTree<int, int> tree;
// tree.insert(make_pair(10, 2));
// tree.insert(make_pair(15, 2));
// tree.insert(make_pair(5, 2));
// tree.insert(make_pair(2, 2));
// tree.inorder();
//}
void test()
{
RBTree<int, int> tree;
int n;
cin >> n;
for (int i = n; i > 0; --i)
tree.insert(make_pair(i, i));
tree.inorder();
cout << "Is it a valid RBTree ? "<< tree.isValidTree() << endl;
}
int main()
{
test();
return 0;
}