map\set封装

目录

  • 1. set和map的底层结构
    • 1.1 红黑树
    • 1.2 set
    • 1.3 map
  • 2. 模拟实现
    • 2.1 红黑树
    • 2.1 map和set以及仿函数
    • 2.3 迭代器
      • 2.3.1 const迭代器
    • 2.3 set和map封装

1. set和map的底层结构

1.1 红黑树

这两个容器底层都是对红黑树的封装,因此需要先看一下红黑树结构部分的底层源码:

//树结点的定义:
struct __rb_tree_node_base
{typedef __rb_tree_color_type color_type;typedef __rb_tree_node_base* base_ptr;color_type color; base_ptr parent;base_ptr left;base_ptr right;
};
//子类继承
template <class Value>
struct __rb_tree_node : public __rb_tree_node_base
{typedef __rb_tree_node<Value>* link_type;//存储有效数据Value value_field;
};//红黑树部分成员
template <class Key, class Value, class KeyOfValue, class Compare,class Alloc = alloc>
class rb_tree {
protected:typedef __rb_tree_node<Value> rb_tree_node;
public:typedef Key key_type;typedef Value value_type;typedef rb_tree_node* link_type;
protected:size_type node_count; // keeps track of size of treelink_type header;  Compare key_compare;}

可以发现红黑树的结点中存储的元素类型只与第二个模板参数Value有关,对于set而言value是Key,对于map而言value是pair<const K, T>类型。

Key也需要传递,树中有些接口的实现需要用到,比如find等。

1.2 set

set底层结构的部分构成:

template <class Key, class Compare = less<Key>, class Alloc = alloc>
class set {
public:// typedefs:typedef Key key_type;typedef Key value_type;typedef Compare key_compare;typedef Compare value_compare;
private:///红黑树typedef rb_tree<key_type, value_type, identity<value_type>, key_compare, Alloc> rep_type;rep_type t;  // red-black tree representing set}

set是K模型,因此key_type是Key,value_type也是Key,实例化后树中要存储的元素类型也是Key类型。

1.3 map

template <class Key, class T, class Compare = less<Key>, class Alloc = alloc>
class map {
public:
// typedefs:typedef Key key_type;typedef T data_type;typedef T mapped_type;typedef pair<const Key, T> value_type;typedef Compare key_compare;private:typedef rb_tree<key_type, value_type, select1st<value_type>, key_compare, Alloc> rep_type;rep_type t;  // red-black tree representing map
}

对于map而言,key_type是Key,而value_type则是一个pair<const Key, T>类型,因此实例化后树中的结点存储的就是pair类型。

2. 模拟实现

2.1 红黑树

由于map和set用到的是同一个类模板,因此红黑树类模板的设计要支持泛型。

树结点的定义:

template<class T>
struct RBTreeNode {RBTreeNode(const T& data):_data(data), _left(nullptr), _right(nullptr), _parent(nullptr), _col(RED){ }T _data;enum Color _col;RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;
};

对于set而言,T就是key,而对于map而言T为pair<const K, T>类型。

树的定义:

template<class K, class T, class KeyOfT>
//第三个模板参数为仿函数
//作用是取出date中的key来进行比较
//插入元素时要按照key的关键码进行比较选择合适的插入位置
//但是插入的是data,对于map来说data无法按照预期进行比较
//因为data的类型T为pair,需要取出data中的key
//而对于set而言,可以用data比,因为data的类型T就是key
//不过为了统一处理,都通过仿函数来取出key
class RBTree {typedef RBTreeNode<T> Node;
public:typedef Iterator<T> iterator;
public:pair<iterator, bool> insert(const T& data) {if (!_root) {_root = new Node(data);_root->_col = BLACK;return pair<iterator, bool>(_root, true);}Node* cur = _root;Node* parent = nullptr;//定义仿函数对象KeyOfT kot;while (cur) {//取出data中的key进行比较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 pair<iterator, bool>(cur, false);}}cur = new Node(data);pair<iterator, bool> ret(cur, true);if (kot(parent->_data) < kot(data)) {parent->_right = cur;}else {parent->_left = cur;}cur->_parent = parent;//parent为红需要一直往上调整while (parent && parent->_col == RED) {Node* grandpa = parent->_parent;if (grandpa->_left == parent) {Node* uncle = grandpa->_right;//叔叔存在且为红if (uncle && uncle->_col == RED) {//把父亲和叔叔染黑//爷爷染红继续往上调整parent->_col = uncle->_col = BLACK;grandpa->_col = RED;cur = grandpa;parent = cur->_parent;}//叔叔不存在或者存在且为黑else {//单纯的一边高(直线)单旋if (cur == parent->_left) {rotateRight(grandpa);parent->_col = BLACK;cur->_col = grandpa->_col = RED;}//折线情况需要双旋else {rotateLeft(parent);rotateRight(grandpa);cur->_col = BLACK;parent->_col = grandpa->_col = RED;}break;}}//同样的逻辑else {Node* uncle = grandpa->_left;if (uncle && uncle->_col == RED) {parent->_col = uncle->_col = BLACK;grandpa->_col = RED;cur = grandpa;parent = cur->_parent;}else {if (parent->_right == cur) {rotateLeft(grandpa);grandpa->_col = cur->_col = RED;parent->_col = BLACK;}else {rotateRight(parent);rotateLeft(grandpa);parent->_col = grandpa->_col = RED;cur->_col = BLACK;}break;}		 }}_root->_col = BLACK;return ret;}	 
private:Node* _root = nullptr;
};

2.1 map和set以及仿函数

set:

template<class K>
class set {struct SetKeyOfT {const K& operator()(const K& key) {return key;}};//显式传递仿函数typedef RBTree<K, const K, SetKeyOfT>	rbt;
private:rbt t;
};

map:

template<class K, class T>
class map {//内部类struct MapKeyOfT {const K& operator()(const pair<const K, T>& key) {return key.first;}};typedef RBTree<K, pair<const K, T>, MapKeyOfT>	rbt;
private:rbt t;
};

仿函数的作用在于统一取出结点中的Key进行比较。

2.3 迭代器

map和set通过迭代器进行遍历时,本质是对这颗树进行中序遍历,得到的结果是有序的,因此迭代器中最重要的功能是++和–的实现逻辑,对于++,本质是中序遍历时的下一个位置,具体可分为两步:

  1. 右子树不为空时,需要去找到右子树的最小结点也就是最左节点。
  2. 右子树为空时,说明当前子树遍历完毕,需要去找到一个根结点(可能是整棵树的根也可能是某颗子树的根),该根结点的左子树是我或者是我的某个祖先结点,因为中序遍历的顺序是:左-根-右
self& operator++() {//右子树不为空,去找右树的最左结点if (_it->_right) {Node* leftMin = _it->_right;while (leftMin && leftMin->_left) {leftMin = leftMin->_left;}_it = leftMin;}//为空//去找孩子是父亲的左的那个根结点else {Node* cur = _it;Node* parent = cur->_parent;while (parent && cur == parent->_right) {cur = cur->_parent;parent = parent->_parent;} _it = parent;}return *this;
}

2.3.1 const迭代器

由于set中的元素不允许修改,所以set的普通和const迭代器都是树中的const迭代器:
在这里插入图片描述
在这里插入图片描述
但是这种设计会出现一个问题:
在这里插入图片描述

会导致类型不匹配,这是因为树的insert返回值pair中的迭代器类型是普通迭代器,而set中的迭代器的类型是const类型,普通迭代器无法构造const迭代器,所以会报错,解决方法为:设计一个支持普通迭代器转化为cons迭代器的构造函数。
在这里插入图片描述
对于实例化const迭代器类型的模板类而言,该函数是构造函数,而对于实例化普通迭代器类型的模板类而言,该函数是拷贝构造,通过该函数即可解决上述问题。

map的普通和const迭代器的设计则与其它容器是类似的,只是不管是普通还是const迭代器都无法对key进行修改,而value是否能修改则取决于迭代器的具体类型。

迭代器整体实现代码:

template <class T, class Ref, class Ptr>
struct Iterator {typedef Iterator<T, Ref, Ptr>			self;typedef Iterator<T, T&, T*>				iterator;typedef Iterator<T, const T&, const T*> const_iterator;typedef RBTreeNode<T> Node;Iterator(const iterator& it) :_it(it._it){  }Iterator(Node* it) :_it(it){  }bool operator!=(const self& s) const {return _it != s._it;}bool operator==(const self& s) const {return !(*this != s);}self& operator++() {if (_it->_right) {Node* leftMin = _it->_right;while (leftMin && leftMin->_left) {leftMin = leftMin->_left;}_it = leftMin;}else {Node* cur = _it;Node* parent = cur->_parent;while (parent && cur == parent->_right) {cur = cur->_parent;parent = parent->_parent;}_it = parent;}return *this;}self& operator--() {if (_it->_left) {Node* leftMax = _it->_left;while (leftMax && leftMax->_right) {leftMax = leftMax->_right;}_it = leftMax;}else {Node* cur = _it;Node* parent = cur->_parent;while (parent && cur == parent->_left) {cur = cur->_parent;parent = parent->_parent;}_it = parent;}return *this;}Ref operator*() {return _it->_data;}Ptr operator->() {return &_it->_data;}Node* _it;
};

2.3 set和map封装

set:

#include "RBTree.h"namespace lzh {template<class K>class set {struct SetKeyOfT {const K& operator()(const K& key) {return key;}};typedef RBTree<K, K, SetKeyOfT>	rbt;public:typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;public:iterator begin() const {return _t.begin();}iterator end() const {return _t.end();}pair<iterator, bool> insert(const K& data) {pair<typename RBTree<K, K, SetKeyOfT>::iterator, bool> p = _t.insert(data);return pair<iterator, bool>(p.first, p.second);}private:rbt _t;};
};

map:

#include "RBTree.h"namespace lzh {template<class K, class T>class map {struct MapKeyOfT {const K& operator()(const pair<const K, T>& key) {return key.first;}};typedef RBTree<K, pair<const K, T>, MapKeyOfT>	rbt;public:typedef  typename RBTree<K, pair<const K, T>, MapKeyOfT>::iterator		 iterator;typedef  typename RBTree<K, pair<const K, T>, MapKeyOfT>::const_iterator const_iterator;public:iterator begin() {return t.begin();}iterator end() {return t.end();}pair<iterator, bool> insert(const pair<const K, T>& data) {return t.insert(data);}T& operator[](const K& k) {return insert({ k, T() }).first->second;}private:rbt t;};
};

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

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

相关文章

4.0 Linux进程前导知识

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 冯.诺依曼体系 CPU&#xff1a;运算器&#xff0c;控制器 输入设备&#xff1a;键盘&#xff0c;麦克风&#xff0c;摄像头&#xff0c;鼠标&#xff0c;网卡&#xff0c;磁盘等。 输出设备&#xff1a;显示器&#xff0…

都快2024年了,别只使用React,需要学习一下Vue,不然没出路了

最近&#xff0c;我的朋友因为不熟悉 Vue.js 而未能通过面试。 她平时工作中大部分时间都在使用React&#xff0c;所以也懒得去了解其他前端框架。 世界上所有的前端框架我们都应该熟悉吗&#xff1f; 不&#xff0c;这是极其不合理的。 但为了生存&#xff0c;朋友还是要学…

2023数字科技生态展,移远通信解锁新成就

11月10日&#xff0c;以“数字科技&#xff0c;焕新启航”为主题的中国电信2023数字科技生态大会暨2023数字科技生态展在广州盛大启幕。作为物联网行业的龙头标杆&#xff0c;同时更与中国电信连续多年维持稳定友好的合作关系&#xff0c;移远通信受邀参加本次展会。 在本次展会…

使用xlwings实现对excel表中指定列隔行求和

需要对上表中的营业额隔行求和&#xff0c;即橙色背景颜色的求和&#xff0c;无背景颜色的求和。 看了大佬的视频&#xff0c;有两种方法&#xff1a; 1.加辅助列 2.使用判断行的奇偶函数&#xff0c;然后在用sumproduct函数 在此&#xff0c;我使用xlwings对excel表中数据…

Java编程--单例模式(饿汉模式/懒汉模式)/阻塞队列

前言 逆水行舟&#xff0c;不进则退&#xff01;&#xff01;&#xff01; 目录 单例模式 饿汉模式&#xff1a; 懒汉模式&#xff1a; 什么是阻塞队列 什么是高内聚 低耦合 阻塞队列的实现 单例模式 单例模式&#xff08;Singleton Pattern&#xff09;是一种常见…

WorldView 1 2 3 4卫星影像

WorldView WorldView卫星是Digitalglobe公司的商业成像卫星系统。它由两颗(WorldView-I和WorldView-II)卫星组成。 WorldView-1 WorldView-1卫星为美国DigitalGlobe公司的高分辨率商用卫星&#xff0c;于2007年9月18日成功发射&#xff0c;可提供0.5m分辨率卫星影像。灵活的…

设计模式之适配器(Adapter)

Adapter Wapper 接口转换器 如果一个类不能直接访问另一个类的时候&#xff0c;中间加一个Adapter转换器就能访问了 常见例子: 电压转接头 java.io jdbc-odbc bridge(不是桥接模式) ASM Transformer java io里面的读文件操作: FileInputStream是字节流读文件&#xff0c;就像…

2023-2024-2 高级语言程序设计-二维数组

7-1 矩阵运算 给定一个nn的方阵&#xff0c;本题要求计算该矩阵除副对角线、最后一列和最后一行以外的所有元素之和。副对角线为从矩阵的右上角至左下角的连线。 输入格式: 输入第一行给出正整数n&#xff08;1<n≤10&#xff09;&#xff1b;随后n行&#xff0c;每行给出…

深入理解Kafka3.6.0的核心概念,搭建与使用

Kafka是最初由Linkedin公司开发&#xff0c;是一个分布式、支持分区的&#xff08;partition&#xff09;、多副本的&#xff08;replica&#xff09;&#xff0c;基于zookeeper协调的分布式消息系统&#xff0c;它的最大的特性就是可以实时的处理大量数据以满足各种需求场景&a…

桌面便签软件用哪个?10款全球好用的便签软件推荐,告别杂论无章!

在如今的快节奏社会中&#xff0c;我们的生活和工作节奏越来越快&#xff0c;每天面对的信息成倍地增长。有时候&#xff0c;我们需要随手记下一些重要的事情&#xff0c;或者是一些突然的灵感&#xff0c;这时候就需要一款好用的桌面便签软件。 桌面便签软件可以帮助我们更好…

LeetCode(13)除自身以外数组的乘积【数组/字符串】【中等】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 238. 除自身以外数组的乘积 1.题目 给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素…

前端NaN解决方案

// 2.3 函数表达式可以传递参数还可以有返回值&#xff0c;使用方法和前面具名函数类似let sum function (x, y) { // 形参xx||0yy||0return x y}let re sum() // 实参console.log(re) // 3 function sum(x 0, y 0) {return x y}console.log(sum()) // 0console.log(s…

HTML+CSS、Vue+less+、HTML+less 组件封装实现二级菜单切换样式跑(含全部代码)

一、HTMLCSS二级菜单 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>Document</title><…

mysql之正则表达式匹配

题目&#xff1a; 今天在牛客网看到一道关于数据库正则表达式匹配的问题&#xff0c;发现自己一点不会做。 正则表达式&#xff1a; 一、正则表达式 MySQL 正则表达式通常是在检索数据库记录的时候&#xff0c;根据指定的匹配模式匹配记录中 符合要求的特殊字符串。MySQL 的…

Flowable串行流程移动活动

在Activiti6和Activiti7中跳转节点都要自己实现&#xff0c;Flowable增加了这个功能。 一&#xff1a;简介 串行流程是指简单的一条线的流程&#xff0c;流程中如果包含会签、排它网关也算串行流程。 节点移动有三种方式&#xff1a; 向前移动foreward。向后移动back。直接跳…

【Python+requests+unittest+excel】实现接口自动化测试框架

一、框架结构&#xff1a; 工程目录 二、Case文件设计 三、基础包 base3.1 封装get/post请求&#xff08;runmethon.py&#xff09; 1 import requests2 import json3 class RunMethod:4 def post_main(self,url,data,headerNone):5 res None6 if header …

微服务nacos实战入门

注册中心 在微服务架构中&#xff0c;注册中心是最核心的基础服务之一 主要涉及到三大角色&#xff1a; 服务提供者 ---生产者 服务消费者 服务发现与注册 它们之间的关系大致如下&#xff1a; 1.各个微服务在启动时&#xff0c;将自己的网络地址等信息注册到注册中心&#x…

【科研新手指南4】ChatGPT的prompt技巧 心得

ChatGPT的prompt心得 写在最前面chatgpt咒语1&#xff08;感觉最好用的竟然是这个&#xff0c;简单方便快捷&#xff0c;不需要多轮对话&#xff09;chatgpt思维链2&#xff08;复杂任务更适用&#xff0c;简单任务把他弄复杂了&#xff09;机理chatgpt完整咒语1&#xff08;感…

Vue基础必备掌握知识点-Vue的指令系统讲解(二)

Vue指令系统继续讲解 v-for 作用:基于数据进行循环&#xff0c;多次渲染整个元素 数据类型:数组.对象.数字。。。 遍历数组语法&#xff1a;v-for"(item,index)" in 数组 item:表示每一项 index:则是表现下标 注意:v-for中的key值&#xff0c;key属性唯一的…