当前位置: 首页 > news >正文

Map和Set的封装

目录

一、底层原理

二、红黑树的节点

三、仿函数

四、迭代器

4.1、迭代器的定义:

4.2、*:解引用操作

4.3、->:成员访问操作符

4.4、!=、==

4.5、迭代器的++:

4.6、迭代器的--

五、Map

六、Set

七、红黑树源码


一、底层原理

我们要知道:Map和Set底层是用红黑树封装的。

红黑树的底层是:KV结构

RBTree是通过传入的Value的值来判断类型,也就是一棵泛型的RBTree,通过不同的实例化,实现出了Map和Set:

对于map:传key,对于set:传pair

为了让我们的红黑树能够识别set与map我们增加一个模板参数T:

template<class K, class T>
class RBTree

对于T模板参数可能是键值Key,也可能是由Key和Value共同构成的键值对

如果是set容器,那么它传入底层红黑树的模板参数就是Key和Key:

template<class K>
class set
{private:RBTree<K,K> _t;
};

如果是map容器,传入底层红黑树的模板参数就是Key和Key和value的键值对:

class map
{
private:RBTree<K, pair<const K,V>> _t;
};

通过上面,我们可以知道,对于set和map的区别:我们只要通过第二个模板参数就能进行区分

那是不是第一个模板参数就没有意义了呢?

对于insert(const Value&v)来说,需要放入存入的值,确实是这个样子的,插入的值是value,对于set就是key,对于map就是pair。

但是对于find(const Key&key)来说,查找的参数不是value,找的不是pair而是Key,对于map容器来说就不行了。

二、红黑树的节点

set容器:K和T都是键值Key; map容器:K是键值Key,T由Key和Value构成的键值对;

但是底层红黑树并不知道上层容器到底是map还是set,因此红黑树的结点当中直接存储T就行了,如果是set的时候,结点当中存储的是键值Key;如果是map的时候,结点当中存储的就是Key和Value构成的键值对,所以红黑树的结点定义如下,由T类型来决定红黑树存的是key还是pair:

template<class T>//三叉链结构
struct RBTreeNode
{T _data;RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Color _col;RBTreeNode(const T& data):_data(data), _left(nullptr), _right(nullptr), _parent(nullptr), _col(RED){}
};

三、仿函数

插入的时候data的大小如何去进行比较:我们并不知道是什么类型是key,还是pair的比较,而我们刚开始kv结构就直接用kv.first去比较了

对于set是Key,可以比较

对于map是pair,那我们要取其中的first来比较,但是pair的大小并不是直接按照first去进行比较的,而我们只需要按照first去进行比较

由于底层的红黑树不知道传的是map还是set容器,当需要进行两个结点键值的比较时,底层红黑树传入的仿函数来获取键值Key,进行两个结点键值的比较:这个时候我们就需要仿函数了,如果是set那就是用于返回T当中的键值Key,如果是map那就是用于返回pair的first:

仿函数/函数对象也是类,是一个类对象。仿函数要重载operator()

map:

namespace lyl
{template<class K,class V>class map{struct  MapKeyOfT{const K& operator()(const pair<const K, V>& kv){return kv.first;}};public:private:RBTree<K, pair<const K,V>,MapKeyOfT> _t;};

set:

namespace lyl
{template<class K>class set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};private:RBTree<K,K,SetKeyOfT> _t;};

我们有了仿函数时,查找函数就可以用仿函数来替换比较部分

KeyOfT kot;//仿函数对象
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{if (kot(cur->_data)<kot(data)){parent = cur;cur = cur->_right;}else if (kot(cur->_data)>kot(data)){parent = cur;cur = cur->_left;}else{return false;}
}

四、迭代器

4.1、迭代器的定义:

template<class T,class Ref,class Ptr>
struct __RBTreeIterator
{typedef RBTreeNode<T> Node;typedef __RBTreeIterator<T,Ref,Ptr> Self;typedef __RBTreeIterator<T, T&, T*> iterator;Node* _node;__RBTreeIterator(Node*node):_node(node){}//普通迭代器的时候,它是拷贝构造//const迭代器的时候,它是构造,支持用普通迭代器构造const迭代器__RBTreeIterator(const iterator& s):_node(s._node){}
}

4.2、*:解引用操作

返回对应结点数据的引用:

Ref operator*(){return _node->_data;}

4.3、->:成员访问操作符

返回结点数据的引用:

Ptr operator->(){return &_node->_data;}

4.4、!=、==

    bool operator !=(const Self & s) const{return _node != s._node;}bool operator ==(const Self& s) const{return _node == s._node;}

4.5、迭代器的++:

一个结点的正向迭代器进行++操作后,根据红黑树中序(左、根、右)找到当前结点的下一个结点,中序的第一个节点是最左,迭代器的++怎么去找:

如果节点的右子树不为空++就是找右子树的最左节点

如果节点的右子树为空++就是找祖先(孩子是父亲的左的那个祖先)

	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;}

4.6、迭代器的--

对于–,如果是根,–就是左子树,找到左子树最大的那一个(最右节点)

如果节点的左子树不为空,--找左子树最右的节点

如果节点的左子树为空,--找祖先(孩子是父亲的右的祖先)

    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;}

4.7、begin(),end()

begin():返回中序(左、根、右)第一个结点的正向迭代器,即最左节点,返回的是最左节点,直接找最左节点即可

end():返回中序(左、根、右)最后一个结点下一个位置的正向迭代器,这里直接用空指针

template<class K, class T,class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;
public:typedef __RBTreeIterator<T> iterator;iterator begin(){Node* left = _root;while (left && left->_left){left = left->_left;}return iterator(left);}iterator end(){return iterator(nullptr);}
}

五、Map

同样是套用上底层红黑树的接口,不过map的实现有一个很重要的地方,那就是[]的实现

#pragma once
#include "RBTree.h"
namespace lyl
{template<class K,class V>class map{struct MapkeyOfT{const K& operator()(const pair<const K, V>& kv){return kv.first;}};public://typename:没有实例化的模板,区分不了是静态变量还是类型,typename告诉编译器是类型typedef typename RBTree<K, pair<const K, V>, MapkeyOfT>::iterator iterator;typedef typename RBTree<K, pair<const K, V>, MapkeyOfT>::const_iterator const_iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}const_iterator begin() const{return _t.begin();}const_iterator end() const{return _t.end();}pair<iterator,bool> insert(const pair<const K, V>& kv){return _t.Insert(kv);}V& operator[](const K& key){pair<iterator, bool> ret = insert(make_pair(key, V()));return ret.first->second;}private:RBTree<K, pair<const K, V>, MapkeyOfT> _t;};void test_map(){int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };map<int, int> m;for (auto e : a){m.insert(make_pair(e, e));}map<int, int>::iterator it = m.begin();while(it!=m.end()){it->second++;cout << it->first << ":" << it->second << endl;++it;}cout << endl;map<string, int> countMap;string arr[] = { "苹果","西瓜","香蕉","苹果"};for (auto& e : arr){countMap[e]++;}for (auto& kv : countMap){cout << kv.first << ":" << kv.second << endl;}}
}

六、Set

#pragma once
#include"RBTree.h"namespace bit
{template<class K>class set{public:struct SetKeyOfT{const K& operator()(const K& key){return key;}};typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}pair<iterator, bool> insert(const K& key){return _t.Insert(key);}private:RBTree<K, K, SetKeyOfT> _t;};
}

七、红黑树源码

#pragma once#pragma once
#include <iostream>
#include <assert.h>
#include <time.h>
using namespace std;
enum Color
{RED,BLACK,
};
template<class T>
struct RBTreeNode
{T _data;RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Color _col;RBTreeNode(const T& data):_data(data), _left(nullptr), _right(nullptr), _parent(nullptr), _col(RED){}
};template<class T,class Ref,class Ptr>
struct __RBTreeIterator
{typedef RBTreeNode<T> Node;typedef __RBTreeIterator<T,Ref,Ptr> Self;typedef __RBTreeIterator<T, T&, T*> iterator;Node* _node;__RBTreeIterator(Node*node):_node(node){}//普通迭代器的时候,它是拷贝构造//const迭代器的时候,它是构造,支持用普通迭代器构造const迭代器__RBTreeIterator(const iterator& s):_node(s._node){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}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;}bool operator !=(const Self & s) const{return _node != s._node;}bool operator ==(const Self& s) const{return _node == s._node;}
};template<class K, class T,class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;
public:typedef __RBTreeIterator<T,T&,T*> iterator;typedef __RBTreeIterator<T,const T&,const T*> const_iterator;const_iterator begin() const {Node* left = _root;while (left && left->_left){left = left->_left;}return const_iterator(left);}const_iterator end() const {return const_iterator(nullptr);}iterator begin(){Node* left = _root;while (left && left->_left){left = left->_left;}return iterator(left);}iterator end(){return iterator(nullptr);}pair<iterator,bool> Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(iterator(_root),true);}KeyOfT kot;Node* parent = nullptr;Node* cur = _root;while (cur){if (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else{return make_pair(iterator(cur),false);}}cur = new Node(data);Node* newnode = cur;cur->_col = RED;if (kot(parent->_data) < kot(data)){parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}while (parent && parent->_col == RED){Node* grandfater = parent->_parent;if (parent == grandfater->_left){Node* uncle = grandfater->_right;//情况一:u存在且为红if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfater->_col = RED;//向上调整cur = grandfater;parent = cur->_parent;}else{//情况2if (cur == parent->_left){RotateR(grandfater);parent->_col = BLACK;grandfater->_col = RED;}//情况3else{//       g//  p//    c RotateL(parent);RotateR(grandfater);cur->_col = BLACK;grandfater->_col = RED;}break;}}else//parent==grandfater->_right{Node* uncle = grandfater->_left;//情况1:u存在且为红色if (uncle && uncle->_col == RED){uncle->_col = parent->_col = BLACK;grandfater->_col = RED;//向上调整cur = grandfater;parent = cur->_parent;}else{//情况2:u不存在/u存在为黑色//g//    p//        cif (cur == parent->_right){RotateL(grandfater);grandfater->_col = RED;parent->_col = BLACK;}//情况3//     g//         p//      celse{RotateR(parent);RotateL(grandfater);cur->_col = BLACK;grandfater->_col = RED;}break;}}}//根变黑_root->_col = BLACK;return make_pair(iterator(newnode),true);}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* ppNode = parent->_parent;subR->_left = parent;parent->_parent = subR;if (ppNode == nullptr){_root = subR;_root->_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;Node* ppNode = parent->_parent;parent->_parent = subL;subL->_right = parent;if (ppNode == nullptr){_root = subL;_root->_parent = nullptr;}else{if (ppNode->_left == parent){ppNode->_left = subL;}else{ppNode->_right = subL;}subL->_parent = ppNode;}}void InOrder(){_InOrder(_root);}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);}bool Check(Node* root, int blackNum, int ref){if (root == nullptr){//cout << blackNum << endl;if (blackNum != ref){cout << "违反规则:本条路径的黑色结点的数量根最左路径不相等" << endl;return false;}return true;}if (root->_col == RED && root->_parent->_col == RED){cout << "违反规则:出现连续的红色结点" << endl;return false;}if (root->_col == BLACK){++blackNum;}return Check(root->_left, blackNum, ref)&& Check(root->_right, blackNum, ref);}bool IsBalance(){if (_root == nullptr){return true;}if (_root->_col != BLACK){return false;}int ref = 0;Node* left = _root;while (left){if (left->_col == BLACK){++ref;}left = left->_left;}return Check(_root, 0, ref);}
private:Node* _root = nullptr;
};

相关文章:

  • 【algorithm】一个简单的PID工程 base 用于手生时候快速复习 用于设计模式 cpp语法八股 快速复习校验
  • 电脑怎么录屏?打造专业级视频内容!
  • 监测Tomcat项目宕机重启脚本(Linux)
  • uniapp中封装一个svg转base64的组件
  • 算法练习03——滑动窗口
  • 氢气泄漏检测仪使用方法:守护安全,从细节开始
  • C++ 之LeetCode刷题记录(二十七)
  • 微服务框架go-zero集成swagger在线接口文档
  • 科普类(遥操作)——快速索引
  • 比瓴科技入围软件供应链安全赛道!为关键信息基础设施安全建设注入新动力
  • 银行数据仓库体系实践(8)--主数据模型设计
  • 如何手机搜智慧职教答案?3个受欢迎的搜题分享了 #微信#学习方法#笔记
  • 深度学习入门笔记(七)卷积神经网络CNN
  • FreeRTOS任务挂起以及延时部分源码分析
  • 计算机网络第4章(网络层)
  • [ 一起学React系列 -- 8 ] React中的文件上传
  • “Material Design”设计规范在 ComponentOne For WinForm 的全新尝试!
  • 【mysql】环境安装、服务启动、密码设置
  • 【跃迁之路】【735天】程序员高效学习方法论探索系列(实验阶段492-2019.2.25)...
  • Android开发 - 掌握ConstraintLayout(四)创建基本约束
  • bearychat的java client
  • CoolViewPager:即刻刷新,自定义边缘效果颜色,双向自动循环,内置垂直切换效果,想要的都在这里...
  • es6要点
  • EventListener原理
  • Hexo+码云+git快速搭建免费的静态Blog
  • Mocha测试初探
  • nginx 配置多 域名 + 多 https
  • oschina
  • React中的“虫洞”——Context
  • Redis字符串类型内部编码剖析
  • SpiderData 2019年2月16日 DApp数据排行榜
  • 对话:中国为什么有前途/ 写给中国的经济学
  • 多线程事务回滚
  • 如何设计一个微型分布式架构?
  • 它承受着该等级不该有的简单, leetcode 564 寻找最近的回文数
  • 微信开放平台全网发布【失败】的几点排查方法
  • 移动端 h5开发相关内容总结(三)
  • 【干货分享】dos命令大全
  • 不要一棍子打翻所有黑盒模型,其实可以让它们发挥作用 ...
  • 摩拜创始人胡玮炜也彻底离开了,共享单车行业还有未来吗? ...
  • ​你们这样子,耽误我的工作进度怎么办?
  • ​七周四次课(5月9日)iptables filter表案例、iptables nat表应用
  • ​软考-高级-信息系统项目管理师教程 第四版【第23章-组织通用管理-思维导图】​
  • #、%和$符号在OGNL表达式中经常出现
  • #NOIP 2014#Day.2 T3 解方程
  • (多级缓存)缓存同步
  • (二十五)admin-boot项目之集成消息队列Rabbitmq
  • (附源码)springboot太原学院贫困生申请管理系统 毕业设计 101517
  • (附源码)计算机毕业设计SSM疫情居家隔离服务系统
  • (四)图像的%2线性拉伸
  • (太强大了) - Linux 性能监控、测试、优化工具
  • (转)MVC3 类型“System.Web.Mvc.ModelClientValidationRule”同时存在
  • (轉貼) 2008 Altera 亞洲創新大賽 台灣學生成果傲視全球 [照片花絮] (SOC) (News)
  • *(长期更新)软考网络工程师学习笔记——Section 22 无线局域网
  • .NET Core 项目指定SDK版本