【C++进阶】七、使用红黑树对set和map进行封装

简介: 目录前言一、改造红黑树1.1 红黑树迭代器相关1.2 红黑树接口相关二、set代码三、map代码

目录

前言

一、改造红黑树

1.1 红黑树迭代器相关

1.2 红黑树接口相关

二、set代码

三、map代码


前言

       set 是 K模型的容器,map 是 KV模型的容器,但是它们的底层实现都是红黑树实现,即用红黑树可以封装出 set和 map,之前的篇章已经讲过红黑树了,这里就不解释了。

       接下来对红黑树进行改造,用改造后的红黑树封装出 set 和 map

一、改造红黑树

使用的代码是之前篇章红黑树实现的代码,改造后红黑树的框架如下:

#pragma onceenumColour{
RED,
BLACK,
};
template<classT>structRBTreeNode{
//构造函数RBTreeNode(constT&data)
        :_data(data)
        ,_left(nullptr)
        ,_right(nullptr)
        ,_parent(nullptr)
        ,_col(RED)
    {}
//成员变量T_data;
RBTreeNode<T>*_left;
RBTreeNode<T>*_right;
RBTreeNode<T>*_parent;
Colour_col;
};
template<classT, classRef, classPtr>struct__RBTreeIterator{
typedefRBTreeNode<T>Node;
typedef__RBTreeIterator<T, Ref, Ptr>Self;
typedef__RBTreeIterator<T, T&, T*>iterator;
//构造__RBTreeIterator(Node*node)
        :_node(node)
    {}
// 普通迭代器的时候,他是拷贝构造// const迭代器的时候,他是构造,支持用普通迭代器构造const迭代器__RBTreeIterator(constiterator&s)
        :_node(s._node)
    {}
Refoperator*()
    {
return_node->_data;
    }
Ptroperator->()
    {
return&_node->_data;
    }
booloperator!=(constSelf&s)const    {
return_node!=s._node;
    }
booloperator==(constSelf&s)const    {
return_node==s._node;
    }
Self&operator++()
    {
if (_node->_right)
        {
Node*min=_node->_right;
while (min->_left)
            {
min=min->_left;
            }
_node=min;
        }
else        {
Node*cur=_node;
Node*parent=cur->_parent;
while (parent&&cur==parent->_right)
            {
cur=cur->_parent;
parent=parent->_parent;
            }
_node=parent;
        }
return*this;
    }
Self&operator--()
    {
if (_node->_left)
        {
Node*max=_node->_left;
while (max->_right)
            {
max=max->_right;
            }
_node=max;
        }
else        {
Node*cur=_node;
Node*parent=cur->_parent;
while (parent&&cur==parent->_left)
            {
cur=cur->_parent;
parent=parent->_parent;
            }
_node=parent;
        }
return*this;
    }
//成员变量Node*_node;
};
template<classK, classT, classKeyOfT>classRBTree{
typedefRBTreeNode<T>Node;
public:
typedef__RBTreeIterator<T, T&, T*>iterator;
typedef__RBTreeIterator<T, constT&, constT*>const_iterator;
iteratorbegin()
    {
Node*left=_root;
while (left->_left)
        {
left=left->_left;
        }
returniterator(left);// 使用匿名对象构造返回    }
const_iteratorbegin()const    {
Node*left=_root;
while (left->_left)
        {
left=left->_left;
        }
returnconst_iterator(left);
    }
iteratorend()
    {
returniterator(nullptr);
    }
const_iteratorend()const    {
returnconst_iterator(nullptr);
    }
//插入pair<iterator, bool>Insert(constT&data)
    {}
//中序遍历voidInOrder()
    {
_InOrder(_root);
    }
//检查红黑树特性boolIsBalance()
    {}
private:
//检查每条路径的黑色节点是否相等 && 是否出现连续红色节点boolCheck(Node*root, intblackNum, intref)
    {}
void_InOrder(Node*root)
    {}
//左单旋voidRotateL(Node*parent)
    {}
//右单旋voidRotateR(Node*parent)
    {}
private:
Node*_root=nullptr;//缺省值};

代码过多就不贴完了,完整代码链接:

https://gitee.com/Maple_fylqh/code/blob/master/code_c++/Set_and_Map/Set_and_Map/RBTree.h

改造红黑树就是给红黑树增加迭代器相关的,这棵泛型的红黑树可以同时封装出set 和 map

1.1 红黑树迭代器相关

迭代器模板参数在 list 模拟实现已经有过解释,这里不解释了,模板参数 T 是红黑树存放数据的类型,下面解释

template<class T, class Ref, class Ptr>

       上面迭代器类只需提供构造函数即可, 拷贝构造和赋值不需要自己实现,默认生成的即可,当前迭代器赋值给另一个迭代器是不需要深拷贝的,浅拷贝就可以,拷贝构造也是;析构释放空间的事情不归迭代器类管理,迭代器类只需构建迭代器对象即可

上面红黑树迭代器实现的接口有:

//构造__RBTreeIterator(Node*node)
        :_node(node)
    {}
// 普通迭代器的时候,他是拷贝构造// const迭代器的时候,他是构造,支持用普通迭代器构造const迭代器__RBTreeIterator(constiterator&s)
        :_node(s._node)
    {}
//解引用,获取数据Refoperator*(){}
//返回数据的地址Ptroperator->(){}
//比较两个迭代器是否不相等booloperator!=(constSelf&s)const{}
//比较两个迭代器是否相等booloperator==(constSelf&s)const{}
//前置++Self&operator++(){}
//前置--Self&operator--(){}

说明一下,迭代器都是支持普通迭代器构造const迭代器

image.png

实现如下,这里它利用了构造函数和拷贝构造函数的特性,当传入的迭代器是普通迭代器时:参数类型与拷贝构造参数要求一致,它就是拷贝构造;当传入的的迭代器是 const迭代器,参数类型与拷贝构造函数不匹配,类型有差异,它就变成了构造函数

typedef__RBTreeIterator<T, T&, T*>iterator;
// 普通迭代器的时候,他是拷贝构造// const迭代器的时候,他是构造,支持用普通迭代器构造const迭代器__RBTreeIterator(constiterator&s)
    :_node(s._node)
{}

前置++ 和前置-- 是红黑树迭代器实现的重点

前置++

一个结点的正向迭代器进行 ++ 操作后,应该根据红黑树中序遍历的序列找到当前结点的下一个结点

如下图,传入第一个节点是1,迭代器++ 下一个节点是6,再次++ 节点为8,迭代器要达到这种效果才可以满足需求

image.png

思路:

  1. 传入一个节点,如果节点右不为空就先往右边走,找到右边节点最小的节点,该节点就是 ++ 的下一个节点
  2. 传入一个节点,如果节点的右子树为空,去找祖先,迭代依次往上走,找到孩子是父亲左的那个祖先节点 
  3. image.png

代码如下:

Self&operator++()
{
if (_node->_right)//传入节点右不为空,找右边最小节点    {
Node*min=_node->_right;
while (min->_left)
        {
min=min->_left;
        }
_node=min;
    }
else)//传入节点为空    {
Node*cur=_node;
Node*parent=cur->_parent;
while (parent&&cur==parent->_right)
        {
cur=cur->_parent;
parent=parent->_parent;
        }
_node=parent;
    }
return*this;
}

前置--

一个结点的迭代器进行 -- 操作后,就是红黑树中序遍历找到的结点的前一个结点

如下图,比如传入第一个节点是27,迭代器-- 下一个节点是25,再次 -- 节点为22,迭代器要达到这种效果才可以满足需求

image.png

--的思路和 ++ 的思路类似,只不过是反过来找

思路:

  1. 传入一个节点,如果节点左不为空就先往左边走,找到左边节点最大的节点,该节点就是 -- 的下一个节点
  2. 传入一个节点,如果节点的左子树为空,去找祖先,迭代依次往上走,找到孩子是父亲右的那个祖先节点
  3. image.png

代码如下:

Self&operator--()
{
if (_node->_left)//传入节点左不为空就先往左边走,    {
Node*max=_node->_left;
while (max->_right)
        {
max=max->_right;
        }
_node=max;
    }
else//传入节点左为空    {
Node*cur=_node;
Node*parent=cur->_parent;
while (parent&&cur==parent->_left)
        {
cur=cur->_parent;
parent=parent->_parent;
        }
_node=parent;
    }
return*this;
}

其他就不介绍了

1.2 红黑树接口相关

template<classK, classT, classKeyOfT>classRBTree{
typedefRBTreeNode<T>Node;
public:
typedef__RBTreeIterator<T, T&, T*>iterator;
typedef__RBTreeIterator<T, constT&, constT*>const_iterator;
private:
Node*_root=nullptr;//缺省值};

说明一下模板参数,为了实现泛型RBTree:

  1. 红黑树第一个参数是 K(Key),主要用于查找数据;
  2. 红黑树第二个模板参数 T ,可以识别是 map 还是 set,因为 set和 map传给 T的参数不一样,set 传的是 Key,map 传的是键值对 pair;
  3. 红黑树第三个参数是 KeyOfT,这是一个仿函数,由于红黑树中 T 存的可能是K,也可能是pair。那我们插入的时候该怎么比较结点的大小呢,对于set是没有问题的,直接可以用T比较,但是map就不行了,我们要取出pair的 first来进行比较
  4. image.png

二、set代码

#include "RBTree.h"namespacefy{
template<classK>classset    {
structSetKeyOfT        {
constK&operator()(constK&key)
            {
returnkey;
            }
        };
public:
typedeftypenameRBTree<K, K, SetKeyOfT>::const_iteratoriterator;
typedeftypenameRBTree<K, K, SetKeyOfT>::const_iteratorconst_iterator;
iteratorbegin()const        {
return_t.begin();
        }
iteratorend()const        {
return_t.end();
        }
pair<iterator, bool>insert(constK&key)
        {
pair<typenameRBTree<K, K, SetKeyOfT>::iterator, bool>ret=_t.Insert(key);
returnpair<iterator, bool>(ret.first, ret.second);
        }
private:
RBTree<K, K, SetKeyOfT>_t;
    };
voidTestSet()
    {
intarr[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
set<int>s;
for (auto&e : arr)
        {
s.insert(e);
        }
set<int>::iteratorit=s.begin();
while (it!=s.end())
        {
cout<<*it<<" ";
++it;
        }
cout<<endl;
for (auto&e : s)
        {
cout<<e<<" ";
        }
cout<<endl;
    }
}

三、map代码

#include "RBTree.h"namespacefy{
template<classK, classV>classmap    {
structMapKeyOfT        {
constK&operator()(constpair<constK, V>&kv)
            {
returnkv.first;
            }
        };
public:
typedeftypenameRBTree<constK, pair<constK, V>, MapKeyOfT>::iteratoriterator;
typedeftypenameRBTree<constK, pair<constK, V>, MapKeyOfT>::const_iteratorconst_iterator;
iteratorbegin()
        {
return_t.begin();
        }
iteratorend()
        {
return_t.end();
        }
const_iteratorbegin()const        {
return_t.begin();
        }
const_iteratorend()const        {
return_t.end();
        }
pair<iterator, bool>insert(constpair<constK, V>&kv)
        {
return_t.Insert(kv);
        }
V&operator[](constK&key)
        {
pair<iterator, bool>ret=insert(make_pair(key, V()));
returnret.first->second;
        }
private:
RBTree<K, pair<constK, V>, MapKeyOfT>_t;
    };
voidTestMap()
    {
intarr[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
map<int, int>m;
for (auto&e : arr)
        {
m.insert(make_pair(e, e));
        }
map<int, int>::iteratorit=m.begin();
while(it!=m.end())
        {
it->second++;
cout<<it->first<<"->"<<it->second<<endl;
++it;
        }
map<string, int>countMap;
stringstr[] = { "苹果", "西瓜", "香蕉", "草莓", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
for (auto&e : str)
        {
countMap[e]++;
        }
for (auto&kv : countMap)
        {
cout<<kv.first<<":"<<kv.second<<endl;
        }
    }
}

这篇文章写起来逻辑有点不通(难写)dog,主要是用来复习的

----------------我是分割线---------------

文章到这里就结束了,下一篇即将更新

相关文章
|
2天前
|
存储 算法 安全
基于红黑树的局域网上网行为控制C++ 算法解析
在当今网络环境中,局域网上网行为控制对企业和学校至关重要。本文探讨了一种基于红黑树数据结构的高效算法,用于管理用户的上网行为,如IP地址、上网时长、访问网站类别和流量使用情况。通过红黑树的自平衡特性,确保了高效的查找、插入和删除操作。文中提供了C++代码示例,展示了如何实现该算法,并强调其在网络管理中的应用价值。
|
2月前
|
存储 C++ 容器
【C++】map、set基本用法
本文介绍了C++ STL中的`map`和`set`两种关联容器。`map`用于存储键值对,每个键唯一;而`set`存储唯一元素,不包含值。两者均基于红黑树实现,支持高效的查找、插入和删除操作。文中详细列举了它们的构造方法、迭代器、容量检查、元素修改等常用接口,并简要对比了`map`与`set`的主要差异。此外,还介绍了允许重复元素的`multiset`和`multimap`。
35 3
【C++】map、set基本用法
|
2月前
|
存储 C++
【C++】红黑树
红黑树是一种自平衡二叉搜索树,通过节点颜色(红或黑)及特定规则维持平衡,确保操作效率。其性质包括:每个节点非红即黑,根节点必黑,红节点的子节点必黑,从任一节点到其每个叶子的所有路径含相同数量的黑节点。实现上,通过节点结构定义、基本操作(插入、删除、旋转等)、维护平衡性质等步骤完成。代码示例展示了节点定义、插入操作及旋转调整方法。
35 2
【C++】红黑树
|
2月前
|
存储 算法 C++
【C++】unordered_map(set)
C++中的`unordered`容器(如`std::unordered_set`、`std::unordered_map`)基于哈希表实现,提供高效的查找、插入和删除操作。哈希表通过哈希函数将元素映射到特定的“桶”中,每个桶可存储一个或多个元素,以处理哈希冲突。主要组成部分包括哈希表、哈希函数、冲突处理机制、负载因子和再散列,以及迭代器。哈希函数用于计算元素的哈希值,冲突通过开链法解决,负载因子控制哈希表的扩展。迭代器支持遍历容器中的元素。`unordered_map`和`unordered_set`的插入、查找和删除操作在理想情况下时间复杂度为O(1),但在冲突较多时可能退化为O(n)。
26 5
|
2月前
|
存储 C++ 容器
【C++】set模拟实现
C++中的`set`是STL提供的一种关联容器,用于存储唯一元素并自动按特定顺序(默认升序)排序。其内部通过红黑树实现,保证了高效的插入、删除和查找操作,时间复杂度均为O(log n)。`set`支持迭代器遍历,提供了良好的数据访问接口。
43 3
|
5月前
|
关系型数据库 C++ 容器
【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——红黑树
【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——红黑树
43 0
|
6月前
|
C++ 容器
【C++】map和set封装
【C++】map和set封装
46 2
|
6月前
|
Java C++ Python
【C++】手撕红黑树
【C++】手撕红黑树
32 1
|
6月前
|
存储 C++ 容器
【C++】开散列实现unordered_map与unordered_set的封装
【C++】开散列实现unordered_map与unordered_set的封装
63 0
|
28天前
|
算法
你对Collection中Set、List、Map理解?
你对Collection中Set、List、Map理解?
60 18
你对Collection中Set、List、Map理解?