map和set的简易封装(纯代码)

RBTree.h

#pragma once#include<iostream>
#include<vector>
using namespace std;enum colar
{   red,black
};template<class T>//有效参数就一个 
struct RBTreeNode
{RBTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _co(red){}RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;colar _co;};template<class T,class ref,class ptr>
struct _Tree_iterator
{typedef RBTreeNode<T> Node;typedef _Tree_iterator<T,ref,ptr> self;Node* _cur;_Tree_iterator(Node* tmp):_cur(tmp){}self& operator++(){//将当前节点看作父节点再分析if (_cur->_right == nullptr)//向上返回(前提是父的左孩子)如果是右孩子则表明父亲已经遍历过了{Node* parent = _cur->_parent;while (parent && parent->_right == _cur)//parent可能为空{_cur = parent;parent = _cur->_parent;}//指向parent指向的left等于_cur 或者parent为空(遍历结束)_cur = parent;}else//自己就属于父节点,找右子树的最左节点{Node* tmp = _cur->_right;while (tmp->_left)//tmp不可能为空{tmp = tmp->_left;}_cur = tmp;}return *this;}self& operator--()//相较于operator++而言就是 右子树 根 左子树 的遍历方式{if (_cur->_left == nullptr)//表明当前节点遍历完成,向上返回……{Node* parent = _cur->_parent;while (parent&&parent->_left == _cur){_cur = parent;parent = parent->_parent;}_cur = parent;}else{//找左子树的最右节点_cur = _cur->_left;while (_cur->_right){_cur = _cur->_right;}}return *this;}ref operator*(){return _cur->_data;}ptr operator->(){return &_cur->_data;}bool operator!=(const self& tmp){return _cur != tmp._cur;}
};template<class K, class T,class Com_T>
class RBTree
{typedef RBTreeNode<T> Node;public:typedef _Tree_iterator<T,T&,T*> iterator;typedef _Tree_iterator<T,const T&,const T*> const_iterator;iterator begin(){Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return iterator(cur);}iterator end(){return iterator(nullptr);}const_iterator cbegin()const{Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return const_iterator(cur);}const_iterator cend()const {return const_iterator(nullptr);}//pair <iterator, bool> insert(const T& data)//data类型取决于T,而T又取决于map和setpair <Node*, bool> insert(const T& data)//data类型取决于T,而T又取决于map和set{Node* newroot = new Node(data);//默认为红if (_root == nullptr){_root = newroot;_root->_co = black;//设为黑return make_pair(newroot,true);}Node* parent = _root, * cur = _root;//插入数据Com_T com;while (1){parent = cur;if (com(cur->_data) > com(data))//这里data的类型可能是pair(不确定){cur = cur->_left;if (cur == nullptr){parent->_left = newroot;newroot->_parent = parent;break;}}else if (com(cur->_data) < com(data)){cur = cur->_right;if (cur == nullptr){parent->_right = newroot;newroot->_parent = parent;break;}}else{return make_pair(cur, false);//数据相同返回相同数据的迭代器(类似是查找数据)}}//父节点的判断cur = newroot;//当前节点就是新插入的节点while (parent && parent->_co == red)//父亲节点可能不存在{Node* pparent = parent->_parent;//parent为红,不可能是根,一定存在pparentNode* uncle = nullptr;//找叔叔节点if (pparent->_right == parent)uncle = parent->_parent->_left;elseuncle = parent->_parent->_right;if (uncle && uncle->_co == red)//叔叔存在且为红{//变色parent->_co = uncle->_co = black;pparent->_co = red;//祖父节点有可能是根节点//继续向上更新处理cur = pparent;parent = cur->_parent;}else//叔叔节点为空或为黑{//旋转if (pparent->_left == parent && parent->_left == cur){//右单旋RotateR(pparent);parent->_co = black;pparent->_co = red;}else if (pparent->_right == parent && parent->_right == cur){//左单旋RotateL(pparent);parent->_co = black;pparent->_co = red;}else if (pparent->_right == parent && parent->_left == cur){//右左双旋RotateR(parent);RotateL(pparent);cur->_co = black;pparent->_co = red;}else if (pparent->_left == parent && parent->_right == cur){//左右双旋RotateL(parent);RotateR(pparent);cur->_co = black;pparent->_co = red;}break;//旋转之后新的根节点都是黑色}}_root->_co = black;//循环体内很有可能将根节点改为红return make_pair(newroot, true);}void RotateL(Node* parent)//左单旋{Node* cur = parent->_right;Node* curl = cur->_left;Node* pparent = parent->_parent;//提前记录parent->_right = curl;if (curl){curl->_parent = parent;}cur->_left = parent;parent->_parent = cur;//处理pparent与parent的连接if (_root == parent){_root = cur;cur->_parent = nullptr;}else{if (pparent->_left == parent)pparent->_left = cur;elsepparent->_right = cur;cur->_parent = pparent;}}void RotateR(Node* parent)//右单旋{{Node* cur = parent->_left;Node* curr = cur->_right;Node* pparent = parent->_parent;//提前记录parent->_left = curr;if (curr){curr->_parent = parent;}cur->_right = parent;parent->_parent = cur;//处理pparent与parent的连接if (_root == parent){_root = cur;cur->_parent = nullptr;}else{if (pparent->_left == parent)pparent->_left = cur;elsepparent->_right = cur;cur->_parent = pparent;}}}void InOrder(){_InOrder(_root);cout << endl;}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << " ";_InOrder(root->_right);}// 根节点->当前节点这条路径的黑色节点的数量bool Check(Node* cur, int blacknum, int ref_val){if (cur == nullptr){if (blacknum == ref_val)return true;cout << "每条路径的黑色节点个数不同" << endl;return false;}Node* parent = cur->_parent;if (cur->_co == red && parent->_co == red)//向上判断,向下判断的节点可能为空或其它的。return false;if (cur->_co == black)blacknum++;return Check(cur->_left, blacknum, ref_val) && Check(cur->_right, blacknum, ref_val);}bool Is_balance(){if (_root->_co == red)return false;if (_root == nullptr)return true;//不能出现连续红节点//每条路径黑色节点要保证相同int blacknum = 0;//必须传值,相当于是每个节点都有一个变量表示从根到当前的黑节点个数int ref_val = 0;//参考值,求出任意一条路径中黑色节点数目Node* cur = _root;while (cur){if (cur->_co == black)ref_val++;cur = cur->_left;}return Check(_root, blacknum, ref_val);}private:Node* _root=nullptr;
};

Set.h

#include"RBTree.h"template<class  key>
class set
{
public:struct setCom//仿函数{const key& operator()(const key& k){return k;}};//typedef _Tree_iterator<key> iterator;typedef typename RBTree<key, key, setCom>::const_iterator iterator;typedef typename RBTree<key, key, setCom>::const_iterator const_iterator;//对类模版取内嵌类型,加typename是为了告诉编译器这里是类型pair<iterator,bool> insert(const key& k)//此时pair的第一个参数类型是const_iterator{return _s.insert(k);//insert返回pair<Node*,bool>会构造出pair<iterator,bool>}iterator begin()const{return _s.cbegin();}iterator end()const{return _s.cend();}private:RBTree<key, key, setCom> _s;//封装红黑树
};

Map.h

#include"RBTree.h"template<class  key,class val>
class map
{
public:struct mapCom//仿函数{const key& operator()(const pair<key,val>& p){return p.first;}}; //typedef _Tree_iterator<pair<key,val>> iterator;typedef typename RBTree<key, pair<const key, val>, mapCom>::iterator iterator;typedef typename RBTree<key, pair<const key, val>, mapCom>::const_iterator const_iterator;//对类模版取内嵌类型,加typename是为了告诉编译器这里是类型pair<iterator, bool> insert(const pair<key, val>& kv){return _m.insert(kv);}iterator begin(){return _m.begin();}iterator end(){return _m.end();}const_iterator cbegin()const{return _m.cbegin();}const_iterator cend()const{return _m.cend();}val& operator[](const key& k){pair<key, val> tmp(k, val());//val给缺省值,tmp是创建变量pair<iterator,bool> ret = insert(tmp);//返回插入的节点的pairreturn (ret.first)->second;}private: RBTree<key, pair<const key, val>,mapCom> _m;//封装红黑树(参数类型决定着红黑树的数据类型)
};  

test.cpp(测试)

#include"Map.h"
#include"Set.h"
#include<string>void test_set()
{set<int> s;s.insert(4);s.insert(1);s.insert(2);s.insert(3);s.insert(2);s.insert(0);s.insert(10);s.insert(5);set<int>::iterator it = s.begin();//浅拷贝while (it != s.end()){cout << *it << " ";++it;}cout << endl;for (auto e : s){cout << e << " ";}cout << endl;
}void test_map()
{map<string, string> dict;dict.insert(make_pair("sort", "排序"));dict.insert(make_pair("sort", "xx"));dict.insert(make_pair("left", "左边"));dict.insert(make_pair("right", "右边"));map<string, string>::const_iterator it = dict.cbegin();while (it != dict.cend()){cout << it->first << ":" << it->second << endl;++it;}cout << endl;string arr[] = { "㽶", "香蕉","ƻ", "香蕉", "ƻ", "香蕉", "ƻ", "ƻ", "香蕉", "ƻ", "㽶", "ƻ", "㽶" };map<string, int> countMap;for (auto& e : arr){countMap[e]++;}for (auto kv : countMap){cout << kv.first << ":" << kv.second << endl;}cout << endl;
}int main()
{//test_set();test_map();return 0;
}

如有问题欢迎留言!!!

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

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

相关文章

在Broker端进行消息过滤

在Broker端进行消息过滤&#xff0c;可以减少无效消息发送到Consumer&#xff0c;少占用网络带宽从而提高吞吐量。Broker端有三种方式进行消息过滤。 1.消息的Tag和Key 对一个应用来说&#xff0c;尽可能只用一个Topic&#xff0c;不同的消息子类型用Tag来标识&#xff08;每条…

一文浅入Springboot+mybatis-plus+actuator+Prometheus+Grafana+Swagger2.9.2开发运维一体化

Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTFUL风格的Web服务,是非常流行的API表达工具。 Swagger能够自动生成完善的 RESTFUL AP文档,,同时并根据后台代码的修改同步更新,同时提供完整的测试页面来调试API。 Prometheus 是一个开源的服务监控系统和时…

聊聊ThreadLocal(二)

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 大部分面试官喜欢问Thr…

git 指定时间代码统计

指定时间代码统计 用法 13 - 17 号 代码情况 近一周 git log --since2023-11-13 00:00:00 --until2023-11-17 23:00:00 --prettytformat: --numstat | awk { add $1; subs $2; loc $1 - $2 } END { printf "added lines: %s, removed lines: %s,total lines: %s\n&…

系列三、GC垃圾回收【总体概览】

一、GC垃圾回收【总体概览】 JVM进行GC时&#xff0c;并非每次都对上面的三个内存区域&#xff08;新生区、养老区、元空间/永久代&#xff09;一起回收&#xff0c;大部分回收的是新生区里边的垃圾&#xff0c;因此GC按照回收的区域又分为了两种类型&#xff0c;一种是发生在新…

hive数仓-数据的质量管理

版本20231116 要理解数据的质量管理&#xff0c;应具备hive数据仓库的相关知识 文章目录 1.理解什么是数据的质量管理&#xff1a;2.数据质量管理的规划数据质量标准的分类 3.数据质量管理解决方案1.ods层的数据质量校验1&#xff09;首先在hive上建立一个仓库&#xff0c;添加…

R语言——taxize(第二部分)

taxize&#xff08;第二部分&#xff09; 3. taxize 文档中译3.10. classification&#xff08;根据类群ID检索分类阶元层级&#xff09;示例1&#xff1a;传递单个ID值示例2&#xff1a;传递多个ID值示例3&#xff1a;传递单个名称示例4&#xff1a;传递多个名称示例5&#xf…

ubuntu 20通过docker安装onlyoffice,并配置https访问

目录 一、安装docker &#xff08;一&#xff09;更新包列表和安装依赖项 &#xff08;二&#xff09;添加Docker的官方GPG密钥 &#xff08;三&#xff09;添加Docker存储库 &#xff08;四&#xff09;安装Docker &#xff08;五&#xff09;启动Docker服务并设置它随系…

001 opencv addWeighted

目录 一、环境 二、addWeighted函数 三、代码演示 一、环境 本文使用环境为&#xff1a; Windows10Python 3.9.17opencv-python 4.8.0.74 二、addWeighted函数 OpenCV中的cv.addWeighted函数是一个用于图像叠加的函数&#xff0c;它可以将两个具有相同尺寸和类型的图像按…

uniapp生成自定义(分享)图片并保存到相册

需求描述 在一个页面中底部有个保存图片的功能&#xff0c;点击能够保存一张生成的自定义表格图片。 第一眼见到这个需求 自己会出现了两个问题 如何去处理图片中的自定义内容以及样式如何将自定义内容转化成图片 至于保存图片&#xff0c;uniapp有对应的api去实现uni.saveIma…

【部署篇】Docker配置MySQL容器+远程连接

一、前言 上篇文章在部署nestjs时&#xff0c;由于docker访问不了主机的localhost&#xff0c;所以无法连接主机数据库。所以我们只能在docker中额外配置一个数据库&#xff0c;映射到主机上&#xff0c;然后可以通过ip地址访问。 在本篇文章我们会在docker中创建一个mysql&a…

Vue3源码reactive和readonly对象嵌套转换,及实现shallowReadonly

前言 官方文档中对reactive的描述&#xff1a; 响应式转换是“深层”的&#xff1a;它会影响到所有嵌套的属性。一个响应式对象也将深层地解包任何 ref 属性&#xff0c;同时保持响应性。 官方文档中对readonly的描述: 只读代理是深层的&#xff1a;对任何嵌套属性的访问都将是…

web:[BUUCTF 2018]Online Tool

题目 打开页面显示如下&#xff0c;进行代码审计 上述代码主要功能是接收‘host’参数&#xff0c;后使用nmap扫描主机端口 首先检查是否存在HTTP_X_FORWARDED_FOR头&#xff0c;若存在&#xff0c;将值赋值给EMOTE_ADDR,是为了跟踪用户真实的IP地址 后用检查get‘host’是否…

验证k8s中HPA功能及测试

部署 使用yaml部署服务 apiVersion: apps/v1 kind: Deployment metadata:name: php-apachenamespace: tools spec:replicas: 1selector:matchLabels:app: php-apachetemplate:metadata:labels:app: php-apachespec:containers:- name: php-apacheimage: registry.cn-beijing.…

Ubuntu18.04平台下Qt开发程序打包的一些问题总结

目录 前言 一、在Ubuntu18.04开发环境下打包有两种方式 1、利用linuxdeployqt软件进行打包 2、利用编写shell脚本的方式进行打包 二、详细介绍shell脚本打包的方式 1、新建一个空的文件夹 2、准备脚本copylib.sh 3、准备脚本xxxx.sh。 4、给上述两个脚本添加可执行权限…

Java虚拟机运行时数据区结构详解

Java虚拟机运行时数据区结构如图所示 程序计数器 程序计数器&#xff08;Program Counter Register&#xff09;是一块较小的内存空间&#xff0c;它可以看作是当前线程所执行的字节码的行号指示器。 多线程切换时&#xff0c;为了能恢复到正确的执行位置&#xff0c;每条线程…

计算机毕业设计选题推荐-二手交易跳蚤市场微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

通信原理板块——脉冲编码调制(PCM)

微信公众号上线&#xff0c;搜索公众号小灰灰的FPGA,关注可获取相关源码&#xff0c;定期更新有关FPGA的项目以及开源项目源码&#xff0c;包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等 1、脉冲编码调制PCM原理 将模拟信号…

6.2 List和Set接口

1. List接口 List接口继承自Collection接口&#xff0c;List接口实例中允许存储重复的元素&#xff0c;所有的元素以线性方式进行存储。在程序中可以通过索引访问List接口实例中存储的元素。另外&#xff0c;List接口实例中存储的元素是有序的&#xff0c;即元素的存入顺序和取…

搭建成功simulink-stm32硬件在环开发环境

本次实验所使用的软件版本和硬件平台参数如下&#xff1a; Matlab版本: 2021b STM32硬件平台&#xff1a;YF_STM32_Alpha 1R4(参考自STM32 Nucleo F103RB官方开发板) YF_STM32_Alpha开发板 STM32 Nucleo F103RB 开发板 2.1 STM32硬件支持包下载 读者朋友平时使用的是和谐版M…