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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/665440.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习_14_多分类及多标签分类算法

文章目录 1 单标签二分类问题1.1 单标签二分类算法原理1.2 Logistic算法原理 2 单标签多分类问题2.1 单标签多分类算法原理2.2 Softmax算法原理2.3 ovo2.4 ovr2.5 OvO和OvR的区别2.6 Error Correcting 3 多标签算法问题3.1 Problem Transformation Methods3.1.1 Binary Relevan…

kubernetes基本概念和操作

基本概念和操作 1.Namespace1.1概述1.2应用示例 2.Pod2.1概述2.2语法及应用示例 3.Label3.1概述3.2语法及应用示例 4.Deployment4.1概述4.2语法及应用示例 5.Service5.1概述5.2语法及应用示例5.2.1创建集群内部可访问的Service5.2.2创建集群外部可访问的Service5.2.3删除服务5.…

Deepin如何开启与配置SSH实现无公网ip远程连接

文章目录 前言1. 开启SSH服务2. Deppin安装Cpolar3. 配置ssh公网地址4. 公网远程SSH连接5. 固定连接SSH公网地址6. SSH固定地址连接测试 前言 Deepin操作系统是一个基于Debian的Linux操作系统&#xff0c;专注于使用者对日常办公、学习、生活和娱乐的操作体验的极致&#xff0…

【JavaScript】前端攻击

前端攻击 1. CSRF的基本概念、缩写、全称 CSRF&#xff08;Cross-site request forgery&#xff09;&#xff1a;跨站请求伪造。 从上图可以看出&#xff0c;要完成一次CSRF攻击&#xff0c;受害者必须满足两个必要的条件&#xff1a; 登录受信任网站A&#xff0c;并在本地生…

09.领域驱动设计:深入学习6本经典推荐书籍

目录 前言 1、《领域驱动设计&#xff1a;软件核心复杂性应对之道》 1.作者简介 2.内容简介 3.推荐理由 4.豆瓣链接 ​编辑 2、《实现领域驱动设计》 1.作者简介 2.内容简介 3.推荐理由 4.豆瓣链接 ​编辑 3、《领域驱动设计精粹》 1.作者简介 2.内容简介 3.推…

深度学习和大数据技术的进步在自然语言处理领域的应用

文章目录 每日一句正能量前言一、深度学习在NLP中的应用二、大数据技术在NLP中的应用三、深度学习和大数据技术的影响四、应用场景后记 每日一句正能量 努力学习&#xff0c;勤奋工作&#xff0c;让青春更加光彩。 前言 随着深度学习和大数据技术的迅猛发展&#xff0c;自然语…

Qt加载网页崩溃 ASSERT:“m_adapterClient“ in file ...

1、软件启动后加载网页无异常&#xff0c;点击按钮&#xff0c;加载新网页时崩溃 崩溃代码&#xff1a; QWebEngineView *createWindow(QWebEnginePage::WebWindowType type) { Q_UNUSED(type); return this; } 2、原因 Qt只是调用谷歌的浏览器引擎&#xff…

iPad“粘贴自”字样不消失解决办法

iPad“粘贴自”字样不消失解决办法 好无语&#xff0c;写论文主要就靠iPad看资料&#xff0c;复制粘帖的时候卡死搞得我无敌焦躁&#xff0c;问了&#x1f34e;支持的客服才解决&#xff0c;方法如下&#xff1a;1.音量上键按一下 2.音量下键按一下 3.一直按开关机键直到出现苹…

031 递归

何为递归 示例 public static void main(String[] args) {System.out.println(fn(5)); }static int fn(int a){return a 1 ? 1 : a * fn(a - 1); }

浅析Redis③:命令处理之数据返回Client(下)

写在前面 Redis作为我们日常工作中最常使用的缓存数据库&#xff0c;其重要性不言而喻&#xff0c;作为普通开发者&#xff0c;我们在日常开发中使用Redis&#xff0c;主要聚焦于Redis的基层数据结构的命令使用&#xff0c;很少会有人对Redis的内部实现机制进行了解&#xff0c…

华为鸿蒙系统第三方桌面手机管家版本号12.0.1.330

起因 放假在家&#xff0c;准备把自己的旧手机给家里老人看小视频&#xff0c;但是老人不懂智能手机的复杂操作&#xff0c;就想换成《极简桌面》这个软件&#xff0c;在“设置->默认应用->桌面”&#xff0c;但是华为为了“保护用户的隐私以及合法权益”禁用了第三方启…

202417读书笔记|《画•大师 阿尔丰斯•穆夏》——在明亮大胆的色调中熠熠生辉

202417读书笔记|《画•大师 阿尔丰斯•穆夏》——在明亮大胆的色调中熠熠生辉 这是读完的第二本或者第三本穆夏的画集。很赞&#xff0c;他的绘画风格&#xff0c;构图&#xff0c;元素&#xff0c;取大自然的月桂树&#xff0c;常青藤&#xff0c;&#x1f319;的不同形态&…

链表与二叉树-数据结构

链表与二叉树-数据结构 创建叶子node节点建立二叉树三元组&#xff1a;只考虑稀疏矩阵中非0的元素&#xff0c;并且存储到一个类&#xff08;三元组&#xff09;的数组中。 创建叶子node节点 class Node{int no;Node next;public Node(int no){this.nono;} } public class Lb…

Fink CDC数据同步(三)Flink集成Hive

1 目的 持久化元数据 Flink利用Hive的MetaStore作为持久化的Catalog&#xff0c;我们可通过HiveCatalog将不同会话中的 Flink元数据存储到Hive Metastore 中。 利用 Flink 来读写 Hive 的表 Flink打通了与Hive的集成&#xff0c;如同使用SparkSQL或者Impala操作Hive中的数据…

流畅的Python(七)-函数装饰器和闭包

一、核心要义 主要解释函数装饰器的工作原理&#xff0c;包括最简单的注册装饰器和较复杂的参数化装饰器。同时&#xff0c;因为装饰器的实现依赖于闭包&#xff0c;因此会首先介绍闭包存在的原因和工作原理。 二、代码示例 1、变量作用域规则 #!/usr/bin/env python # -*-…

前端学习笔记 | HTML5+CSS3静态网页制作的技巧(持续更新)

注&#xff1a;本文的css样式均使用less写法 1、字体居中 &#xff08;1&#xff09;先text-align:center;行内元素水平居中 &#xff08;2&#xff09;再line-heigh:(盒子高度);行内元素垂直居中 text-align: center;line-height: ( 30 / vw ); 2、盒子居中 情景1&#…

【课程作业_01】国科大2023模式识别与机器学习实践作业

国科大2023模式识别与机器学习实践作业 作业内容 从四类方法中选三类方法&#xff0c;从选定的每类方法中 &#xff0c;各选一种具体的方法&#xff0c;从给定的数据集中选一 个数据集&#xff08;MNIST&#xff0c;CIFAR-10&#xff0c;电信用户流失数据集 &#xff09;对这…

【大数据技术攻关专题】「Apache-Flink零基础入门」手把手+零基础带你玩转大数据流式处理引擎Flink(基础加强+运行原理)

手把手零基础带你玩转大数据流式处理引擎Flink&#xff08;运行机制原理加深&#xff09; 前提介绍运行Flink应用运行机制Flink的两大核心组件JobManagerTaskManagerTaskSlot Flink分层架构Stateful Stream ProcessingDataStream和DataSetDataStream&#xff08;数据流&#xf…

GPIO中断

1.EXTI简介 EXTI是External Interrupt的缩写&#xff0c;指外部中断。在嵌入式系统中&#xff0c;外部中断是一种用于处理外部事件的机制。当外部事件发生时&#xff08;比如按下按钮、传感器信号变化等&#xff09;&#xff0c;外部中断可以立即打断正在执行的程序&#xff0…

大红喜庆版UI猜灯谜小程序源码/猜字谜微信小程序源码

今天给大家带来一款UI比较喜庆的猜灯谜小程序&#xff0c;大家看演示图的时候当然也是可以看得到那界面是多么的喜庆&#xff0c;而且新的一年也很快就来了,所以种种的界面可能都比较往喜庆方面去变吧。 这款小程序搭建是免服务器和域名的&#xff0c;只需要使用微信开发者工具…