数据结构关联式容器底层红黑树的模拟实现
Posted zhaocx111222333
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构关联式容器底层红黑树的模拟实现相关的知识,希望对你有一定的参考价值。
红黑树是一种二叉搜索树,接近平衡。
主要有几个特点:
- 每个结点不是红色就是黑色
- 根节点是黑色的
- 如果一个节点是红色的,则它的两个孩子结点是黑色的
- 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
- 每个叶子结点都是黑色的
红黑树节点
#include<iostream>
#include<utility>
using namespace std;
enum COLOR{
RED,
BLACK
};
//红黑树节点
//包含3指向,1颜色,1val
//val可以是pair,和k
template<class V>
struct RBNode{
RBNode<V>* _parent;
RBNode<V>* _left;
RBNode<V>* _right;
V _val;
COLOR _color;
RBNode(const V& val = V())
:_parent(nullptr)
, _left(nullptr)
, _right(nullptr)
, _val(val)
, _color(RED){}
};
迭代器
由于树形结构的底层实现,红黑树的迭代器是封装了node节点的迭代器
//红黑树迭代器
template<class V>
struct RBTreeIterator{
typedef RBNode<V> Node;
typedef RBTreeIterator<V> Self;
Node* _node;
RBTreeIterator(Node* node)
:_node(node)
{}
//迭代器一般要实现解引用,->,!=操作符
//解引用需要返回节点的val的引用,可以修改val
V& operator*(){
return _node->_val;
}
//->操作符需要返回节点的val的指针
V* operator->(){
return &_node->_val;
}
//判断迭代器是否相等传参就是迭代器,不是迭代器指针
bool operator!=(const Self& it){
return _node != it._node;
}
//迭代器的++和--重载
//返回值就是迭代器本身
//红黑树的迭代器是根据中序遍历实现,++就是中序遍历的下一个
Self& operator++(){
//如果右节点存在,下一个就是右节点的最左节点
if (_node->_right){
_node = _node->_right;
while (_node->_left){
_node = _node->_left;
}
}
//若右节点不存在,需要向上寻找
//当node作为父节点的左子树时,下一个才是parent
//是父节点的右子树,父亲已经先遍历过了
else{
Node* parent = _node->_parent;
while (_node == parent->_right){
_node = parent;
parent = parent->_parent;
}
//避免出现没有右子树,node==parent(此时parent是root),此时结束遍历了
//若node再++,程序还会跑,p更新到了root的左子树,node再一次=parent
//再次进入循环,出现死循环
if (_node->_right != parent){
_node = parent;
}
}
return *this;//返回调用++的迭代器*(前置++)
}
//--和++只有顺序上的不同,逻辑上相同
Self& operator--(){
if (_node->_left){
_node = _node->_left;
while (_node->_right){
_node = _node->_right;
}
}
else{
Node* parent = _node->_parent;
while (parent->_left == _node){
_node = parent;
parent = parent->_parent;
}
if (_node->_left != parent){
_node = parent;
}
}
return *this;
}
};
//红黑树实现
template<class K,class V,class KeyOfValue>
class RBTree{
public:
typedef RBNode<V> Node;
typedef RBTreeIterator<V> iterator;
RBTree()
:_header(new Node)
{
_header->_left = _header->_right = _header;
//partent默认为nullptr
}
iterator begin(){
return iterator(_header->_left);
}
iterator end(){
return iterator(_header);
}
iterator rbegin(){
return iterator(_header->_right);
}
红黑树的插入
插入接口非常繁琐,涉及到了各种情况的不同处理,需要旋转操作来保持平衡
//红黑树的插入
//返回值是当前插入位置的迭代器和一个bool
pair<iterator, bool> insert(const V& val){
//若是空树的情况
if (_header->_parent == nullptr){
Node* root = new Node(val);
_header->_parent = root;
root->_parent = _header;
_header->_left = _header->_right = root;
root->_color = BLACK;
return make_pair(iterator(root), true);
}
//不是空树走二叉搜索树的逻辑
Node* node = _header->_parent;//无法直接拿到root,需要这样取
Node* parent = nullptr;
KeyOfValue kov;
//注意此时就要使用回调函数区分map,set
//map的val是<k,v>键值对,比较的是val.first
//set的val就是k,可以直接比较
while (node){
parent = node;
if (kov(node->_val) == kov(val)){
return make_pair(iterator(node), false);
}
else if (kov(node->_val) > kov(val)){
node = node->_left;
}
else{
node = node->_right;
}
}
node = new Node(val);
Node* cur = node;//用于返回迭代器
if (kov(parent->_val) > kov(node->_val)){
parent->_left = node;
}
else {
parent->_right = node;
}
node->_parent = parent;//加入指向
//开始进行调整,若出现连续的两个红色就需要调整
//结束条件是node不是根节点,因为根节点是黑色的,node和parent是红色就需要调整
while (node != _header->_parent&&node->_parent->_color == RED){
parent = node->_parent;
Node* gfather = parent->_parent;
//若父节点是祖父节点的左节点
if (gfather->_left == parent){
Node* uncle = gfather->_right;
//若果uncle存在并为红色
if (uncle && uncle->_color == RED){
parent->_color = uncle->_color = BLACK;
gfather->_color = RED;
//此时父节点是黑色,gfather为红色,所以更新到红色
node = gfather;
}
else{
//uncle不存在或者为黑色都需要旋转
//此时node的位置也需要判断是否为双旋
if (node == parent->_right){
RotateL(parent);
swap(node, parent);
}
RotateR(gfather);
parent->_color = BLACK;
gfather->_color = RED;
break;
}
}
//若父节点是祖父节点的右节点
else{
Node* uncle = gfather->_left;
//若果uncle存在并为红色
if (uncle && uncle->_color == RED){
parent->_color = uncle->_color = BLACK;
gfather->_color = RED;
//此时父节点是黑色,gfather为红色,所以更新到红色
node = gfather;
}
else{
//uncle不存在或者为黑色都需要旋转
//此时node的位置也需要判断是否为双旋
if (node == parent->_left){
RotateR(parent);
swap(node, parent);
}
RotateL(gfather);
parent->_color = BLACK;
gfather->_color = RED;
break;
}
}
}
//调整结束,node此时可能刚刚结束旋转,gparent为红,并且不会再进入循环
//需要把根节点改黑色
_header->_parent->_color = BLACK;
//更新_header的指向
_header->_left = leftmost();
_header->_right = rightmost();
return make_pair(iterator(cur), true);
}
//左旋接口
void RotateL(Node* parent){
Node* subR = parent->_right;
Node* subRL = subR->_left;
subR->_left = parent;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
//判断根,不是根的情况需要祖父节点的指向
if (parent == _header->_parent){
_header->_parent = subR;
subR->_parent = _header;
}
else{
Node* gparent = parent->_parent;
if (gparent->_left == parent){
gparent->_left = subR;
}
else{
gparent->_right = subR;
}
subR->_parent = gparent;
}
parent->_parent = subR;
}
//右旋接口
void RotateR(Node* parent){
Node* subL = parent->_left;
Node* subLR = subL->_right;
subL->_right = parent;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
//判断根,不是根的情况需要祖父节点的指向
if (parent == _header->_parent){
_header->_parent = subL;
subL->_parent = _header;
}
else{
Node* gparent = parent->_parent;
if (gparent->_left == parent){
gparent->_left = subL;
}
else{
gparent->_right = subL;
}
subL->_parent = gparent;
}
parent->_parent = subL;
}
Node* leftmost(){
Node* node = _header->_parent;
while (node&& node->_left){
node = node->_left;
}
return node;
}
Node* rightmost(){
Node* node = _header->_parent;
while (node && node->_right){
node = node->_right;
}
return node;
}
void inorder(){
_inorder(_header->_parent);
cout << endl;
}
void _inorder(Node* root){
if (root){
_inorder(root->_left);
cout << root->_kv.first << " ";
_inorder(root->_right);
}
}
验证
根据红黑树的特点验证树
//红黑树的验证
bool isBalence(){
//空树也是红黑树
if (_header->_parent == nullptr){
return true;
}
Node* root = _header->_parent;
//根节点必须为黑色
if (root->_color == RED){
return false;
}
//统计一条路径黑色节点的个数
int bcount = 0;
Node* node = root;
while (node){
if (node->_color == BLACK){
++bcount;
}
node = node->_left;
}
cout << "Bcount: " << bcount << endl;
int nodebcount = 0;
return _isBalence(root, bcount, nodebcount);
}
//递归判断每一条路径上的节点数字是否相等
//递归的结束条件:当递归到空指针时,用计数器的节点大小相比
//需要返回上一级是否当前路径的数字相等,bool返回值
//本轮递归需要判断是否为黑色并++,并继续判定左右子树是否为黑色
bool _isBalence(Node* root,int &bcount,int nodecount){
if (root == nullptr){
if (nodecount == bcount){
return true;
}
else{
return false;
}
}
if (root->_color == BLACK){
++nodecount;
}
//判断是否有相邻的红色节点
if (root->_parent && root->_color == RED && root->_parent->_color == RED){
cout << "data: " << root->_kv.first << endl;
return false;
}
return _isBalence(root->_left, bcount, nodecount) &&
_isBalence(root->_right, bcount, nodecount);
}
private:
Node* _header;
};
以上是关于数据结构关联式容器底层红黑树的模拟实现的主要内容,如果未能解决你的问题,请参考以下文章
[C/C++]详解STL容器9-基于红黑树模拟实现map和set
[C/C++]详解STL容器9-基于红黑树模拟实现map和set
[C/C++]详解STL容器9-基于红黑树模拟实现map和set