作者:小 琛
欢迎转载,请标明出处
红黑树的概念
红黑树,是一种二叉搜索树,它在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。
AVL树确保平衡是通过调节平衡因子实现,即每个节点平衡因子绝对值不大于1, 红黑树通过对任何一条从根到叶子的路径上各个结点着色方式的限制,而红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的。
相比较之前的AVL树,红黑树对于高度的要求比AVL差一些,但由于现代硬件设备的优化,这种差异几乎可以忽略。
红黑树的特点
红黑树要求:最长路径的长度不会大于最短路径的两倍。
特点:
- 根节点的颜色为黑
- 树中每条路径的黑节点数量一定相同
- 若一个节点颜色为红,则其父亲节点和孩子节点一定为黑,即没有连续的红节点。
分析:
当满足上述的特点后,实际上实现了:最短路径就是全黑节点路径,最长路径就是一黑一红路径,因此最多是二倍的关系。
红黑树节点的设定
这里写一个kv模型的
成员:
- 该节点的左右孩子指针
- 该节点的父亲节点指针
- 该节点的颜色
- 该节点的值
enum Colour
{
Red,
Black,
};
template <class K,class V>
struct RBTreeNode
{
RBTreeNode<K, V>* _parent;//该节点的父亲节点
RBTreeNode<K, V>* _left;//该节点的左孩子
RBTreeNode<K, V>* _right;//该节点的右孩子
std::pair<K,V> _data;//该节点的数据
Colour _col;//节点的颜色
RBTreeNode(const std::pair<K, V> data)
:_parent(nullptr)
, _left(nullptr)
, _right(nullptr)
, _col(Red)
, _data(data)
{}
};
红黑树的插入
-
按照二叉搜索树的方式先进行插入,新插入的节点默认为红节点
这里默认新插入的节点为红,原因在于:对于红黑树的三个限制条件,在新插入节点后总可能破坏一个,而条件(没有连续的红节点)比条件(没有连续的黑节点)更好进行调整
-
检查红黑树的条件是否被破坏
-
若第二步中,红黑树的条件已被破坏,则进行颜色调整
针对新插入的节点,总结下来有三种情况需要调整该树
设:待调整的节点为cur,父节点为p,叔叔节点为u,爷爷节点为g
- cur为红色,p为红色,u存在且为红色,g为黑色。
解决方法:p和u均变黑,g变红。若g为根,将g再变黑;若g不为根,g为cur继续向上检查。
- cur为红,p为红,g为黑,u不存在或存在且为黑(若u不存在,则cur为新增节点;若u存在且为黑,证明该节点是第一种调整后产生的不符合)
解决方法:右旋,将p该为黑,g改为红;左旋,将p改为黑,g改为红。
举个例子:
- cur为红且为左子树的右边或右子树的左边,p为红,g为黑,u不存在或存在为黑
解决办法:先进行左旋,变为情况二,再根据情况二的解决方法解决。
整体代码
# pragma once
#include <iostream>
enum Colour
{
Red,
Black,
};
template <class K,class V>
class RBTreeNode
{
RBTreeNode<K, V>* _parent;//该节点的父亲节点
RBTreeNode<K, V>* _left;//该节点的左孩子
RBTreeNode<K, V>* _right;//该节点的右孩子
std::pair<K,V> _data;//该节点的数据
Colour _col;//节点的颜色
RBTreeNode(const std::pair<K, V> data)
:_parent(nullptr)
, _left(nullptr)
, _right(nullptr)
, _col(Red)
, _data(data)
{}
};
template <class K,class V>
class RBTree
{
typedef RBTreeNode Node;
public:
bool Insert(std::pair<K, V>& kv)
{
newnode = new Node(kv);
if (_root == nullptr)
{
_root = newnode;
_root->_col = Black;
return true;
}
//按照二叉搜索树的规则插入
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (kv.first > cur->_data.first)
{
parent = cur;
cur = cur->_right;
}
else if (kv.first < cur->_data.first)
{
parent = cur;
cur = cur->_left;
}
else
return false;
}
cur = newnode;
if (cur->_data.first>parent->_data.first)
{
cur->_parent = parent;
parent->_right = cur;
}
else
{
cur->_parent = parent;
parent->_left = cur;
}
cur->_col = Red;//默认新插入节点为红
//检查颜色是否满足要求
while (parent && parent->_col == Red)
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)//表明在左树进行插入
{
Node* uncle = grandfather->_right;
//情况1:uncle存在且为红
if (uncle && uncle->_col == Red)
{
//父亲叔叔变黑,爷爷变红
parent->_col = uncle->_col = Black;
grandfather->_col = Red;
//继续向上处理
cur = grandfather;
parent = cur->_parent;
}
//情况2:uncle不存在或uncle存在为黑
else
{
//情况3:为左子树的右边,需要转换为情况2
if (cur == parent->_right)
{
RotateL(parent);
}
//处理情况2
RotateR(grandfather);
grandfather->_col = Red;
parent->_col = Black;
break;
}
}
//右树插入
else
{
Node* uncle = grandfther->_left;
// 情况1:uncle存在,且为红
// 情况2 or 情况3:uncle不存在 or uncle存在,且为黑
if (uncle && uncle->_col == Red)
{
parent->_col = uncle->_col = Black;
grandfther->_col = Red;
cur = grandfther;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
RotateR(parent);
}
RotateL(grandfther);
grandfther->_col = Red;
parent->_col = Black;
}
}
//根恒为黑色
_root->_col = Black;
return true;
}
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
subR->_left = parent;
Node* ppNode = parent->_parent;
parent->_parent = subR;
// 1、原来parent是这颗树的跟,现在subR是根
// 2、parent为根的树只是整颗树中的子树,改变链接关系,那么subR要顶替他的位置
if (_root == parent)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (ppNode->_left == parent)
ppNode->_left = subR;
else
ppNode->_right = subR;
subR->_parent = ppNode;
}
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
subL->_right = parent;
Node* ppNode = parent->_parent;
parent->_parent = subL;
if (_root == parent)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (ppNode->_left == parent)
ppNode->_left = subL;
else
ppNode->_right = subL;
subL->_parent = ppNode;
}
}
private:
Node* _root = nullptr;
};