模拟实现map&set

文章目录

  • 模拟实现map&set
  • 1. map的特性
  • 2. map的模板参数说明
  • 3. map的构造
  • 4. map的迭代器
  • 5. map的容量与元素访问
  • 6. map中元素的修改
  • 7. map的模拟实现
  • 8. set的模拟实现

1. map的特性

1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。

2. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名称为pair:
typedef pair value_type;

3. 在内部,map中的元素总是按照键值key进行比较排序的。

4. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。

5. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。

6. map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))

2. map的模板参数说明

  • key: 键值对中key的类型
  • T: 键值对中value的类型
  • Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)
  • Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的空间配置器

注意:在使用map时,需要包含头文件。

3. map的构造

函数声明 功能介绍
map() 构造一个空的map

4. map的迭代器

5. map的容量与元素访问

函数声明 功能简介
bool empty ( ) const 检测map中的元素是否为空,是返回true,否则返回false
size_type size() const 返回map中有效元素的个数
mapped_type& operator[] (constkey_type& k) 返回去key对应的value

注意当key不存在时,mapped_type& operator[] (constkey_type& k)相当于用默认value与key构造键值对然后插入,返回该默认value,at()函数直接抛异常

6. map中元素的修改

函数声明 功能简介
pair<iterator,bool> insert (const value_type& x ) 在map中插入键值对x,注意x是一个键值对,返回值也是键值对:iterator代表新插入元素的位置,bool代表释放插入成功
void erase ( iterator position ) 删除position位置上的元素
size_type erase ( const key_type& x) 删除键值为x的元素
void erase ( iterator first,iterator last ) 删除[first, last)区间中的元素
void swap (map<Key,T,Compare,Allocator>&mp) 交换两个map中的元素
void clear ( ) 将map中的元素清空
iterator find ( const key_type& x) 在map中插入key为x的元素,找到返回该元素的位置的迭代器,否则返回end
const_iterator find ( const key_type& x ) const 在map中插入key为x的元素,找到返回该元素的位置的const迭代器,否则返回cend
size_type count ( const key_type& x ) const 返回key为x的键值在map中的个数,注意map中key是唯一的,因此该函数的返回值要么为0,要么为1,因此也可以用该函数来检测一个key是否在map中

【总结】

  1. map中的的元素是键值对
  2. map中的key是唯一的,并且不能修改
  3. 默认按照小于的方式对key进行比较
  4. map中的元素如果用迭代器去遍历,可以得到一个有序的序列
  5. map的底层为平衡搜索树(红黑树),查找效率比较高
  6. 支持[]操作符,operator[]中实际进行插入查找。

set/multimap/multiset的接口和map基本类似,主要的区别在于元素能否重复,是否可以使用operator[]操作

7. map的模拟实现

map的底层使用的是红黑树,所以先封装一个红黑树
红黑树

要实现map还需要在前文的基础上进行填充一些操作:

1. 红黑树的迭代器

迭代器的好处是可以方便遍历,是数据结构的底层实现与用户透明。如果想要给红黑树增加迭代器,需要考虑以前问题:

begin()与end()

  • STL明确规定,begin()与end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,可以得到一 个有序的序列,
  • 因此:begin()可以放在红黑树中最小节点(即最左侧节点)的位置,end()放在最大节点(最右侧节点)的下一个位置,关键是最大节点的下一个位置在哪块?
  • 能否给成nullptr呢?答案是行不通的,因为对end()位置的迭代器进行–操作,必须要能找最后一个元素,此处就不行,因此最好的方式是将end()放在头结点的位置

2. operator++()与operator–()

// 找迭代器的下一个节点,下一个节点肯定比其大
void Increasement()
{//分两种情况讨论:_pNode的右子树存在和不存在// 右子树存在if(_pNode->_pRight){// 右子树中最小的节点,即右子树中最左侧节点_pNode = _pNode->_pRight;while(_pNode->_pLeft)_pNode = _pNode->_pLeft;}else{// 右子树不存在,向上查找,直到_pNode != pParent->rightPNode pParent = _pNode->_pParent;while(pParent->_pRight == _pNode){_pNode = pParent;pParent = _pNode->_pParent;}// 特殊情况:根节点没有右子树if(_pNode->_pRight != pParent)_pNode = pParent;}
}// 获取迭代器指向节点的前一个节点
void Decreasement()
{//分三种情况讨论:_pNode 在head的位置,_pNode 左子树存在,_pNode 左子树不存在// 1. _pNode 在head的位置,--应该将_pNode放在红黑树中最大节点的位置if(_pNode->_pParent->_pParent == _pNode && _pNode->_color == RED)_pNode = _pNode->_pRight;else if(_pNode->_pLeft){// 2. _pNode的左子树存在,在左子树中找最大的节点,即左子树中最右侧节点_pNode = _pNode->_pLeft;while(_pNode->_pRight)_pNode = _pNode->_pRight;}else{// _pNode的左子树不存在,只能向上找PNode pParent = _pNode->_pParent;while(_pNode == pParent->_pLeft){_pNode = pParent;pParent = _pNode->_pParent;}_pNode = pParent;}
}

3. 2 改造红黑树

// 因为关联式容器中存储的是<key, value>的键值对,因此
// k为key的类型,
// ValueType: 如果是map,则为pair<K, V>; 如果是set,则为k
// KeyOfValue: 通过value来获取key的一个仿函数类
template<class K, class ValueType, class KeyOfValue>class RBTree{typedef RBTreeNode<ValueType> Node;typedef Node* PNode;public:typedef RBTreeIterator<ValueType, ValueType*, ValueType&>      Iterator;public:RBTree();~RBTree()
/
// IteratorIterator Begin(){ return Iterator(_pHead->_pLeft);}Iterator End(){ return Iterator(_pHead);}
//
// Modifypair<Iterator, bool> Insert(const ValueType& data){// 插入节点并进行调整// 参考上文...return make_pair(Iterator(pNewNode), true);}// 将红黑树中的节点清空void Clear();Iterator Find(const K& key);
//
// capacitysize_t Size()const;bool Empty()const;// ……private:PNode _pHead;size_t _size; // 红黑树中有效节点的个数
};

RBTree.hpp

#pragma once
#include<algorithm>enum Color {RED,BLACK
};template<class ValueType>
struct RBTreeNode
{RBTreeNode(const ValueType& data = ValueType(), Color color = RED):_pLeft(nullptr),_pRight(nullptr),_pParent(nullptr),_data(data),_color(color){}RBTreeNode<ValueType>* _pLeft;RBTreeNode<ValueType>* _pRight;RBTreeNode<ValueType>* _pParent;ValueType _data;Color _color;
};template<class T>
class Iterator {public:typedef RBTreeNode<T> Node;typedef Iterator<T> Self;Iterator(Node* pNode=nullptr):_pNode(pNode){}T& operator*() {return _pNode->_data;}T* operator->() {return &(_pNode->_data);}Self& operator++() {Next();return *this;}Self operator++(int) {Self tmp(_pNode);Next();return tmp;}bool operator==(Self& t){return _pNode == t._pNode;}bool operator!=(Self& t){return _pNode != t._pNode;}Self& operator--() {Prev();return *this;}Self operator--(int) {Self tmp(_pNode);Prev();return tmp;}
private:void Next() {//如果有右子树if (_pNode->_pRight) {_pNode = _pNode->_pRight;while (_pNode->_pLeft)_pNode = _pNode->_pLeft;return;}Node* pParent = _pNode->_pParent;while (pParent->_pRight == _pNode) {_pNode = pParent;pParent = _pNode->_pParent;}//根节点没有右子树,并且迭代器刚好在根节点位置if (_pNode->_pRight != pParent)_pNode = pParent;}void Prev() {//1._pNode在head位置(即end()位置),应该将_pNode放在最大结点处if (_pNode->_pParent->_pParent == _pNode && _pNode->_color == RED)_pNode = _pNode->_pRight;//2. 如果有左子树else if (_pNode->_pLeft) {_pNode = _pNode->_pLeft;while (_pNode->_pRight)_pNode = _pNode->_pRight;}else {Node* pParent = _pNode->_pParent;while (pParent->_pLeft == _pNode) {_pNode = pParent;pParent = _pNode->_pParent;}_pNode = pParent;}}Node* _pNode;
};template<class T,class KorV>
class RBTree {public:typedef RBTreeNode<T> Node;typedef Node* PNode;typedef RBTree<T, KorV> Self;typedef Iterator<T> iterator;
public:RBTree():_pHead(new Node),_size(0){_pHead->_pLeft = _pHead;_pHead->_pRight = _pHead;}~RBTree() {if (_pHead->_pParent)del(_pHead->_pParent);delete _pHead;}std::pair<iterator,bool> Insert(const T& data) {PNode& pRoot = GetRoot();   //获取根节点PNode newPtr = nullptr;if (nullptr == pRoot) {   //如果红黑树为空newPtr = pRoot = new Node(data, BLACK);pRoot->_pParent = _pHead;}else {PNode pParent = nullptr;PNode pCur = pRoot;//插入节点while (pCur) {pParent = pCur;if (KorV()(data) < KorV()(pCur->_data))pCur = pCur->_pLeft;else if (KorV()(data) > KorV()(pCur->_data))pCur = pCur->_pRight;elsereturn std::make_pair(iterator(), false);}newPtr = pCur = new Node(data);if (KorV()(data) < KorV()(pParent->_data)) {pParent->_pLeft = pCur;pCur->_pParent = pParent;}else {pParent->_pRight = pCur;pCur->_pParent = pParent;}//检测新节点插入后。红黑树的性质是否遭到破坏while (pParent != _pHead && RED == pParent->_color) {PNode pGrand = pParent->_pParent;//pParent在pGrand左侧的情况if (pParent == pGrand->_pLeft) {PNode unclue = pGrand->_pRight;//情况一if (unclue&&RED == unclue->_color) {pParent->_color = BLACK;unclue->_color = BLACK;pGrand->_color = RED;pCur = pGrand;pParent = pCur->_pParent;}else {//情况三if (pParent->_pRight == pCur) {RotateLeft(pParent);std::swap(pParent, pCur);}//情况二RotateRight(pGrand);pParent->_color = BLACK;pGrand->_color = RED;}}//pParent在pGrand右侧的情况else {PNode unclue = pGrand->_pLeft;//情况一if (unclue&&RED == unclue->_color) {pParent->_color = BLACK;unclue->_color = BLACK;pGrand->_color = RED;pCur = pGrand;pParent = pCur->_pParent;}else {//情况三if (pParent->_pLeft == pCur) {RotateRight(pParent);std::swap(pParent, pCur);}//情况二RotateLeft(pGrand);pParent->_color = BLACK;pGrand->_color = RED;}}}}//根节点的颜色可能被修改,将其改回黑色pRoot->_color = BLACK;//更新头结点的左右孩子_pHead->_pLeft = LeftMost();_pHead->_pRight = RightMost();++_size;return std::make_pair(iterator(newPtr), true);}void Inorder(){_InOrder(GetRoot());std::cout << std::endl;}bool IsValidRBTree(){PNode pRoot = GetRoot();// 空树也是红黑树if (nullptr == pRoot)return true;// 检测根节点是否满足情况if (BLACK != pRoot->_color){std::cout << "违反红黑树性质二:根节点必须为黑色" << std::endl;return false;}// 获取任意一条路径中黑色节点的个数size_t blackCount = 0;PNode pCur = pRoot;while (pCur){if (BLACK == pCur->_color)blackCount++;pCur = pCur->_pLeft;}// 检测是否满足红黑树的性质,k用来记录路径中黑色节点的个数size_t k = 0;return _IsValidRBTree(pRoot, k, blackCount);}iterator find(const T& data)const {PNode ptr = GetRoot();while (ptr) {if (KorV()(data) == KorV()(ptr->_data))return iterator(ptr);else if (KorV()(data) < KorV()(ptr->_data))ptr = ptr->_pLeft;elseptr = ptr->_pRight;}return end();}size_t size()const {return _size;}bool empty()const {return _size == 0;}iterator begin() {return iterator(_pHead->_pLeft);}iterator end() {return iterator(_pHead);}
private:void _InOrder(Node* pRoot){if (pRoot){_InOrder(pRoot->_pLeft);std::cout << pRoot->_data << " ";_InOrder(pRoot->_pRight);}}void del(PNode ptr) {if (ptr->_pLeft)del(ptr->_pLeft);if (ptr->_pRight)del(ptr->_pRight);delete ptr;}bool _IsValidRBTree(PNode pRoot, size_t k, const size_t blackCount) {//走到null之后,判断k和black是否相等if (nullptr == pRoot){if (k != blackCount){std::cout << "违反性质四:每条路径中黑色节点的个数必须相同" << std::endl;return false;}return true;}// 统计黑色节点的个数if (BLACK == pRoot->_color)k++;// 检测当前节点与其双亲是否都为红色PNode pParent = pRoot->_pParent;if (pParent && RED == pParent->_color && RED == pRoot->_color){std::cout << "违反性质三:没有连在一起的红色节点" << std::endl;return false;}return _IsValidRBTree(pRoot->_pLeft, k, blackCount) &&_IsValidRBTree(pRoot->_pRight, k, blackCount);}PNode& GetRoot() {return _pHead->_pParent;}PNode LeftMost() {PNode ptr = GetRoot();if (!ptr)return _pHead;while (ptr->_pLeft)ptr = ptr->_pLeft;return ptr;}PNode RightMost() {PNode ptr = GetRoot();if (!ptr)return _pHead;while (ptr->_pRight)ptr = ptr->_pRight;return ptr;}void RotateLeft(PNode pParent) {PNode pPParent = pParent->_pParent;PNode pRight = pParent->_pRight;pParent->_pRight = pRight->_pLeft;if (pRight->_pLeft)pRight->_pLeft->_pParent = pParent;pRight->_pLeft = pParent;pParent->_pParent = pRight;//当pPParent不存在时if (pPParent==_pHead) {//设置pRight为根节点_pHead->_pParent = pRight;pRight->_pParent = _pHead;}else {if (pPParent->_pLeft == pParent) {pPParent->_pLeft = pRight;pRight->_pParent = pPParent;}else {pPParent->_pRight = pRight;pRight->_pParent = pPParent;}}}void RotateRight(PNode pParent) {PNode pPParent = pParent->_pParent;PNode pLeft = pParent->_pLeft;pParent->_pLeft = pLeft->_pRight;if (pLeft->_pRight)pLeft->_pRight->_pParent = pParent;pLeft->_pRight = pParent;pParent->_pParent = pLeft;//当pPParent不存在时if (pPParent == _pHead) {//设置pLeft为根节点_pHead->_pParent = pLeft;pLeft->_pParent = _pHead;}else {if (pPParent->_pLeft == pParent) {pPParent->_pLeft = pLeft;pLeft->_pParent = pPParent;}else {pPParent->_pRight = pLeft;pLeft->_pParent = pPParent;}}}
private:PNode _pHead;    //头结点(根节点的父亲节点)size_t _size;
};

map.hpp

#include"RBTree.hpp"template<class K,class V>
class Map {typedef std::pair<K, V> ValueType;struct KorV {const K& operator()(const ValueType& v)const{return v.first;}};typename typedef RBTree<ValueType, KorV>::iterator iterator;
public:Map() {}iterator begin() {return _tree.begin();}iterator end() {return _tree.end();}size_t size()const {return _tree.size();}bool empty()const {return _tree.empty();}std::pair<iterator, bool> insert(const ValueType& data) {return _tree.Insert(data);}iterator find(const K& key)const{return _tree.find(ValueType(key, V()));}V& operator[](const K& k) {return (*(insert(ValueType(k, V())).first)).second;}
private:RBTree<ValueType, KorV> _tree;
};

8. set的模拟实现

set.hpp

#include"RBTree.hpp"template<class K>
class Set {typedef K ValueType;struct KorV {const K& operator()(const ValueType& data) {return data;}};
public:typename typedef RBTree<ValueType, KorV>::iterator iterator;
public:Set(){}iterator begin() {return _tree.begin();}iterator end() {return _tree.end();}std::pair<iterator, bool> insert(const ValueType& data) {return _tree.Insert(data);}size_t size()const {return _tree.size();}bool empty()const {return _tree.empty();}iterator find(const K& key) {return _tree.find(key);}
private:RBTree<ValueType, KorV> _tree;
};

模拟实现mapset相关推荐

  1. springboot实现SSE服务端主动向客户端推送数据,java服务端向客户端推送数据,kotlin模拟客户端向服务端推送数据

    SSE服务端推送 服务器向浏览器推送信息,除了 WebSocket,还有一种方法:Server-Sent Events(以下简称 SSE).本文介绍它的用法. 在很多业务场景中,会涉及到服务端向客户端 ...

  2. curl模拟post请求

    另外可尝试 postman工具 或者用request 直接请求 CURL 发送POST请求curl -header "Content-Type: application/json" ...

  3. flask_模拟请求post,get

    #coding:utf-8 import requestsres = requests.post(url="http://192.168.135.105:8888/",data={ ...

  4. 模拟内存计算如何解决边缘人工智能推理的功耗挑战

    模拟内存计算如何解决边缘人工智能推理的功耗挑战 How analog in-memory computing can solve power challenges of edge AI inferen ...

  5. 为放大器模拟输入模块提供可靠的输入过电压保护

    为放大器模拟输入模块提供可靠的输入过电压保护 Signal Chain Basics #159: Provide robust input overvoltage protection for amp ...

  6. 模拟Servlet本质

    JavaWeb系列教程,持续更新 JavaWeb-Servlet 模拟Servlet本质 使用IDEA开发Servlet程序 Servlet对象的生命周期 适配器(GenericServlet)改造S ...

  7. 2021年大数据Flink(四十):​​​​​​​Flink模拟双十一实时大屏统计

    目录 Flink模拟双十一实时大屏统计 需求 数据 编码步骤: 1.env 2.source 3.transformation 4.使用上面聚合的结果,实现业务需求: 5.execute 参考代码 实 ...

  8. Python:模拟登录、点击和执行 JavaScript 语句案例

    案例一:网站模拟登录 # douban.pyfrom selenium import webdriver from selenium.webdriver.common.keys import Keys ...

  9. 杨老师课堂_Java核心技术下之控制台模拟文件管理器案例

    背景需求介绍: 编写一个模拟文件管理器的程序,实现控制台对文件和文件夹的管理操作. 要求在此程序中: 当用户输入指令 1 时,代表"指定关键字检索文件",此时需要用户输入检索的目录 ...

最新文章

  1. 撰写科技奖励申报材料的“思路”“要点”与“技巧”
  2. java反射机制_java反射机制的讲解
  3. java 整数溢出检测,Java如何处理整数下溢和溢出以及如何检查它?
  4. Js中 关于top、clientTop、scrollTop、offsetTop的用法
  5. GO编程程序员修炼秘籍:十本经典书单
  6. HexorBase 登陆、执行语句、数据库帐号密码破解
  7. Python学习笔记——Python的下载与安装
  8. 三次样条插值算法C++实现
  9. android 图片查看器
  10. 自定义分区partitioner实现数据分区存储
  11. 华为网关服务器型号,02311CWM CN21ITGC SP212 I350-T4 华为服务器四口千兆网卡
  12. 《软件工程导论》考试复习题集锦
  13. python中国大学慕课网_高级语言程序设计(Python)中国大学慕课搜题网站
  14. 【Mysql】认识Mysql重要架构(一)
  15. 腾讯游戏扫码登录源码
  16. 安装 ubuntu16.04 ROS2 超过5分钟你打我 后带 测试talker listener demo
  17. BUUCTF warmup_csaw_2016 writeup
  18. 2015最新P2P行业监管细则解读
  19. 产品防伪码查询系统_学历学籍查询系统_证书查询系统_录取成绩查询系统_工资查询系统_信息查询系统
  20. 不同局域网内的主机互访(海蜘蛛实现linux虚拟机与windows虚拟机不同网段间互访)(海蜘蛛的安装与使用)超详细

热门文章

  1. python动态生成数据库表_Python-Flask:动态创建表的示例详解
  2. HID接口设备-简介
  3. Node.js~ioredis处理耗时请求时连接数瀑增
  4. 开源 免费 java CMS - FreeCMS1.9 移动APP管理 执行配置
  5. 《网络安全原理与实践》一2.1 安全区介绍
  6. 合法练习黑客技术?这15个网站也许可以帮到你
  7. 结构体之位域全面分析
  8. CodeForces - 1486E Paired Payment(分层图最短路)
  9. 2019ICPC(徐州) - Loli, Yen-Jen, and a cool problem(广义后缀自动机)
  10. CodeForces - 1090D Similar Arrays(构造+思维)