红黑树也是一种二叉搜索树,顾名思义,红黑树是节点为黑色或者红色的一棵二叉搜索树。通过对任何一条从根到叶子的路径上各个节点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
红黑树与AVL树的比较:
红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(log2N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。
红黑树常应用于:C++STL库(map、multimap、set、multiset),Linux内核等。
红黑树的性质:
1. 每个节点不是红色就是黑色。
2. 根节点是黑色。
3. 如果一个节点是红色的,则它的孩子节点是黑色的。
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点。
5. 每个叶子节点都是黑色的(此处叶子节点指的是空节点)。
红黑树节点的定义:
enum Color
{
RED,
BLACK,
};
template<class T>
struct RBNode
{
RBNode<T>* left;
RBNode<T>* right;
RBNode<T>* parent;
T val;
Color color;
RBNode(const T& value = T(), Color _color = RED)
:left(nullptr)
, right(nullptr)
, parent(nullptr)
, val(value)
, color(_color)
{}
};
红黑树的结构:
为了后面实现关联式容器,给红黑树中增加一个头节点,头节点的 parent 指针指向根节点,头节点的 left 指针指向红黑树中最小的节点,头节点的 right 指针指向红黑树中最大的节点。
红黑树中的迭代器:
因为红黑树要作为 map、set 等一些STL库的底层实现,我们需要给红黑树也封装一个迭代器。
STL明确规定,begin() 与 end() 代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,可以得到一个有序的序列,因此:begin() 可以放在红黑树中最小节点(即最左侧节点)的位置,end() 放在最大节点(最右侧节点)的下一个位置。那么最大节点的下一个位置在哪?可以是nullptr吗?答案是不可以,因为我们知道,当迭代器位置为 end()时,进行operator--() 操作是可以得到最后一个元素的,如果end()是nullptr,就行不通了,所以我们将 end()位置设置为头节点。
迭代器用法与指针类似,所以我们要重载operator*() 和 operator->(),还要重载前置--/++,后置--/++和==/!=。
对于++操作:我们分两种情况,一种是当前节点的 right 存在,一种是当前节点的 right 不存在。
如果当前节点右子树存在,则去找右子树的最小(最左)节点。
如果当前节点右子树不存在,又分为两种情况:
1. 如果父亲节点的右孩子等于当前节点,则向上查找,即将父亲节点赋给当前节点,直到当父亲节点的右孩子不等于当前节点,则将当前节点指向该父亲节点,并返回。
例如上图,假如节点为5,父亲节点为4,4的右孩子等于当前节点5,将4赋给当前节点,4的父亲节点为8,8的右孩子不为当前节点4,则将当前节点指向8,并返回8,则节点8即为节点5通过++操作得到的位置。
2. 如果当前节点为根结点,且没有右孩子,同样,父亲节点(即头节点)的右孩子等于当前节点(根节点)则将父亲节点赋给当前节点,此时,当前节点(头节点)的父亲节点(根节点)的右孩子不等于当前节点,而是nullptr,此时,直接返回当前节点。
例如上图,假如当前节点为8,父亲节点为头节点,父亲节点的右孩子为当前节点,则将当前节点指向父亲节点,此时当前节点为头节点,父亲节点为8(根节点),父亲节点的右孩子不等于当前节点,所以该当前节点就是通过++操作得到的位置。
对于--操作:我们分成三种情况:当前节点为头节点;当前节点的左孩子存在;当前节点的左孩子不存在。
1. 当前节点为头节点,通过--操作后即为整个红黑树最大的节点,即头节点的右孩子。
2. 当前节点有左子树,找到该节点左子树的最大(最右)节点。
3. 当前节点没有左子树,向上查找,若当前节点的父节点的左孩子等于当前节点,将父节点赋给当前节点,继续向上找,直到当前节点的父亲节点的左孩子不等于当前节点,则将该节点的父亲节点赋给当前节点,并返回。
例如上图,假如当前节点为7,7的父亲节点为8,父亲节点的左孩子等于当前节点7,则将父亲结点8赋给当前节点,此时当前节点为8,8的父亲结点为4,节点4的左孩子不等于当前节点8,则将当前节点指向父亲节点,即节点4就是通过--操作得到的位置。
迭代器实现代码:
template<class T>
class Iterator
{
typedef RBNode<T> Node;
public:
Iterator(Node* _node = nullptr)
:pNode(_node)
{}
Iterator(const Iterator& it)
:pNode(it.pNode)
{}
T& operator*()
{
return pNode->val;
}
T* operator->()
{
return &(operator*());
}
Iterator<T>& operator++()
{
increasement();
return *this;
}
Iterator<T>& operator++(int)
{
Iterator<T> temp(*this);
increasement();
return temp;
}
Iterator<T>& operator--()
{
decreasement();
return *this;
}
Iterator<T>& operator--(int)
{
Iterator<T> temp(*this);
decreasement();
return temp;
}
bool operator!=(const Iterator& it)
{
return pNode != it.pNode;
}
bool operator==(const Iterator& it)
{
return pNode == it.pNode;
}
private:
void increasement()
{
if (pNode->right)
{
pNode = pNode->right;
while (pNode->left)
{
pNode = pNode->left;
}
}
else
{
Node* pParent = pNode->parent;
while (pParent->right == pNode)
{
pNode = pParent;
pParent = pParent->parent;
}
if (pNode->right != pParent)
{
pNode = pParent;
}
}
}
void decreasement()
{
if (pNode->parent->parent == pNode&&pNode->color == RED)
{
pNode = pNode->right;
}
else if (pNode->right)
{
pNode = pNode->left;
while (pNode->right)
{
pNode = pNode->right;
}
}
else
{
Node* pParent = pNode->parent;
while (pParent->left == pNode)
{
pNode = pParent;
pParent = pParent->parent;
}
pNode = pParent;
}
}
Node* pNode;
};
红黑树的插入操作
因为红黑树也是一颗二叉搜索树,所以红黑树的插入操作和二叉搜索树一样,只不过插入之后,需要检测是否破坏了红黑树的性质。所以红黑树的插入操作重点就在于红黑树插入节点之后,检测红黑树性质是否被破坏,并且做相应操作。
那么,如何判断红黑树性质是否遭到破坏呢?
因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论。
约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点。
情况一:cur为红,p为红,g为黑,u存在且为红
解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。
情况二:cur为红,p为红,g为黑,u存在且为黑或者u不存在
解决方法:p为g的左孩子,cur为p的左孩子,则进行右单旋转;p为g的右孩子,cur为p的右孩子,则进行左单旋转。然后将p变黑,g变红。
情况三:cur为红,p为红,g为黑,u存在且为黑或者u不存在
解决方式:p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;p为g的右孩子,cur为p的左孩子,则针对p做右单旋转。然后会发现,此时变成了情况二。然后再根据情况二的具体情况去处理。
完成插入新节点,并且对红黑树进行检测并处理之后,根节点颜色可能改变,需要将根节点颜色置为黑色,然后重置头节点的左孩子和右孩子(即最小值和最大值可能改变)。
上述操作用到了旋转,旋转操作之前我在讲解AVL树中详细介绍过(博客链接:https://blog.youkuaiyun.com/smx_dd/article/details/86581492),红黑树旋转与AVL旋转操作一样。
插入代码实现:
pair<_iterator,bool> insert(const T& value)
{
if (header->parent == nullptr)
{
Node* root = new Node(value,BLACK);
root->parent = header;
header->left = root;
header->right = root;
header->parent = root;
return make_pair(_iterator(root), true);
}
Node* parent = nullptr;
Node* root = header->parent;
Node* cur = root;
while (cur)
{
if (cur->val > value)
{
parent = cur;
cur = cur->left;
}
else if (cur->val < value)
{
parent = cur;
cur = cur->right;
}
else
{
return make_pair(_iterator(cur), false);
}
}
cur = new Node(value, RED);
Node* newNode = cur;
if (parent->val > cur->val)
{
parent->left = cur;
cur->parent = parent;
}
else
{
parent->right = cur;
cur->parent = parent;
}
while (cur != root&&cur->parent->color == RED)
{
parent = cur->parent;
Node* grandpa = parent->parent;
if (grandpa->left == parent)
{
Node* uncle = grandpa->right;
if (uncle&&uncle->color == RED)
{
parent->color = BLACK;
uncle->color = BLACK;
grandpa->color = RED;
cur = grandpa;
}
else
{
if (cur == parent->right)
{
rotateL(parent);
swap(parent, cur);
}
rotateR(grandpa);
parent->color = BLACK;
grandpa->color = RED;
break;
}
}
else
{
Node* uncle = grandpa->left;
if (uncle&&uncle->color == RED)
{
parent->color = BLACK;
uncle->color = BLACK;
grandpa->color = RED;
cur = grandpa;
}
else
{
if (cur == parent->left)
{
rotateR(parent);
swap(parent, cur);
}
rotateL(grandpa);
parent->color = BLACK;
grandpa->color = RED;
break;
}
}
}
header->parent->color = BLACK;
header->left = LeftMost();
header->right = RightMost();
return make_pair(_iterator(newNode), true);
}
红黑树验证:
红黑树的验证比较简单,原理就是围绕着几个性质来判断即可。
bool IsValidRBTree()
{
Node* root = header->parent;
if (root == nullptr)
{
return true;
}
if (BLACK != root->color)
{
cout << "不满足根结点为黑色!" << endl;
return false;
}
// 获取任意一条路径中黑色节点的个数
size_t blackcount = 0;
Node* cur = root;
while (cur)
{
if (cur->color == BLACK)
{
++blackcount;
}
cur = cur->left;
}
// 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数
size_t k = 0;
_IsValidRBTree(root, k, blackcount);
}
bool _IsValidRBTree(Node* root, size_t k, size_t blackcount)
{
if (nullptr == root)
{
return true;
}
// 统计黑色节点的个数
if (BLACK == root->color)
{
++k;
}
// 检测当前节点与其双亲是否都为红色
Node* pParent = root->parent;
if (pParent&&pParent->color == RED&&root->color == RED)
{
cout << "不满足不存在两个连接的红色节点!" << endl;
return false;
}
// 如果pRoot是因子节点,检测当前路径中黑色节点的个数是否有问题
if (nullptr == root->left&&nullptr == root->right)
{
if (k != blackcount)
{
cout << "不满足每条路径上黑色节点个数相同!" << endl;
return false;
}
}
return _IsValidRBTree(root->left, k, blackcount)
&& _IsValidRBTree(root->right, k, blackcount);
}
红黑树完整实现代码:
#include<iostream>
using namespace std;
enum Color
{
RED,
BLACK,
};
template<class T>
struct RBNode
{
RBNode<T>* left;
RBNode<T>* right;
RBNode<T>* parent;
T val;
Color color;
RBNode(const T& value = T(), Color _color = RED)
:left(nullptr)
, right(nullptr)
, parent(nullptr)
, val(value)
, color(_color)
{}
};
template<class T>
class Iterator
{
typedef RBNode<T> Node;
public:
Iterator(Node* _node = nullptr)
:pNode(_node)
{}
Iterator(const Iterator& it)
:pNode(it.pNode)
{}
T& operator*()
{
return pNode->val;
}
T* operator->()
{
return &(operator*());
}
Iterator<T>& operator++()
{
increasement();
return *this;
}
Iterator<T>& operator++(int)
{
Iterator<T> temp(*this);
increasement();
return temp;
}
Iterator<T>& operator--()
{
decreasement();
return *this;
}
Iterator<T>& operator--(int)
{
Iterator<T> temp(*this);
decreasement();
return temp;
}
bool operator!=(const Iterator& it)
{
return pNode != it.pNode;
}
bool operator==(const Iterator& it)
{
return pNode == it.pNode;
}
private:
void increasement()
{
if (pNode->right)
{
pNode = pNode->right;
while (pNode->left)
{
pNode = pNode->left;
}
}
else
{
Node* pParent = pNode->parent;
while (pParent->right == pNode)
{
pNode = pParent;
pParent = pParent->parent;
}
if (pNode->right != pParent)
{
pNode = pParent;
}
}
}
void decreasement()
{
if (pNode->parent->parent == pNode&&pNode->color == RED)
{
pNode = pNode->right;
}
else if (pNode->right)
{
pNode = pNode->left;
while (pNode->right)
{
pNode = pNode->right;
}
}
else
{
Node* pParent = pNode->parent;
while (pParent->left == pNode)
{
pNode = pParent;
pParent = pParent->parent;
}
pNode = pParent;
}
}
Node* pNode;
};
template<class K,class T>
class RBTree
{
public:
typedef RBNode<T> Node;
typedef Iterator<T> _iterator;
_iterator begin()
{
return _iterator(header->left);
}
_iterator end()
{
return _iterator(header);
}
RBTree()
{
header = new Node;
header->parent = nullptr;
header->left = header;
header->right = header;
}
pair<_iterator,bool> insert(const T& value)
{
if (header->parent == nullptr)
{
Node* root = new Node(value,BLACK);
root->parent = header;
header->left = root;
header->right = root;
header->parent = root;
return make_pair(_iterator(root), true);
}
Node* parent = nullptr;
Node* root = header->parent;
Node* cur = root;
while (cur)
{
if (cur->val > value)
{
parent = cur;
cur = cur->left;
}
else if (cur->val < value)
{
parent = cur;
cur = cur->right;
}
else
{
return make_pair(_iterator(cur), false);
}
}
cur = new Node(value, RED);
Node* newNode = cur;
if (parent->val > cur->val)
{
parent->left = cur;
cur->parent = parent;
}
else
{
parent->right = cur;
cur->parent = parent;
}
while (cur != root&&cur->parent->color == RED)
{
parent = cur->parent;
Node* grandpa = parent->parent;
if (grandpa->left == parent)
{
Node* uncle = grandpa->right;
if (uncle&&uncle->color == RED)
{
parent->color = BLACK;
uncle->color = BLACK;
grandpa->color = RED;
cur = grandpa;
}
else
{
if (cur == parent->right)
{
rotateL(parent);
swap(parent, cur);
}
rotateR(grandpa);
parent->color = BLACK;
grandpa->color = RED;
break;
}
}
else
{
Node* uncle = grandpa->left;
if (uncle&&uncle->color == RED)
{
parent->color = BLACK;
uncle->color = BLACK;
grandpa->color = RED;
cur = grandpa;
}
else
{
if (cur == parent->left)
{
rotateR(parent);
swap(parent, cur);
}
rotateL(grandpa);
parent->color = BLACK;
grandpa->color = RED;
break;
}
}
}
header->parent->color = BLACK;
header->left = LeftMost();
header->right = RightMost();
return make_pair(_iterator(newNode), true);
}
void rotateR(Node* parent)
{
Node* subL = parent->left;
Node* subLR = subL->right;
parent->left = subLR;
if (subLR)
{
subLR->parent = parent;
}
subL->right = parent;
Node* grandpa = parent->parent;
parent->parent = subL;
if (header->parent==parent)
{
header->parent = subL;
subL->parent = header;
}
else
{
if (grandpa->left == parent)
{
grandpa->left = subL;
}
else
{
grandpa->right = subL;
}
subL->parent = grandpa;
}
}
void rotateL(Node* parent)
{
Node* subR = parent->right;
Node* subRL = subR->left;
parent->right = subRL;
if (subRL)
{
subRL->parent = parent;
}
subR->left = parent;
Node* grandpa = parent->parent;
parent->parent = subR;
if (header->parent == parent)
{
header->parent = subR;
subR->parent = header;
}
else
{
if (grandpa->left == parent)
{
grandpa->left = subR;
}
else
{
grandpa->right = subR;
}
subR->parent = grandpa;
}
}
Node* LeftMost()
{
Node* cur = header->parent;
if (nullptr == cur)
{
return header;
}
while (cur->left)
{
cur = cur->left;
}
return cur;
}
Node* RightMost()
{
Node* cur = header->parent;
if (nullptr == cur)
{
return header;
}
while (cur->right)
{
cur = cur->right;
}
return cur;
}
void inorder()
{
_inorder(header->parent);
cout << endl;
}
bool empty()
{
return header->parent == nullptr;
}
bool IsValidRBTree()
{
Node* root = header->parent;
if (root == nullptr)
{
return true;
}
if (BLACK != root->color)
{
cout << "不满足根结点为黑色!" << endl;
return false;
}
size_t blackcount = 0;
Node* cur = root;
while (cur)
{
if (cur->color == BLACK)
{
++blackcount;
}
cur = cur->left;
}
size_t k = 0;
_IsValidRBTree(root, k, blackcount);
}
private:
Node* header;
void _inorder(Node* root)
{
if (root == nullptr)
{
return;
}
_inorder(root->left);
cout << root->val << " ";
_inorder(root->right);
}
bool _IsValidRBTree(Node* root, size_t k, size_t blackcount)
{
if (nullptr == root)
{
return true;
}
if (BLACK == root->color)
{
++k;
}
Node* pParent = root->parent;
if (pParent&&pParent->color == RED&&root->color == RED)
{
cout << "不满足不存在两个连接的红色节点!" << endl;
return false;
}
if (nullptr == root->left&&nullptr == root->right)
{
if (k != blackcount)
{
cout << "不满足每条路径上黑色节点个数相同!" << endl;
return false;
}
}
return _IsValidRBTree(root->left, k, blackcount) && _IsValidRBTree(root->right, k, blackcount);
}
};
int main()
{
RBTree<int, int> tree;
int a[] = { 8, 2, 3, 1, 9, 3, 2, 5, 7 };
for (auto e : a)
{
tree.insert(e);
}
tree.inorder();
cout << tree.IsValidRBTree() << endl;
system("pause");
return 0;
}