【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,主要是用来复习的

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

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

相关文章
|
24天前
|
存储 自然语言处理 C++
map和set的简单介绍
map和set的简单介绍
20 1
|
14天前
|
存储 JavaScript 前端开发
set和map的区别
set和map的区别
17 4
|
24天前
|
存储 算法 C语言
【C++入门到精通】C++入门 —— map & multimap (STL)
之前我们学习了C++的基础和一些概念,现在将探讨重要的STL组件——map与multimap。map是关联容器,提供有序键值对存储,基于红黑树,支持高效查找、插入和删除。每个键唯一对应一个值。multimap则允许键的重复。两者都提供迭代器支持,但map的键是唯一的,而multimap允许键重复,插入和查找效率不同。更多详情,请查阅官方文档。祝学习愉快!
11 0
|
24天前
|
存储 算法 C++
【C++ map结构 】std::map 和 std::unordered_map 在使用上的差异
【C++ map结构 】std::map 和 std::unordered_map 在使用上的差异
20 0
|
24天前
|
存储 编译器 容器
用红黑树封装实现map和set
用红黑树封装实现map和set
14 0
|
24天前
|
算法 安全 编译器
C++:模版进阶 | Priority_queue的模拟实现
C++:模版进阶 | Priority_queue的模拟实现
|
25天前
|
存储 JavaScript 索引
js开发:请解释什么是ES6的Map和Set,以及它们与普通对象和数组的区别。
ES6引入了Map和Set数据结构。Map的键可为任意类型,有序且支持get、set、has、delete操作;Set存储唯一值,提供add、delete、has方法。两者皆可迭代。示例展示了Map和Set的基本用法,如添加、查询、删除元素。
13 2
|
25天前
|
存储 算法 数据管理
【C++入门到精通】C++入门 ——搜索二叉树(二叉树进阶)
在C++中,本文介绍了搜索二叉树(二叉搜索树,BST)的概念和基本操作,包括搜索、插入和删除。搜索操作从根节点开始,按值大小决定左右查找;插入操作找到合适位置新建节点;删除操作需考虑无子节点、单子节点和双子节点的情况。文中还提供了非递归和递归实现的C++代码示例。此外,讨论了搜索二叉树在K模型和KV模型中的应用以及性能分析,强调了保持树平衡的重要性。
15 0
|
28天前
|
存储 安全 程序员
【C++ 包装器类 智能指针】完全教程:std::unique_ptr、std::shared_ptr、std::weak_ptr的用法解析与优化 — 初学者至进阶指南
【C++ 包装器类 智能指针】完全教程:std::unique_ptr、std::shared_ptr、std::weak_ptr的用法解析与优化 — 初学者至进阶指南
66 0
|
28天前
|
存储 算法 C++
【C++ 包装器类 map】C++ 标准库(std)中的map结构 哈希表(unordered_map)和黑红树(map)教程
【C++ 包装器类 map】C++ 标准库(std)中的map结构 哈希表(unordered_map)和黑红树(map)教程
78 1

热门文章

最新文章