红黑树概念与特性
红黑树是一种自平衡二叉树,同时也是set和map的底层实现容器。
- 节点只能是黑或者红
- 根节点与叶子节点必须是黑色(这里的叶子节点指nullptr)
- 不能出现连续的红色节点
- 任意节点到其叶子节点的每一条路径上黑色节点数量必须相同
单个节点的定义:
template<class T>
struct RBTreeNode
{
RBTreeNode<T>* _pLeft;//左孩子(指向左节点)
RBTreeNode<T>* _pRight;//右孩子(指向右节点)
RBTreeNode<T>* _pParent;//父节点(指向上一个节点)
T _data;//存储的数据
Colour _col;//当前节点的颜色
//构造函数
RBTreeNode(const T& data = T())
:_pLeft(nullptr)
, _pRight(nullptr)
, _pParent(nullptr)
, _data(data)
, _col(RED)
{
}
};
普通红黑树:
带哨兵位红黑树:
- 带哨兵位的红黑树,其哨兵位的 parent 指向根节点(64),同样的根节点的parent也指向哨兵位。
- 哨兵位的 left 指向最左节点(即指向最小值)
- 哨兵位的 right 指向最右节点(即指向最大值)
插入节点的实现
首先我们会面临第一个问题,插入的节点应该是什么颜色的?
答:插入节点应为红色。因为树本身原来是平衡的,插入黑色节点一定会违反规则4(任意节点到其叶子节点的每一条路径上黑色节点数量必须相同),插入红色则是可能会违反规则3(不能出现连续的红色节点)对比之下,显然新节点设置为红色更合理。
调整平衡
注:在此处树为空时,头节点所有指针指向自己。
根据 左子树的值 < 根的值 < 右子树的值 找到合适的插入位置(即cur)后,可能有以下情况。
情况一
这种情况只需要将p节点和u节点变成黑色,并且将g节点变成红色就可以了。
- (1)首先,因为节点位置没有改变,数值大小符合二叉树原则
- (2)其次,同一条路径上黑色节点数量没有改变(为什么没有改变?从g开始往下走,虽然g节点改为红导致黑色节点减一,但是g节点的左右子树黑色节点各自加一,所以总径上的黑色节点总数没变)
注意:因为g节点可能会是根节点,如果是最后要重新变为黑色(规则2根节点和叶子节点必须是黑色的)。
情况二
如果叔叔节点不存在,则参考左图,如果存在且为黑参考右图(绿色框框代表一棵子树)。
为什么要这么画?因为如果叔叔节点存在且为黑,那么用图一表示时树已经失衡了(右边路径的黑色节点数已经为2,而左边仅为1)。图二的三棵子树保证了每条路径都有相同的黑色节点数。不存在插入就出现左图并且叔叔存在且为黑的情况,因为插入前红黑树已经失衡。而右图的情况会在向上调整中出现。
情况二处理起来要麻烦的多,当p为g的左孩子,且cur为p的左孩子,则进行右单旋。
当p为g的左孩子,且cur为p的右孩子,则先以p节点旋转点进行左单旋,再以g节点为旋转点进行右单旋。
向上调整
当调整后要检查当前的g节点是否为红色,如果是,则可能出现连续红色节点需要将g赋值给c重新进行调整。
插入实现逻辑及代码
pair<iterator, bool> Insert(const T& data)
{
//判断根节点是否为空
//为空则生成节点并建立连接
if (_pHead->_pParent == _pHead)
{
Node* _root = new Node(data);
_root->_col = BLACK;
_pHead->_pParent = _root;
_pHead->_pLeft = _root;
_pHead->_pRight = _root;
_root->_pParent = _pHead;
return pair<iterator, bool>({ _root ,true });
}
//调用仿函数需要的对象
KeyOfValue kot;
//记录当前位置和上一个位置
Node* parent = _pHead;
Node* cur = _pHead->_pParent;
//查找一个合适的插入节点的位置
while (cur)
{
if (kot(data) > kot(cur->_data))
{
parent = cur;
cur = cur->_pRight;
}
else if (kot(data) < kot(cur->_data))
{
parent = cur;
cur = cur->_pLeft;
}
else
{
return pair<iterator, bool>({ nullptr ,false });
}
}
//找到合适位置后生成节点
cur = new Node(data);
//ret记录返回节点,最后用于返回
Node* ret = cur;
//判断当前节点应该是父节点的左孩子还是右孩子,然后连接
if (kot(parent->_data) < kot(data))
{
parent->_pRight = cur;
}
else
{
parent->_pLeft = cur;
}
cur->_pParent = parent;
//调整平衡因子
while (parent != _pHead && parent->_col == RED)
{
Node* grandfather = parent->_pParent;
if (parent == grandfather->_pLeft)
{
Node* uncle = grandfather->_pRight;
if (uncle && uncle->_col == RED)
{
grandfather->_col = RED;
parent->_col = uncle->_col = BLACK;
cur = grandfather;
parent = cur->_pParent;
}
else//叔叔不存在或颜色为黑
{
if (cur == parent->_pLeft)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
else
{
Node* uncle = grandfather->_pLeft;
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_pParent;
}
else//叔叔不存在或颜色为黑
{
if (cur == parent->_pRight)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
//将根节点颜色改为黑,并更新头节点指向的最左节点和最右节点
_pHead->_pParent->_col = BLACK;
_pHead->_pLeft = _LeftMost();
_pHead->_pRight = _RightMost();
return pair<iterator,bool>({ ret ,true });
}
仿函数兼容数据类型
在插入函数中常常能看见if (kot(data) > kot(cur->_data))这样的代码,kot是为了调用仿函数的所创建的对象。
为了兼容pair类型和普通类型,在上层的set和map中实现了提取数据的仿函数。为什么做在set和map中?因为红黑树他无法知道是常规类型还是pair类型,但上层的set和map知道。
namespace MySet
{
template<class K>
class Set
{
public:
//仿函数
struct KOfV
{
public:
const K& operator()(const K& k)
{
return k;
}
};
typedef typename RB::RBTree<const K, KOfV>::iterator iterator;
//以下函数都是对红黑树接口的复用
iterator begin()
{
return _t.Begin();
}
iterator end()
{
return _t.End();
}
bool insert(const K& key)
{
return (_t.Insert(key)).second;
}
private:
RB::RBTree<const K, KOfV> _t;
};
}
比较时是根据键值来判断大小的,所以仿函数返回kv.first。
namespace MyMap
{
template<class K, class V>
class Map
{
public:
//仿函数
struct KOfV
{
public:
const K& operator()(const pair<K, V>& kv)
{
return kv.first;
}
};
typedef typename RB::RBTree<pair<const K, V>, KOfV>::iterator iterator;
//以下函数都是对红黑树接口的复用
iterator begin()
{
return _t.Begin();
}
iterator end()
{
return _t.End();
}
bool insert(const pair<K, V>& kv)
{
return (_t.Insert(kv)).second;
}
private:
RB::RBTree<pair<const K, V>, KOfV> _t;
};
}
迭代器实现
主要是实现++和--的重载,按照从小到大的顺序遍历则恰好是中序遍历。所以最左节点是起点,最右节点是终点。而++与--最重要的逻辑是寻找下一个节点。
寻找++的下一个节点
方法:
(1)若当前节点(cur)有右子树则进入右子树中寻找最左节点。
(2)若没有,则向上查找一个祖先节点,而cur必须位于该祖先节点的左子树否则继续向上查找。
原理:
(1)因为是从小到大遍历,则++的下一个一定大于当前节点同时小于其余未遍历节点。要寻找这样的节点首先去右子树找右子树的最小节点(最左节点)。
(2)若不存在右子树,只能去找祖先节点,又因为搜索二叉树的特性(左子树值<根值<右子树值),必须找一个cur所在子树是该祖先节点的左子树。
寻找--的下一个节点
方法:
(1)若当前节点(cur)有左子树则进入左子树中寻找最右节点。
(2)若没有,则向上查找一个祖先节点,而cur必须位于该祖先节点的右子树否则继续向上查找。
原理:
(1)因为是从大到小遍历,则--的下一个一定小于当前节点同时大于其余未遍历节点。要寻找这样的节点首先去左子树找左子树的最大节点(最右节点)。
(2)若不存在左子树,只能去找祖先节点,又因为搜索二叉树的特性(左子树值<根值<右子树值),必须找一个cur所在子树是该祖先节点的右子树。
template<class T, class Ref, class Ptr>
struct RBTreeIterator
{
typedef RBTreeNode<T> Node;
typedef RBTreeIterator<T, Ref, Ptr> Self;
RBTreeIterator(Node* pNode)
: _pNode(pNode)
{}
// 让迭代器具有类似指针的行为
Ref operator*()
{
return _pNode->_data;
}
Ptr operator->()
{
return &_pNode->_data;
}
// 然迭代器可以移动:前置/后置++
Self& operator++()
{
if (_pNode->_pRight)
{
Node* RightMin = _pNode->_pRight;
while (RightMin->_pLeft)
{
RightMin = RightMin->_pLeft;
}
_pNode = RightMin;
}
else
{
Node* cur = _pNode;
Node* parent = cur->_pParent;
while (parent && cur == parent->_pRight)
{
cur = parent;
parent = cur->_pParent;
}
_pNode = parent;
}
return *this;
}
Self operator++(int)
{
Node* temp = _pNode;
if (_pNode->_pRight)
{
Node* RightMin = _pNode->_pRight;
while (RightMin->_pLeft)
{
RightMin = RightMin->_pLeft;
}
_pNode = RightMin;
}
else
{
Node* cur = _pNode;
Node* parent = cur->_pParent;
while (parent && cur == parent->_pRight)
{
cur = parent;
parent = cur->_pParent;
}
_pNode = parent;
}
return temp;
}
// 然迭代器可以移动:前置/后置--
Self& operator--()
{
if (_pNode->_pLeft)
{
Node* LeftMax = _pNode->_pLeft;
while (LeftMax->_pRight)
{
LeftMax = LeftMax->_pRight;
}
_pNode = LeftMax;
}
else
{
Node* cur = _pNode;
Node* parent = cur->_pParent;
while (parent && cur == parent->_pLeft)
{
cur = parent;
parent = cur->_pParent;
}
_pNode = parent;
}
return *this;
}
Self operator--(int)
{
Node* temp = _pNode;
if (_pNode->_pLeft)
{
Node* LeftMax = _pNode->_pLeft;
while (LeftMax->_pRight)
{
LeftMax = LeftMax->_pRight;
}
_pNode = LeftMax;
}
else
{
Node* cur = _pNode;
Node* parent = cur->_pParent;
while (parent && cur == parent->_pLeft)
{
cur = parent;
parent = cur->_pParent;
}
_pNode = parent;
}
return temp;
}
// 让迭代器可以比较
bool operator!=(const Self& s)const
{
return _pNode != s._pNode;
}
bool operator==(const Self& s)const
{
return _pNode == s._pNode;
}
private:
Node* _pNode;
};
完整代码实现
#pragma once
#pragma once
#include<assert.h>
#include<vector>
#include<iostream>
using namespace std;
namespace RB
{
enum Colour
{
RED,
BLACK
};
template<class T>
struct RBTreeNode
{
RBTreeNode<T>* _pLeft;
RBTreeNode<T>* _pRight;
RBTreeNode<T>* _pParent;
T _data;
Colour _col;
RBTreeNode(const T& data = T())
:_pLeft(nullptr)
, _pRight(nullptr)
, _pParent(nullptr)
, _data(data)
, _col(RED)
{
}
};
template<class T, class Ref, class Ptr>
struct RBTreeIterator
{
typedef RBTreeNode<T> Node;
typedef RBTreeIterator<T, Ref, Ptr> Self;
RBTreeIterator(Node* pNode)
: _pNode(pNode)
{}
// 让迭代器具有类似指针的行为
Ref operator*()
{
return _pNode->_data;
}
Ptr operator->()
{
return &_pNode->_data;
}
// 然迭代器可以移动:前置/后置++
Self& operator++()
{
if (_pNode->_pRight)
{
Node* LeftMin = _pNode->_pRight;
while (LeftMin->_pLeft)
{
LeftMin = LeftMin->_pLeft;
}
_pNode = LeftMin;
}
else
{
Node* cur = _pNode;
Node* parent = cur->_pParent;
while (parent && cur == parent->_pRight)
{
cur = parent;
parent = cur->_pParent;
}
_pNode = parent;
}
return *this;
}
Self operator++(int)
{
Node* temp = _pNode;
if (_pNode->_pRight)
{
Node* RightMin = _pNode->_pRight;
while (RightMin->_pLeft)
{
RightMin = RightMin->_pLeft;
}
_pNode = RightMin;
}
else
{
Node* cur = _pNode;
Node* parent = cur->_pParent;
while (parent && cur == parent->_pRight)
{
cur = parent;
parent = cur->_pParent;
}
_pNode = parent;
}
return temp;
}
// 然迭代器可以移动:前置/后置--
Self& operator--()
{
if (_pNode->_pLeft)
{
Node* LeftMax = _pNode->_pLeft;
while (LeftMax->_pRight)
{
LeftMax = LeftMax->_pRight;
}
_pNode = LeftMax;
}
else
{
Node* cur = _pNode;
Node* parent = cur->_pParent;
while (parent && cur == parent->_pLeft)
{
cur = parent;
parent = cur->_pParent;
}
_pNode = parent;
}
return *this;
}
Self operator--(int)
{
Node* temp = _pNode;
if (_pNode->_pLeft)
{
Node* LeftMax = _pNode->_pLeft;
while (LeftMax->_pRight)
{
LeftMax = LeftMax->_pRight;
}
_pNode = LeftMax;
}
else
{
Node* cur = _pNode;
Node* parent = cur->_pParent;
while (parent && cur == parent->_pLeft)
{
cur = parent;
parent = cur->_pParent;
}
_pNode = parent;
}
return temp;
}
// 让迭代器可以比较
bool operator!=(const Self& s)const
{
return _pNode != s._pNode;
}
bool operator==(const Self& s)const
{
return _pNode == s._pNode;
}
private:
Node* _pNode;
};
// T: 可能是键值对<key,value>
// 可能是一个key
// 不论节点中存储的是<key, value> || key, 都是按照key来进行比较的
// KeyOfValue: 提取data中的Key
template<class T, class KeyOfValue>
class RBTree
{
typedef RBTreeNode<T> Node;
public:
typedef RBTreeIterator<T, T&, T*> iterator;
public:
RBTree()
: _size(0)
{
_pHead = new Node;
_pHead->_pLeft = _pHead;
_pHead->_pRight = _pHead;
_pHead->_pParent = _pHead;
}
~RBTree()
{
_Destroy();
}
// 插入值为data的节点
// 返回值含义:iterator代表新插入节点 bool:代表释放插入成功
pair<iterator, bool> Insert(const T& data)
{
if (_pHead->_pParent == _pHead)
{
Node* _root = new Node(data);
_root->_col = BLACK;
_pHead->_pParent = _root;
_pHead->_pLeft = _root;
_pHead->_pRight = _root;
_root->_pParent = _pHead;
return pair<iterator, bool>({ _root ,true });
}
KeyOfValue kot;
Node* parent = _pHead;
Node* cur = _pHead->_pParent;
while (cur)
{
if (kot(data) > kot(cur->_data))
{
parent = cur;
cur = cur->_pRight;
}
else if (kot(data) < kot(cur->_data))
{
parent = cur;
cur = cur->_pLeft;
}
else
{
return pair<iterator, bool>({ nullptr ,false });
}
}
cur = new Node(data);
Node* ret = cur;
if (kot(parent->_data) < kot(data))
{
parent->_pRight = cur;
}
else
{
parent->_pLeft = cur;
}
cur->_pParent = parent;
//调整平衡因子
while (parent != _pHead && parent->_col == RED)
{
Node* grandfather = parent->_pParent;
if (parent == grandfather->_pLeft)
{
Node* uncle = grandfather->_pRight;
if (uncle && uncle->_col == RED)
{
grandfather->_col = RED;
parent->_col = uncle->_col = BLACK;
cur = grandfather;
parent = cur->_pParent;
}
else//叔叔不存在或颜色为黑
{
if (cur == parent->_pLeft)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
else
{
Node* uncle = grandfather->_pLeft;
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_pParent;
}
else//叔叔不存在或颜色为黑
{
if (cur == parent->_pRight)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_pHead->_pParent->_col = BLACK;
_pHead->_pLeft = _LeftMost();
_pHead->_pRight = _RightMost();
return pair<iterator,bool>({ ret ,true });
}
// Begin和End迭代器s
iterator Begin()
{
return iterator(_pHead->_pLeft);
}
iterator End()
{
return iterator(_pHead);
}
void InOrder()
{
_InOrder(_pHead->_pParent);
}
// 检测红黑树是否为有效的红黑树,注意:其内部主要依靠_IsValidRBTRee函数检测
bool IsValidRBTRee()
{
//根节点存在 && 根节点为红
if (_pHead->_pParent != _pHead && _pHead->_pParent->_col == RED) return false;
//一条路径的黑色节点个数
int pathBlack = 0;
Node* cur = _pHead->_pParent;
while (cur)
{
if (cur->_col == BLACK)
pathBlack++;
cur = cur->_pLeft;
}
return _IsValidRBTRee(_pHead->_pParent, 0, pathBlack);
}
// 红黑树是否为红,是返回true,否则返回false
bool Empty()const
{
return Size() == 0;
}
// 返回红黑树中有效节点的个数
size_t Size()const
{
return _size;
}
// 将红黑树中的有效节点删除,注意:删除的是有效节点,不删除头结点
void Clear()
{
if (_pHead->_pParent == _pHead) return;
Node* cur = _pHead->_pParent;
_Clear(cur);
}
void _Clear(Node* root)
{
if (root == nullptr) return;
_Clear(root->_pLeft);
_Clear(root->_pRight);
delete root;
}
// 在红黑树中查找data,存在赶回该节点对应的迭代器,否则返回End()
iterator Find(const T& data)
{
KeyOfValue kot;
Node* cur = _pHead->_pParent;
while (cur)
{
if (kot(cur->_data) < kot(data))
{
cur = cur->_pRight;
}
else if (kot(cur->_data) > kot(data))
{
cur = cur->_pLeft;
}
else
{
return cur;
}
}
return nullptr;
}
private:
Node* _LeftMost()
{
if (_pHead->_pParent == _pHead) return nullptr;
Node* cur = _pHead->_pParent;
while (cur->_pLeft)
{
cur = cur->_pLeft;
}
return cur;
}
Node* _RightMost()
{
if (_pHead->_pParent == _pHead) return nullptr;
Node* cur = _pHead->_pParent;
while (cur->_pRight)
{
cur = cur->_pRight;
}
return cur;
}
void _Destroy()
{
Clear();
delete _pHead;
}
void RotateL(Node* pParent)
{
{
Node* subR = pParent->_pRight;
Node* subRL = subR->_pLeft;
pParent->_pRight = subRL;
if (subRL)
subRL->_pParent = pParent;
subR->_pLeft = pParent;
Node* temp = pParent->_pParent;
pParent->_pParent = subR;
//pParent有可能为根,右单旋后应该更新根节点指向。
if (pParent == _pHead)
{
_pHead = subR;
_pHead->_pParent = nullptr;
}
else
{
if (temp->_pLeft == pParent)
{
temp->_pLeft = subR;
}
else
{
temp->_pRight = subR;
}
subR->_pParent = temp;
}
}
}
void RotateR(Node* pParent)
{
{
Node* subL = pParent->_pLeft;
Node* subLR = subL->_pRight;
pParent->_pLeft = subLR;
if (subLR)
subLR->_pParent = pParent;
subL->_pRight = pParent;
Node* temp = pParent->_pParent;
pParent->_pParent = subL;
//pParent有可能为根,右单旋后应该更新根节点指向。
if (pParent == _pHead)
{
_pHead = subL;
_pHead->_pParent = nullptr;
}
else
{
if (temp->_pLeft == pParent)
{
temp->_pLeft = subL;
}
else
{
temp->_pRight = subL;
}
subL->_pParent = temp;
}
}
}
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_pLeft);
cout << root->_data << endl;
_InOrder(root->_pRight);
}
bool _IsValidRBTRee(Node* pRoot, size_t blackCount, size_t pathBlack)
{
if (pRoot == nullptr)
{
if (blackCount != pathBlack)
{
return false;
}
else
{
return true;
}
}
if (pRoot->_col == RED && pRoot->_pParent->_col == RED)
{
return false;
}
if (pRoot->_col == BLACK)
{
blackCount++;
}
return _IsValidRBTRee(pRoot->_pLeft, blackCount, pathBlack)
&& _IsValidRBTRee(pRoot->_pRight, blackCount, pathBlack);
}
Node*& GetRoot()
{
return _pHead->_pParent == _pHead ? nullptr : _pHead->_pParent;
}
private:
Node* _pHead;
size_t _size;
};
}