27 map和set封装

map和set可以采用两套红黑树实现,也可以用同一个红黑树,就需要对前面的结构进行修改
迭代器的好处是可以方便遍历,是数据结构的底层实现与用户透明。如果想要给红黑树增加迭代器,需要考虑以前问题:

  • begin()和end()

stl明确规定,begin和edn代表的是一段前闭后开的区间,而对红黑树进行中序遍历后,可以得到一个有序序列,因此:begin可以放在红黑树中最小节点(即最左侧节点)的位置,end放在最大结点(最右侧节点)的下一个位置,关键是最大节点的下一个位置在哪?如果给成nullptr,end的–操作要能找到最后一个元素的位置,最好的方式是将end放在头节点的位置

在这里插入图片描述

迭代器结构

在红黑树中提供一个迭代器类,提供基本的*,->,++,–等运算符重载。变量时一个节点指针,用节点来初始化迭代器
在这里插入图片描述
加入上面构造的好处
固定的普通迭代器初始化这个迭代器类,参数模板都用T,如果T传入的是普通迭代器,就是拷贝构造,如果是cosnt迭代器,就支持用普通迭代器隐式转换常量迭代器,保证set的key不能修改

++和–

迭代器的++是按中序遍历的顺序找到下一个元素,遵循中序的原则。规则是:当前节点判断它的右节点有没有节点,如果有,就找到右子树中最小的,也就是最左的结点。如果是空,就看它是双亲的左还是右,如果是左,按中序原则就到它的父亲位置,如果是右,说明这颗子树遍历完毕,向上重复直到找到cur是par的左节点的时候,如果到end的位置就结束

self& operator++()
{//1.右不为空,找右树最左节点if (_node->_right != nullptr){node* subleft = _node->_right;while (subleft->_left){subleft = subleft->_left;}_node = subleft;}else{//2.右为空,沿着路径找孩子是父亲左的祖先node* cur = _node;node* parent = cur->_parent;while (parent && parent->_right == cur){cur = parent;parent = parent->_parent;}_node = parent;}return *this;
}

–和++的操作是镜像的,左为空,找是右子树的结点

self& operator--()
{//1.左不为空,找左树最右节点if (_node->_left != nullptr){node* subright = _node->_left;while (subright->_right){subright = subright->_right;}_node = subright;}else{//2.左为空,沿着路径找孩子是父亲右的祖先node* cur = _node;node* parent = cur->_parent;while (parent && parent->_left == cur){cur = parent;parent = parent->_parent;}_node = parent;}return *this;
}

enum color
{RED,BLACK
};
template <class T>
struct TreeNode
{struct TreeNode<T>* _parent;struct TreeNode<T>* _left;struct TreeNode<T>* _right;T _data;color _col;TreeNode(T data):_parent(nullptr), _left(nullptr), _right(nullptr), _data(data), _col(RED){}
};
template <class T, class Ref, class Ptr>
struct __RBTreeIterator
{typedef TreeNode<T> node;typedef __RBTreeIterator<T, Ref, Ptr> self;node* _node;__RBTreeIterator(node* cur):_node(cur){}//普通迭代器,当迭代器是普通时是拷贝构造,是const迭代器时支持普通转换const__RBTreeIterator(const __RBTreeIterator<T, T&, T*>& it){_node = it._node;}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const self& x){return x._node != _node;}self& operator++(){//1.右不为空,找右树最左节点if (_node->_right != nullptr){node* subleft = _node->_right;while (subleft->_left){subleft = subleft->_left;}_node = subleft;}else{//2.右为空,沿着路径找孩子是父亲左的祖先node* cur = _node;node* parent = cur->_parent;while (parent && parent->_right == cur){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}self& operator--(){//1.左不为空,找左树最右节点if (_node->_left != nullptr){node* subright = _node->_left;while (subright->_right){subright = subright->_right;}_node = subright;}else{//2.左为空,沿着路径找孩子是父亲右的祖先node* cur = _node;node* parent = cur->_parent;while (parent && parent->_left == cur){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}};

begin和end

红黑树提供begin和end功能,begin返回最左的元素,也就是最小的值。end返回空迭代器

typedef __RBTreeIterator<T, T&, T*> iterator;
typedef __RBTreeIterator<T, const T&, const T*> const_iterator;iterator begin()
{//中序第一个,最左节点node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return iterator(cur);
}const_iterator begin() const
{//中序第一个,最左节点node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return const_iterator(cur);
}iterator end()
{return iterator(nullptr);
}const_iterator end() const
{return const_iterator(nullptr);
}

insert返回值

insert的返回值返回pair,第一个参数是iterator,第二个参数插入成功或失败,如果插入成功,就返回插入位置,插入失败,就返回已经存在的数据位置

insert比较

set是比较key的值,而map传入的值是pair,pair本身的比较规则是先比较first,first相等比较second。而我们想要的是只比较first,所以要统一两个的比较方法
在这里插入图片描述
在这里插入图片描述

模板里传入一个KeyOfT,是一个仿函数,根据不同的T的类型,取到set和map用于比较的元素

std::pair<iterator, bool> insert(const T& data)
{KeyOfT kot;if (_root == nullptr){_root = new node(data);_root->_col = BLACK;return std::make_pair(iterator(_root), true);}node* parent = nullptr;node* cur = _root;while (cur){parent = cur;if (kot(data) < kot(cur->_data)){cur = cur->_left;}else if (kot(data) > kot(cur->_data)){cur = cur->_right;}else{return std::make_pair(iterator(cur), false);;}}//插入cur = new node(data);node* newnode = cur;cur->_parent = parent;if (data < parent->_data){parent->_left = cur;}else{parent->_right = cur;}//父节点是红色调整while (parent && parent->_col == RED){node* gdparent = parent->_parent;node* uncle;if (gdparent->_left == parent){uncle = gdparent->_right;//第一种情况 叔叔节点是红色,变色if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;gdparent->_col = RED;}//第二种情况 分左右//     g//  par  un// curelse{if (cur == parent->_left){RotateRight(gdparent);parent->_col = BLACK;gdparent->_col = RED;}//     g//  par  un//    curelse{RotateLeft(parent);RotateRight(gdparent);cur->_col = BLACK;parent->_col = RED;gdparent->_col = RED;}break;}}else{uncle = gdparent->_left;if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;gdparent->_col = RED;}else{if (cur == parent->_right){RotateLeft(gdparent);parent->_col = BLACK;gdparent->_col = RED;}//     g//  par  un//    curelse{RotateRight(parent);RotateLeft(gdparent);cur->_col = BLACK;//parent->_col = RED;gdparent->_col = RED;}break;}}cur = gdparent;parent = cur->_parent;}//根必须是黑色_root->_col = BLACK;return std::make_pair(iterator(newnode), true);;
}

set和map的仿函数如下:

struct SetOfK
{const K& operator()(const K& key){return key;}
};
struct SetOfV
{const K& operator()(const std::pair<const K, V>& kv){return kv.first;}
};

#pragma once
#include <iostream>
#include <assert.h>
#include <queue>template <class K, class T, class KeyOfT>
class RBTree
{typedef TreeNode<T> node;
public:typedef __RBTreeIterator<T, T&, T*> iterator;typedef __RBTreeIterator<T, const T&, const T*> const_iterator;iterator begin(){//中序第一个,最左节点node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return iterator(cur);}const_iterator begin() const{//中序第一个,最左节点node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return const_iterator(cur);}iterator end(){return iterator(nullptr);}const_iterator end() const{return const_iterator(nullptr);}node* find(const K& key){KeyOfT kot;if (_root == nullptr){return nullptr;}node* cur = _root;while (cur){if (kot(key) < kot(cur->_data)){cur = cur->_left;}else if (kot(key) > kot(cur->_data)){cur = cur->_right;}else{return cur;}}return nullptr;}std::pair<iterator, bool> insert(const T& data){KeyOfT kot;if (_root == nullptr){_root = new node(data);_root->_col = BLACK;return std::make_pair(iterator(_root), true);}node* parent = nullptr;node* cur = _root;while (cur){parent = cur;if (kot(data) < kot(cur->_data)){cur = cur->_left;}else if (kot(data) > kot(cur->_data)){cur = cur->_right;}else{return std::make_pair(iterator(cur), false);;}}//插入cur = new node(data);node* newnode = cur;cur->_parent = parent;if (data < parent->_data){parent->_left = cur;}else{parent->_right = cur;}//父节点是红色调整while (parent && parent->_col == RED){node* gdparent = parent->_parent;node* uncle;if (gdparent->_left == parent){uncle = gdparent->_right;//第一种情况 叔叔节点是红色,变色if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;gdparent->_col = RED;}//第二种情况 分左右//     g//  par  un// curelse{if (cur == parent->_left){RotateRight(gdparent);parent->_col = BLACK;gdparent->_col = RED;}//     g//  par  un//    curelse{RotateLeft(parent);RotateRight(gdparent);cur->_col = BLACK;parent->_col = RED;gdparent->_col = RED;}break;}}else{uncle = gdparent->_left;if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;gdparent->_col = RED;}else{if (cur == parent->_right){RotateLeft(gdparent);parent->_col = BLACK;gdparent->_col = RED;}//     g//  par  un//    curelse{RotateRight(parent);RotateLeft(gdparent);cur->_col = BLACK;//parent->_col = RED;gdparent->_col = RED;}break;}}cur = gdparent;parent = cur->_parent;}//根必须是黑色_root->_col = BLACK;return std::make_pair(iterator(newnode), true);;}void RotateLeft(node* parent){node* sub = parent->_right;node* subl = sub->_left;sub->_left = parent;parent->_right = subl;//父节点修改node* Pparent = parent->_parent;parent->_parent = sub;//有子节点改变指向if (subl){subl->_parent = parent;}if (parent == _root){_root = sub;}else{//原parent在父节点的左右if (Pparent->_left == parent){Pparent->_left = sub;}else{Pparent->_right = sub;}}sub->_parent = Pparent;}void RotateRight(node* parent){node* sub = parent->_left;node* subr = sub->_right;sub->_right = parent;parent->_left = subr;if (subr){subr->_parent = parent;}node* Pparent = parent->_parent;parent->_parent = sub;if (parent == _root){_root = sub;}else{if (Pparent->_left == parent){Pparent->_left = sub;}else{Pparent->_right = sub;}}sub->_parent = Pparent;}bool IsBalance(){if (_root->_col == RED){std::cout << "根节点是红色" << std::endl;return false;}int refVal = 0;  //记录最左路径黑色节点的数量,用来参考node* cur = _root;while (cur){if (cur->_col == BLACK){refVal++;}cur = cur->_left;}return _IsBalance(_root, 0, refVal);}bool _IsBalance(node* cur, int blackNum, int refVal){if (cur == nullptr){//判断每条路径黑色节点数量正常if (blackNum != refVal){std::cout << "黑色节点数量不相等" << std::endl;return false;}return true;}if (cur->_col == BLACK){blackNum++;}if (cur->_col == RED && cur->_parent->_col == RED){std::cout << cur->_kv.first << " 连续红色节点" << std::endl;}return _IsBalance(cur->_left, blackNum, refVal)&& _IsBalance(cur->_right, blackNum, refVal);}void layer(){if (_root == nullptr){return;}std::queue<node*> q;q.push(_root);int lay = 1;while (!q.empty()){std::cout << "第" << lay << "层: ";int num = q.size();while (num--){node* cur = q.front();q.pop();std::cout << cur->_kv.first << " 颜色:" << cur->_col << "  ";if (cur->_left != nullptr){q.push(cur->_left);}if (cur->_right != nullptr){q.push(cur->_right);}}lay++;std::cout << std::endl;}std::cout << std::endl;}void inorder(){_inorder(_root);std::cout << std::endl;}void _inorder(node* root){if (root == nullptr){return;}_inorder(root->_left);std::cout << root->_kv.first << " ";_inorder(root->_right);}int size(){return _size(_root);}int _size(node* node){if (node == nullptr){return 0;}return _size(node->_left) + _size(node->_right) + 1;}int TreeHeight(){return _TreeHeight(_root);}int _TreeHeight(node* node){if (node == nullptr){return 0;}int lhight = _TreeHeight(node->_left);int rhight = _TreeHeight(node->_right);return lhight > rhight ? lhight + 1 : rhight + 1;}private:node* _root = nullptr;
};

set

在这里插入图片描述
两个参数都用K初始化,传入仿函数
在这里插入图片描述
普通迭代器也用const,保证key不被修改,typename用来区分是一个类还是变量类型

#pragma once
#include "RBTree.h"template <class K>
class set
{struct SetOfK{const K& operator()(const K& key){return key;}};public://typename 区分是类还是变量typedef typename RBTree<K, K, SetOfK>::const_iterator iterator;typedef typename RBTree<K, K, SetOfK>::const_iterator const_iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}std::pair<iterator, bool> insert(const K& key){return _t.insert(key);}private:RBTree<K, K, SetOfK> _t;
};

map

在这里插入图片描述
map第一个是K,用来erase,find等函数接口是key,它的数据类型是pair

V& operator[](const K& key)
{std::pair<iterator, bool> ret = _t.insert(make_pair(key, V()));return ret.first->second;
}

map提供[]操作,返回key位置的值,并可以对自身修改

#pragma once
#include <iostream>
#include "RBTree.h"template <class K, class V>
class map
{struct SetOfV{const K& operator()(const std::pair<const K, V>& kv){return kv.first;}};public:typedef typename RBTree<K, std::pair<const K, V>, SetOfV>::iterator iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}std::pair<iterator, bool> insert(const std::pair<const K, V> kv){return _t.insert(kv);}V& operator[](const K& key){std::pair<iterator, bool> ret = _t.insert(make_pair(key, V()));return ret.first->second;}private:RBTree<K, std::pair<const K, V>, SetOfV> _t;
};

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

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

相关文章

【PB案例学习笔记】-23创建一个窗口菜单

写在前面 这是PB案例学习笔记系列文章的第23篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gite…

前端根据环境变量配置网页的title和favicon

前端根据环境变量配置网页的title和favicon 前言流程步骤一、设置environment文件二、在入口文件中配置三、删除index.html中的title和 icon link四、使用对应的打包命令进行部署 注意事项一、angular中&#xff0c;需要在angular.json添加favicon.ico额外的构建 前言 有些项目…

[C++][数据结构][图][中][图的遍历][最小生成树]详细讲解

目录 1.图的遍历1.广度优先遍历2.深度优先遍历 2.最小生成树1.Kruskal算法2.Prim算法 1.图的遍历 给定一个图G和其中任意一个顶点 v 0 v_0 v0​&#xff0c;从 v 0 v_0 v0​出发&#xff0c;沿着图中各边访问图中的所有顶点&#xff0c;且每个顶 点仅被遍历一次 “遍历”&…

《Windows API每日一练》5.1 键盘基础

本节我们讲述关于键盘的一些基础知识。当我们按下一个键盘按键时&#xff0c;会产生一个键盘按键消息。这一点你能确定吗&#xff1f;假如是一个菜单快捷键消息&#xff0c;或者是一个子窗口控件消息呢&#xff1f;这就超出了本节讨论的范围&#xff0c;我们将在菜单和子窗口控…

Jenkins+K8s实现持续集成(二)

部署前呢&#xff0c;要先把jenkins搭建好。 同时呢已经有了k8s的环境。 基于以上两步已经有了的情况&#xff0c;继续要实现jenkinsk8s持续集成&#xff0c;需要先准备四个文件&#xff1a; Dockerfile首先要准备好一个Dockerfile文件&#xff0c;用于构建Docker镜像的文本…

安装cuda、cudnn、Pytorch(用cuda和cudnn加速计算)

写在前面 最近几个月都在忙着毕业的事&#xff0c;好一阵子没写代码了。今天准备跑个demo&#xff0c;发现报错 AssertionError: Torch not compiled with CUDA enabled 不知道啥情况&#xff0c;因为之前有cuda环境&#xff0c;能用gpu加速&#xff0c;看这个报错信息应该是P…

React常用方法汇总【更新中】

文章目录 前言创建项目启动命令列表渲染父子组件传值useEffect 异步函数使用方法useEffect 异步函数清除方法控制组件显示隐藏axios 安装使用 前言 运行 react 需要先安装 node.js&#xff0c;具体安装步骤可以参考这篇文章 https://blog.csdn.net/weixin_43721000/article/de…

Vue65-vue-resource:ajax请求

vue-resource是vue的插件库&#xff0c;用vue.use(xxxx)使用插件。 1、安装 2、引入和使用 这个库&#xff0c;维护的频率不高了。还是建议使用&#xff1a;axios&#xff0c;vue-resource只是了解即可。

MySQL8,Navicat能登陆成功,密码却忘记了

执行成功的图&#xff1a; 以下为步骤&#xff1a;本文一共8个简单步骤。 环境&#xff1a;mysql8、window10、navicat11 1、打开本地电脑window10的命令窗&#xff08;俗称黑窗口&#xff09;&#xff0c;windowR 2、输入regegit&#xff0c;回车&#xff0c;打开注册表 3、…

技术差异,应用场景;虚拟机可以当作云服务器吗

虚拟机和云服务器是现在市面上常见的两种计算资源提供方式&#xff0c;很多人把这两者看成可以相互转换或者替代的物品&#xff0c;实则不然&#xff0c;这两种资源提供方式有许多相似之处&#xff0c;但是也有不少区别&#xff0c;一篇文章教你识别两者的技术差异&#xff0c;…

【全文档】软件项目经理需要掌握的文档有哪些?

软件项目经理在项目管理过程中需要编写多种文档&#xff0c;以下是常见的十五个文档&#xff1a; 项目计划&#xff1a; 详细描述了项目的范围、时间、成本、资源、沟通计划等关键信息&#xff0c;是项目管理的核心文档。 需求文档&#xff1a; 记录了项目的业务需求、功能需求…

深入理解预处理

1.预定义符号 C语言设置了⼀些预定义符号&#xff0c;可以直接使用&#xff0c;预定义符号也是在预处理期间处理的。 __FILE__ //进⾏编译的源⽂件 __LINE__ //⽂件当前的⾏号 __DATE__ //⽂件被编译的⽇期 __TIME__ //⽂件被编译的时间 __STDC__ //如果编译器遵循ANSI C&…

3. ceph-mimic版本部署

ceph-mimic版本部署 一、ceph-mimic版本部署1、环境规划2、系统基础环境准备2.1 关闭防火墙、SELinux2.2 确保所有主机时间同步2.3 所有主机ssh免密2.4 添加所有主机解析 3、配置ceph软件仓库4、安装ceph-deploy工具5、ceph集群初始化6、所有ceph集群节点安装相关软件7、客户端…

LeetCode322.零钱兑换(一)

LeetCode刷题记录 文章目录 &#x1f4dc;题目描述&#x1f4a1;解题思路⌨C代码 &#x1f4dc;题目描述 给你一个整数数组 coins &#xff0c;表示不同面额的硬币&#xff1b;以及一个整数 amount &#xff0c;表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。…

项目监督与控制

1.什么是项目过程度量&#xff1f;其方法有哪些&#xff1f; 项目过程度量是一种对项目执行过程中的活动和性能进行量化测量的方法。它涉及到收集、分析和解释项目数据&#xff0c;以便更好地理解项目的进度、质量和效率。过程度量的目的是提供关于项目健康状况的客观信息&…

代码随想录第28天|回溯算法

491. 非递减子序列 思路: 不可以排序, 否则会改变元素的顺序对收获的结果有要求, num.size() > 2, 且 num[i - 1] < num[i]需要进行去重, 不能使用排序后的方法去重每一层可用 unordered_set 去重组合问题, for 遍历需要标记起始位置 bug: 一定要先判断元素是否重复, …

使用CAPL创建系统变量之sysDefineNamespace

目录 0 前言 1 使用CAPL创建系统变量 0 前言 最近在项目中发现可以通过CAPL来创建系统变量&#xff0c;这样方法在一定程度上提高了代码的统一性和测试的便利性。想要加入HIL自动化测试群的小伙伴欢迎评论区留言或私信&#xff0c;让我们一起进步&#xff01; 1 使用CAPL创建…

染发膏粪大肠菌群检测 化妆品毒理学检测 功效测试

染发膏中粪大肠菌群的检测 染发膏中的粪大肠菌群检测是为了确保产品的卫生安全&#xff0c;因为粪大肠菌群通常存在于动物的肠道中&#xff0c;它们的存在可能表明产品受到了外部环境的污染。根据国家标准GB/T 7918.3-1987&#xff0c;对化妆品中粪大肠菌群的检测方法进行了规范…

【html】爱心跳动动画:CSS魔法背后的故事

效果展示&#xff1a; 代码介绍&#xff1a; 爱心跳动动画&#xff1a;CSS魔法背后的故事 在前端开发中&#xff0c;CSS不仅仅是一种用于控制网页样式的工具&#xff0c;它也是一种表达创意和想象力的艺术手段。今天&#xff0c;我要为大家介绍一段使用CSS实现的爱心跳动动画…

【bug】配置SpringCloudAlibaba AI的maven依赖问题

问题描述 尝鲜alibaba的ai模块&#xff0c;maven依赖一直报找不到包&#xff0c;报错如下 Unresolved dependency: org.springframework.ai:spring-ai-core:jar:0.8.1原因分析&#xff1a; 由于是按照官方文档配置的&#xff0c;所以检查了很多遍maven配置&#xff0c;加上去…