C++手撕红黑树
1、红黑树的概念
红黑树是一棵二叉搜索树,他的每个结点增加⼀个存储位来表示结点的颜色,可以是红色或者黑色。通过对任何一条从根到叶子的路径上各个结点的颜色进行约束,红黑树确保没有一条路径会比其他路径长出2倍,因而是接近平衡的。
红黑树的规则:
1. 每个结点不是红色就是黑色。
2. 根节点是黑色的。
3. 如果一个节点是红色的,则它的两个孩子结点必须是黑色的,也就是说任意一条路径不会有连续的红色结点。
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点。
5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)。
说明:《算法导论》等书籍上补充了一条每个叶子结点(NIL)都是黑色的规则。他这里所指的叶子结点不是传统的意义上的叶子结点,而是我们说的空结点,有些书籍上也把NIL叫做外部结点。NIL是为了方便准确的标识出所有路径,《算法导论》在后续讲解实现的细节中也忽略了NIL结点,所以我们知道一下这个概念即可。

思考一下,红黑树如何确保最长路径不超过最短路径的2倍的?

红黑树的效率分析:

红黑树最好情况下就是满二叉树,那么高度为logN,而最长就是2logN,因此时间复杂度还是O(logN)。
红黑树的表达相对AVL树要抽象一些,AVL树通过高度差直观的控制了平衡。红黑树通过4条规则的颜色约束,间接的实现了近似平衡,他们效率都是同一档次,但是相对而言,插如相同数量的结点,红黑树的旋转次数是更少的,因为他对平衡的控制没那么严格。
2、红黑树的结构
红黑树节点也是需要保存父节点的指针,方便在向上调整的时候快速找到父节点。颜色的控制我们通过枚举类型来实现。
enum Colour {
RED,
BLACK
};
template<class K, class V>
struct RBTreeNode
{
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
pair<K, V> _kv;
Colour _col;
RBTreeNode(const pair<K, V>& kv)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _kv(kv)
, _col(RED)
{}
};
template<class K, class V>
class RBTree
{
typedef RBTreeNode<K, V> Node;
public:
private:
private:
Node* _root = nullptr;
};
3、红黑树的插入
3.1、大概过程
插入过程和AVL树相似,不过在AVL树插入后需要向上调整平衡因子,在红黑树这里插入后需要向上调整节点的颜色。
如果插入的是根节点,记得将插入节点的颜色修改为黑色,因为根节点必须是黑色,而我们节点的构造函数创建出的节点默认是红色。
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = BLACK;
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(kv);
if (parent->_kv.first < kv.first)
parent->_right = cur;
else
parent->_left = cur;
cur->_parent = parent;
// 调整颜色
// ....
return true;
}
思考:插入的时候是插入红色节点,还是插入黑色节点?
如果插入黑色节点,那么就会破坏规则4,不容易调整。如果插入红色节点,可能破坏规则3,破坏规则3相对于破坏规则4调整来的容易。因此我们要插入红色节点。
3.2、插入情况分析
旋转在上篇AVL树中已有讲解,对旋转有疑问可移步:C++手撕AVL树


3.3、调整代码
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (grandfather->_left == parent)
{
Node* uncle = grandfather->_right;
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (parent->_left == cur)
{
RotateR(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
else
{
RotateL(parent);
RotateR(grandfather);
grandfather->_col = RED;
cur->_col = BLACK;
}
break;
}
}
else
{
Node* uncle = grandfather->_left;
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (parent->_right == cur)
{
RotateL(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
else
{
RotateR(parent);
RotateL(grandfather);
grandfather->_col = RED;
cur->_col = BLACK;
}
break;
}
}
}
_root->_col = BLACK;
当向上调整到根节点时,cur等于当前根节点,parent不存在,所以跳出循环,但是当前根节点的颜色为红色,需要调整成黑色,所以在循环外我们需要加一句:_root->_col = BLACK; 然后返回true。另外循环内的break也可以不写,因为当旋转后parent指针所指向的节点一定是黑色节点。
3.4、旋转代码
旋转在上篇AVL树中已有讲解,对旋转有疑问可移步:C++手撕AVL树
void RotateL(Node* parent)
{
Node* cur = parent->_right;
Node* curleft = cur->_left;
Node* ppnode = parent->_parent;
parent->_right = curleft;
if (curleft)
curleft->_parent = parent;
cur->_left = parent;
parent->_parent = cur;
if (_root == parent)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
ppnode->_left = cur;
else
ppnode->_right = cur;
cur->_parent = ppnode;
}
}
void RotateR(Node* parent)
{
Node* cur = parent->_left;
Node* curright = cur->_right;
Node* ppnode = parent->_parent;
parent->_left = curright;
if (curright)
curright->_parent = parent;
cur->_right = parent;
parent->_parent = cur;
if (_root == parent)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
ppnode->_left = cur;
else
ppnode->_right = cur;
cur->_parent = ppnode;
}
}
4、红黑树的删除

代码:
bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > key)
{
parent = cur;
cur = cur->_left;
}
else
{
if (cur->_left == nullptr)
{
if (_root == cur)
{
_root = cur->_right;
if (_root)
{
_root->_parent = nullptr;
_root->_col = BLACK;
}
delete cur;
return true;
}
}
else if (cur->_right == nullptr)
{
if (_root == cur)
{
_root = cur->_left;
if (_root)
{
_root->_parent = nullptr;
_root->_col = BLACK;
}
delete cur;
return true;
}
}
else
{
parent = cur;
Node* rightMin = cur->_right;
while (rightMin->_left)
{
parent = rightMin;
rightMin = rightMin->_left;
}
cur->_kv = rightMin->_kv;
cur = rightMin;
}
break;
}
}
if (cur == nullptr) return false;
Node* del = cur;
Node* delParent = parent;
if (cur->_col == BLACK && !cur->_left && !cur->_right)
{
while (parent)
{
if (parent->_left == cur)
{
Node* brother = parent->_right;
if (brother->_col == RED)
{
brother->_col = BLACK;
parent->_col = RED;
RotateL(parent);
brother = parent->_right;
}
if ((!brother->_left || brother->_left->_col == BLACK)
&& (!brother->_right || brother->_right->_col == BLACK))
{
brother->_col = RED;
if (parent->_col == RED)
{
parent->_col = BLACK;
break;
}
cur = parent;
parent = cur->_parent;
}
else
{
if (!brother->_right || brother->_right->_col == BLACK)
{
brother->_left->_col = BLACK;
brother->_col = RED;
RotateR(brother);
brother = parent->_right;
}
brother->_right->_col = BLACK;
brother->_col = parent->_col;
parent->_col = BLACK;
RotateL(parent);
break;
}
}
else
{
Node* brother = parent->_left;
if (brother->_col == RED)
{
brother->_col = BLACK;
parent->_col = RED;
RotateR(parent);
brother = parent->_left;
}
if ((!brother->_left || brother->_left->_col == BLACK)
&& (!brother->_right || brother->_right->_col == BLACK))
{
brother->_col = RED;
if (parent->_col == RED)
{
parent->_col = BLACK;
break;
}
cur = parent;
parent = cur->_parent;
}
else
{
if (!brother->_left || brother->_left->_col == BLACK)
{
brother->_right->_col = BLACK;
brother->_col = RED;
RotateL(brother);
brother = parent->_left;
}
brother->_left->_col = BLACK;
brother->_col = parent->_col;
parent->_col = BLACK;
RotateR(parent);
break;
}
}
}
}
cur = del, parent = delParent;
if (cur->_left == nullptr)
{
if (parent->_left == cur)
parent->_left = cur->_right;
else
parent->_right = cur->_right;
if (cur->_right)
{
cur->_right->_parent = parent;
cur->_right->_col = BLACK;
}
}
else
{
if (parent->_left == cur)
parent->_left = cur->_left;
else
parent->_right = cur->_left;
if (cur->_left)
{
cur->_left->_parent = parent;
cur->_left->_col = BLACK;
}
}
delete cur;
return true;
}
5、红黑树的查找
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < key)
cur = cur->_right;
else if (cur->_kv.first > key)
cur = cur->_left;
else
return cur;
}
return nullptr;
}
6、红黑树的验证
bool IsBalance()
{
return IsBalance(_root);
}
bool IsBalance(Node* root)
{
if (root == nullptr) return true;
if (root->_col != BLACK) return false;
int benchmark = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK) benchmark++;
cur = cur->_left;
}
return CheckColour(root, 0, benchmark);
}
bool CheckColour(Node* root, int blacknum, int benchmark)
{
if (root == nullptr)
{
if (blacknum != benchmark) return false;
return true;
}
if (root->_col == BLACK) blacknum++;
if (root->_col == RED && root->_parent && root->_parent->_col == RED)
{
cout << root->_kv.first << "出现连续的红色节点" << endl;
return false;
}
return CheckColour(root->_left, blacknum, benchmark)
&& CheckColour(root->_right, blacknum, benchmark);
}
IsBalance用来判断我们的树是否满足红黑树规则。首先判断根节点是不是黑色节点,不是直接返回false。紧接着我们随意计算一条路径上黑色节点的个数,将计算出来的benchmark作为基准值传入CheckColour函数中判断。
在CheckColor函数中,如果当前节点为空指针,说明一条路径已经结束,我们判断这条路径上的黑色节点数目和我们所给的基准值是否相同,不相同说明不是红黑树,直接返回false。如果当前节点不是空节点就往后走,判断当前节点是否是黑色节点,如果是就让这条路径上的黑色节点++,同时判断当前节点及其父节点是否是连续的红色节点,然后递归判断它的左子树和右子树。
如果基准值是错的呢?也无所谓,因为如果基准值是错的,在某条路径结束后黑色节点数就不会等于基准值,照样返回false。
7、红黑树的其他函数
这里实现构造函数、拷贝构造函数、赋值运算符重载、析构函数、中序遍历、高度函数。基本上类似前面的二叉搜索树。
RBTree()
:_root(nullptr)
{}
RBTree(const RBTree<K, V>& t)
:_root(nullptr)
{
_root = Copy(t._root, nullptr);
}
RBTree<K, V>& operator=(RBTree<K, V> t)
{
swap(_root, t._root);
return *this;
}
~RBTree()
{
Destroy(_root);
}
int Height()
{
return Height(_root);
}
void InOrder()
{
InOrder(_root);
cout << endl;
}
Node* Copy(Node* root, Node* parent)
{
if (root == nullptr) return nullptr;
Node* copy = new Node(root->_kv);
copy->_col = root->_col;
copy->_parent = parent;
copy->_left = Copy(root->_left, copy);
copy->_right = Copy(root->_right, copy);
return copy;
}
void Destroy(Node*& root)
{
if (root == nullptr) return;
Destroy(root->_left);
Destroy(root->_right);
delete root;
root = nullptr;
}
void InOrder(Node* root)
{
if (root == nullptr) return;
InOrder(root->_left);
cout << root->_kv.first << " ";
InOrder(root->_right);
}
int Height(Node* root)
{
if (root == nullptr) return 0;
int left = Height(root->_left);
int right = Height(root->_right);
return left > right ? left + 1 : right + 1;
}
完整代码
#pragma once
enum Colour {
RED,
BLACK
};
template<class K, class V>
struct RBTreeNode
{
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
pair<K, V> _kv;
Colour _col;
RBTreeNode(const pair<K, V>& kv)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _kv(kv)
, _col(RED)
{}
};
template<class K, class V>
class RBTree
{
typedef RBTreeNode<K, V> Node;
public:
RBTree()
:_root(nullptr)
{}
RBTree(const RBTree<K, V>& t)
:_root(nullptr)
{
_root = Copy(t._root, nullptr);
}
RBTree<K, V>& operator=(RBTree<K, V> t)
{
swap(_root, t._root);
return *this;
}
~RBTree()
{
Destroy(_root);
}
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = BLACK;
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(kv);
if (parent->_kv.first < kv.first)
parent->_right = cur;
else
parent->_left = cur;
cur->_parent = parent;
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (grandfather->_left == parent)
{
Node* uncle = grandfather->_right;
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (parent->_left == cur)
{
RotateR(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
else
{
RotateL(parent);
RotateR(grandfather);
grandfather->_col = RED;
cur->_col = BLACK;
}
break;
}
}
else
{
Node* uncle = grandfather->_left;
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (parent->_right == cur)
{
RotateL(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
else
{
RotateR(parent);
RotateL(grandfather);
grandfather->_col = RED;
cur->_col = BLACK;
}
break;
}
}
}
_root->_col = BLACK;
return true;
}
bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > key)
{
parent = cur;
cur = cur->_left;
}
else
{
if (cur->_left == nullptr)
{
if (_root == cur)
{
_root = cur->_right;
if (_root)
{
_root->_parent = nullptr;
_root->_col = BLACK;
}
delete cur;
return true;
}
}
else if (cur->_right == nullptr)
{
if (_root == cur)
{
_root = cur->_left;
if (_root)
{
_root->_parent = nullptr;
_root->_col = BLACK;
}
delete cur;
return true;
}
}
else
{
parent = cur;
Node* rightMin = cur->_right;
while (rightMin->_left)
{
parent = rightMin;
rightMin = rightMin->_left;
}
cur->_kv = rightMin->_kv;
cur = rightMin;
}
break;
}
}
if (cur == nullptr) return false;
Node* del = cur;
Node* delParent = parent;
if (cur->_col == BLACK && !cur->_left && !cur->_right)
{
while (parent)
{
if (parent->_left == cur)
{
Node* brother = parent->_right;
if (brother->_col == RED)
{
brother->_col = BLACK;
parent->_col = RED;
RotateL(parent);
brother = parent->_right;
}
if ((!brother->_left || brother->_left->_col == BLACK)
&& (!brother->_right || brother->_right->_col == BLACK))
{
brother->_col = RED;
if (parent->_col == RED)
{
parent->_col = BLACK;
break;
}
cur = parent;
parent = cur->_parent;
}
else
{
if (!brother->_right || brother->_right->_col == BLACK)
{
brother->_left->_col = BLACK;
brother->_col = RED;
RotateR(brother);
brother = parent->_right;
}
brother->_right->_col = BLACK;
brother->_col = parent->_col;
parent->_col = BLACK;
RotateL(parent);
break;
}
}
else
{
Node* brother = parent->_left;
if (brother->_col == RED)
{
brother->_col = BLACK;
parent->_col = RED;
RotateR(parent);
brother = parent->_left;
}
if ((!brother->_left || brother->_left->_col == BLACK)
&& (!brother->_right || brother->_right->_col == BLACK))
{
brother->_col = RED;
if (parent->_col == RED)
{
parent->_col = BLACK;
break;
}
cur = parent;
parent = cur->_parent;
}
else
{
if (!brother->_left || brother->_left->_col == BLACK)
{
brother->_right->_col = BLACK;
brother->_col = RED;
RotateL(brother);
brother = parent->_left;
}
brother->_left->_col = BLACK;
brother->_col = parent->_col;
parent->_col = BLACK;
RotateR(parent);
break;
}
}
}
}
cur = del, parent = delParent;
if (cur->_left == nullptr)
{
if (parent->_left == cur)
parent->_left = cur->_right;
else
parent->_right = cur->_right;
if (cur->_right)
{
cur->_right->_parent = parent;
cur->_right->_col = BLACK;
}
}
else
{
if (parent->_left == cur)
parent->_left = cur->_left;
else
parent->_right = cur->_left;
if (cur->_left)
{
cur->_left->_parent = parent;
cur->_left->_col = BLACK;
}
}
delete cur;
return true;
}
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < key)
cur = cur->_right;
else if (cur->_kv.first > key)
cur = cur->_left;
else
return cur;
}
return nullptr;
}
bool IsBalance()
{
return IsBalance(_root);
}
int Height()
{
return Height(_root);
}
void InOrder()
{
InOrder(_root);
cout << endl;
}
private:
void InOrder(Node* root)
{
if (root == nullptr) return;
InOrder(root->_left);
cout << root->_kv.first << " ";
InOrder(root->_right);
}
int Height(Node* root)
{
if (root == nullptr) return 0;
int left = Height(root->_left);
int right = Height(root->_right);
return left > right ? left + 1 : right + 1;
}
Node* Copy(Node* root, Node* parent)
{
if (root == nullptr) return nullptr;
Node* copy = new Node(root->_kv);
copy->_col = root->_col;
copy->_parent = parent;
copy->_left = Copy(root->_left, copy);
copy->_right = Copy(root->_right, copy);
return copy;
}
void Destroy(Node*& root)
{
if (root == nullptr) return;
Destroy(root->_left);
Destroy(root->_right);
delete root;
root = nullptr;
}
bool IsBalance(Node* root)
{
if (root == nullptr) return true;
if (root->_col != BLACK) return false;
int benchmark = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK) benchmark++;
cur = cur->_left;
}
return CheckColour(root, 0, benchmark);
}
bool CheckColour(Node* root, int blacknum, int benchmark)
{
if (root == nullptr)
{
if (blacknum != benchmark) return false;
return true;
}
if (root->_col == BLACK) blacknum++;
if (root->_col == RED && root->_parent && root->_parent->_col == RED)
{
cout << root->_kv.first << "出现连续的红色节点" << endl;
return false;
}
return CheckColour(root->_left, blacknum, benchmark)
&& CheckColour(root->_right, blacknum, benchmark);
}
void RotateL(Node* parent)
{
Node* cur = parent->_right;
Node* curleft = cur->_left;
Node* ppnode = parent->_parent;
parent->_right = curleft;
if (curleft)
curleft->_parent = parent;
cur->_left = parent;
parent->_parent = cur;
if (_root == parent)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
ppnode->_left = cur;
else
ppnode->_right = cur;
cur->_parent = ppnode;
}
}
void RotateR(Node* parent)
{
Node* cur = parent->_left;
Node* curright = cur->_right;
Node* ppnode = parent->_parent;
parent->_left = curright;
if (curright)
curright->_parent = parent;
cur->_right = parent;
parent->_parent = cur;
if (_root == parent)
{
_root = cur;
cur->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
ppnode->_left = cur;
else
ppnode->_right = cur;
cur->_parent = ppnode;
}
}
private:
Node* _root = nullptr;
};
709

被折叠的 条评论
为什么被折叠?



