1.红黑树的概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的。
2.红黑树的性质
- 每个节点不是红色就是黑色
- 根结点是黑色的
- 如果一个结点是红色的,则它的两个孩子是黑色的
- 对于每个节点,从该节点到其后代叶结点的简单路径上,均包含相同数目的黑结点。
- 每个叶子结点的都是黑色的(此处叶子结点指的是空节点)
思考:为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍?
答案:最短的路径上节点的颜色全部都为黑色;最长的路径则为黑红交叉的路径,其上有与最短路径的黑节点数目相同的黑节点数和红节点数目。所以我们按照红黑树性质所建立的红黑树的最长路径必然不会超过最短路径的两倍!
3.红黑树结点的定义
//节点的颜色
enum Color{RED,BLACK};
//结点的定义
template<class V>
struct RBTreeNode
{
RBTreeNode(const V& val = V(), Color color = RED)
:_pLeft(nullptr)
, _pRight(nullptr)
, _pParent(nullptr)
, _val(val)
,_color(color)
{}
RBTreeNode<V>* _pLeft; //节点的左孩子
RBTreeNode<V>* _pRight; //节点的右孩子
RBTreeNode<V>* _pParent; //节点的双亲
V _val; //结点的值域
Color _color; //节点的颜色
};
为什么插入的新节点默认是红色的?
原因是:插入黑节点必然会影响所有路径都含有相同数目的黑色节点这一原则,较难维护!
4.红黑树的结构
我们常用的STL容器,如map/multimap、set/multimap等容器的底层都是通过红黑树实现的,为了后续实现这些关联式容器,红黑树的实现中增加一个头结点,因为跟节点必须为黑色,为了与根节点进 行区分,将头结点给成红色,并且让头结点的 pParent 域指向红黑树的根节点,pLeft域指向红黑树中小的节点,_pRight域指向红黑树中大的节点,如下:
template<class V>
class RBTree
{
typedef RBTreeNode<V> Node;
public:
typedef RBTreeIterator<V> iterator;
public:
RBTree()
{
_pHead = new Node;
_pHead->_pLeft = _pHead;
_pHead->_pRight = _pHead;
}
private:
Node*& GetRoot()
{
return _pHead->_pParent;
}
private:
Node* _pHead;
};
5.红黑树的插入(重点)
红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:
1.按照二叉搜索树规则插入新的结点
2. 检测新节点插入后,红黑树的性质是否造到破坏
因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不 需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此 时需要对红黑树分情况来讨论:
一、父节点是祖父节点的左孩子
1.uncle结点存在且为红色
①当前节点cur是parent的左孩子
②当前节点cur是parent的右孩子
上面两种情况,均是将cur 的父亲节点和叔叔节点的颜色变为黑色,此时这两条路径上均多了一个黑色结点,因此将祖父结点的颜色变为红色。此时将cur指向祖父结点的位置,p指向祖父父亲节点的位置。继续向上更新。
2.uncle的颜色是黑色 或者 uncle为NULL
①cur是p的左孩子,右单旋
将p的结点变为黑色,g结点变为红色,然后对g结点进行右旋。
②cur是parent的右孩子,先左后右双旋
先将cur结点进行左旋,转换为第二种情况,此时与上面的其情况相同,将cur的颜色变为黑,g的颜色变为红,然后对g进行右旋。
二、父节点是祖父节点的右孩子
1.uncle的颜色是红色
①cur是parent的右孩子
②cur是parent的左孩子
2.uncle的颜色是黑色 或者 uncle为NULL
①cur是parent的右孩子
②cur是parent的左孩子
5.红黑树的验证
红黑树的检测分为两步:
1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
2. 检测其是否满足红黑树的性质
看下面的验证代码:
//是否有效的红黑树,通过性质进行检查
bool IsValidRBTree()
{
Node* pRoot = GetRoot();
if (nullptr == pRoot)
return true;
//性质二:
if (BLACK != pRoot->_color)
{
cout << "违反性质二:根节点的颜色不是黑色" << endl;
return false;
}
//性质三:不能有相连的红色结点
//获取一条路径中黑色结点的个数
Node* pCur = pRoot;
size_t blackCount = 0;
while (pCur)
{
if (BLACK == pCur->_color)
++blackCount;
pCur = pCur->_pLeft;
}
// 性质四:每条路径里面的黑色结点数目相同
size_t pathCount = 0;
return _IsValidRBTree(pRoot, blackCount, pathCount);
}
bool _IsValidRBTree(Node* pRoot, size_t blackCount,size_t pathCount)
{
if (nullptr == pRoot)
return true;
if (pRoot->_color == BLACK)
pathCount++;
Node* pParent = pRoot->_pParent;
if (pParent != _pHead && RED == pParent->_color && pRoot->_color == RED)
{
cout << "违反了性质三,有连在一起的红色结点" << endl;
return false;
}
if (nullptr == pRoot->_pLeft && pRoot->_pRight == nullptr)
{
if (pathCount != blackCount)
{
cout << "违反性质四:路径中黑色结点得个数不相同" << endl;
return false;
}
}
return _IsValidRBTree(pRoot->_pLeft, blackCount, pathCount)&&
_IsValidRBTree(pRoot->_pRight, blackCount, pathCount);
}
6.红黑树的删除
红黑树的删除相对比较复杂,有兴趣的大家可以下去学习学习,这里我们就不做讨论了。
大家可以参考下面别人的连接进行学习:
7.红黑树的插入、删除时间复杂度
因为每一个红黑树也是一个特化的二叉查找树,
因此红黑树上的只读操作与普通二叉查找树上的只读操作相同。
然而,在红黑树上进行插入操作和删除操作会导致不再符合红黑树的性质。
恢复红黑树的属性需要少量(O(log n))的颜色变更(实际是非常快速的)和
不超过三次树旋转(对于插入操作是两次)。
虽然插入和删除很复杂,但操作时间仍可以保持为 O(log n) 次。
与AVL树的比较:
红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O( logN),红黑树不追求绝对平衡,其 只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构 中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。
下面给大家分享一下我写的一份完整得实现代码,希望帮助大家理解:
#pragma once
#include<iostream>
using namespace std;
enum Color{RED,BLACK};
template<class V>
struct RBTreeNode
{
RBTreeNode(const V& val = V(), Color color = RED)
:_pLeft(nullptr)
, _pRight(nullptr)
, _pParent(nullptr)
, _val(val)
,_color(color)
{}
RBTreeNode<V>* _pLeft;
RBTreeNode<V>* _pRight;
RBTreeNode<V>* _pParent;
V _val;
Color _color;
};
template<class V>
class RBTreeIterator
{
typedef RBTreeNode<V> Node;
typedef RBTreeIterator<V> Self;
public:
RBTreeIterator(Node* pNode = nullptr)
:_pNode(pNode)
{}
//让迭代器具有指针的功能
V& operator* ()
{
return _pNode->_val;
}
V* operator->()
{
return &(operator*());
}
//移动
Self operator++()
{
InCreament();
return *this;
}
Self operator++(int)
{
Self temp(*this);
InCreament();
return temp;
}
//移动
Self operator--()
{
DeCreament();
return *this;
}
Self operator--(int)
{
Self temp(*this);
DeCreament();
return temp;
}
//比较
bool operator!=(const Self& s) const
{
return _pNode != s._pNode;
}
bool operator== (const Self& s) const
{
return _pNode == s._pNode;
}
void InCreament()
{
if (_pNode->_pRight)
{
_pNode = _pNode->_pRight;
while (_pNode->_pLeft)
_pNode = _pNode->_pLeft;
}
else
{
Node* pParent = _pNode->_pParent;
while (_pNode == pParent->_pRight)
{
_pNode = pParent;
pParent = _pNode->_pParent;
}
//_pNdoe在根节点的位置,并且根节点没有右孩子
if(_pNode->_pRight != pParent )
_pNode = _pNode->_pParent;
}
}
void DeCreament()
{
//_pNode在end,end--取到最大值
if (_pNode->_pParent->_pParent == _pNode && _pNode->_color == RED)
{
_pNode = _pNode->_pRight;
}
if (_pNode->_pLeft)
{
_pNode = _pNode->_pLeft;
while (_pNode->_pRight)
{
_pNode = _pNode->_pRight;
}
}
else
{
Node* pParent = _pNode->_pParent;
while (_pNode == pParent->_pLeft)
{
_pNode = pParent;
pParent = _pNode->_pParent;
}
_pNode = pParent;
}
}
private:
Node* _pNode;
};
template<class V>
class RBTree
{
typedef RBTreeNode<V> Node;
public:
typedef RBTreeIterator<V> iterator;
public:
RBTree()
{
_pHead = new Node;
_pHead->_pLeft = _pHead;
_pHead->_pRight = _pHead;
}
iterator begin()
{
return iterator(_pHead->_pLeft);
}
iterator end()
{
return iterator(_pHead);
}
bool Insert(const V& val)
{
Node*& pRoot = _pHead->_pParent;
if (nullptr == pRoot)
{
pRoot = new Node(val,BLACK);
pRoot->_pParent = _pHead;
}
else
{
//按照二叉搜索树得性质插入结点
//找待插入节点再二叉搜索树中的位置
Node* pCur = pRoot;
Node* pParent = _pHead;
while (pCur)
{
pParent = pCur;
if (val < pCur->_val)
pCur = pCur->_pLeft;
else if (val > pCur->_val)
pCur = pCur->_pRight;
else
return false;
}
//插入新节点
pCur = new Node(val);
if (val < pParent->_val)
pParent->_pLeft = pCur;
else
pParent->_pRight = pCur;
pCur->_pParent = pParent;
//pParent的颜色是红色,一定违反红黑树的性质
while (pParent != _pHead && RED == pParent->_color)
{
Node* grandFather = pParent->_pParent;
if (pParent == grandFather->_pLeft)
{
Node* uncle = grandFather->_pRight;
//情况一:叔叔节点存在且为红色
if (uncle && RED == uncle->_color)
{
pParent->_color = BLACK;
uncle->_color = BLACK;
grandFather->_color = RED;
pCur = grandFather;
pParent = pCur->_pParent;
}
else
{
//情况二:叔叔节点不存在或者存在且为黑色
//情况三:pCur是pParent的右孩子
if (pCur == pParent->_pRight)
{
RotateL(pParent);
swap(pParent, pCur);
}
//情况二:
grandFather->_color = RED;
pParent->_color = BLACK;
RotateR(grandFather);
}
}
else
{
Node* uncle = grandFather->_pLeft;
if (uncle && RED == uncle->_color)
{
pParent->_color = BLACK;
uncle->_color = BLACK;
grandFather->_color = RED;
pCur = grandFather;
pParent = pCur->_pParent;
}
else
{
if (pCur == pParent->_pLeft)
{
RotateR(pCur);
swap(pParent, pCur);
}
pParent->_color = BLACK;
grandFather->_color = RED;
RotateL(grandFather);
}
}
}
}
_pHead->_pLeft = LeftMost();
_pHead->_pRight = RightMost();
pRoot->_color = BLACK;
return true;
}
void Inorder()
{
return _Inorder(GetRoot());
}
//是否有效的红黑树,通过性质进行检查
bool IsValidRBTree()
{
Node* pRoot = GetRoot();
if (nullptr == pRoot)
return true;
//性质二:
if (BLACK != pRoot->_color)
{
cout << "违反性质二:根节点的颜色不是黑色" << endl;
return false;
}
//性质三:不能有相连的红色结点
//获取一条路径中黑色结点的个数
Node* pCur = pRoot;
size_t blackCount = 0;
while (pCur)
{
if (BLACK == pCur->_color)
++blackCount;
pCur = pCur->_pLeft;
}
// 性质四:每条路径里面的黑色结点数目相同
size_t pathCount = 0;
return _IsValidRBTree(pRoot, blackCount, pathCount);
}
private:
bool _IsValidRBTree(Node* pRoot, size_t blackCount,size_t pathCount)
{
if (nullptr == pRoot)
return true;
if (pRoot->_color == BLACK)
pathCount++;
Node* pParent = pRoot->_pParent;
if (pParent != _pHead && RED == pParent->_color && pRoot->_color == RED)
{
cout << "违反了性质三,有连在一起的红色结点" << endl;
return false;
}
if (nullptr == pRoot->_pLeft && pRoot->_pRight == nullptr)
{
if (pathCount != blackCount)
{
cout << "违反性质四:路径中黑色结点得个数不相同" << endl;
return false;
}
}
return _IsValidRBTree(pRoot->_pLeft, blackCount, pathCount)&&
_IsValidRBTree(pRoot->_pRight, blackCount, pathCount);
}
Node* LeftMost()
{
Node* pCur = GetRoot();
if (nullptr == pCur)
return _pHead;
while (pCur->_pLeft)
pCur = pCur->_pLeft;
return pCur;
}
Node* RightMost()
{
Node* pCur = GetRoot();
if (nullptr == pCur)
return _pHead;
while (pCur->_pRight)
pCur = pCur->_pRight;
return pCur;
}
void RotateL(Node* pParent)
{
Node* pSubR = pParent->_pRight;
Node* pSubRL = pSubR->_pLeft;
pParent->_pRight = pSubRL;
if(pSubRL)
pSubRL->_pParent = pParent;
pSubR->_pLeft = pParent;
Node* pPParent = pParent->_pParent;
pParent->_pParent = pSubR;
pSubR->_pParent = pPParent;
if (pPParent == _pHead)
{
_pHead->_pParent = pSubR;
}
else
{
if (pParent == pPParent->_pLeft)
pPParent->_pLeft = pSubR;
else
pPParent->_pRight = pSubR;
}
}
void RotateR(Node* pParent)
{
Node* pSubL = pParent->_pLeft;
Node* pSubLR = pSubL->_pRight;
pParent->_pLeft = pSubLR;
if (pSubLR)
pSubLR->_pParent = pParent;
pSubL->_pRight = pParent;
Node* pPParent = pParent->_pParent;
pParent->_pParent = pSubL;
pSubL->_pParent = pPParent;
if (_pHead == pPParent)
{
_pHead->_pParent = pSubL;
}
else
{
if (pParent == pPParent->_pLeft)
pPParent->_pLeft = pSubL;
else
pPParent->_pRight = pSubL;
}
}
void _Inorder(Node* pRoot)
{
if (pRoot)
{
_Inorder(pRoot->_pLeft);
cout << pRoot->_val << " ";
_Inorder(pRoot->_pRight);
}
}
private:
Node*& GetRoot()
{
return _pHead->_pParent;
}
private:
Node* _pHead;
};
关于AVL树以及二叉平衡树的细节大家可以看我之前的博客:
AVL树---- 详解及其实现
二叉搜索树的实现