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…

Spring和Spring Boot常用注解介绍及使用

Spring和Spring Boot框架通过丰富的注解集简化了Java开发&#xff0c;使得配置更加简洁且易于理解。 下面是一些常用的Spring和Spring Boot注解及其使用方式的简介&#xff1a; 目录 1. Component 2. Service 3. Repository 4. Controller 5. RestController 6. Autowire…

前端根据环境变量配置网页的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…

如何在 MySQL 或 MariaDB 中导入和导出数据库

作者选择了自由开源基金会作为 Write for Donations 计划的捐赠对象。 介绍 在软件开发中&#xff0c;导入和导出数据库是一项常见任务。您可以使用数据转储来备份和恢复信息。您还可以使用它们将数据迁移到新的服务器或开发环境。 在本教程中&#xff0c;您将使用 MySQL 或…

spring xml实现bean的生命周期

spring xml实现bean的生命周期 搭建maven工程&#xff0c;导入maven依赖(需要jdk17或者降低spring-context版本) <dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><versio…

Linux GCC G++:各个版本发行年份及新增特性盘点

目录 GCC盘点 GCC 4.0 GCC 4.1 GCC 4.2 GCC 4.3 GCC 4.4 GCC 4.5 GCC 4.6 GCC 4.7 GCC 4.8 GCC 4.9 GCC 5.0 GCC 6.0 GCC 7.0 GCC 8.0 GCC 9.0 GCC 10.0 GCC 11.0 GCC 12.0 GCC 13.0 GCC稳定版本 GCC 4.8 GCC 4.9 GCC 5.0 GCC 6.0 GCC 7.0 GCC 8.0 …

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、…

【React】《React 学习手册 (第2版) 》笔记-Chapter12-React 服务器端渲染

十二、React 服务器端渲染 我们可以采用同构方式渲染 React&#xff0c;以便支持浏览器以外的平台。这意味着&#xff0c;我们可以在服务器端渲染 UI&#xff0c;然后再发给浏览器。借助服务器端渲染&#xff0c;可以提升性能、增进可移植性、提高安全性。 同构&#xff08;is…

Flink Time 详解

Flink在处理实时数据流时&#xff0c;时间是一个核心概念。Flink支持多种时间语义&#xff0c;以满足不同业务场景的需求。以下是对Flink中时间概念的详细解释&#xff1a; 一、时间概念概述 在Flink中&#xff0c;时间主要分为三种类型&#xff1a; 事件时间&#xff08;Eve…

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

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

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

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

vue3第二阶段的开发文档

1 2.1 案例——学习计划表 2.1.1 准备工作 在开发“学习计划表”案例之前&#xff0c;需要先完成一些准备工作&#xff0c;具体步骤如下。 ① 打开命令提示符&#xff0c;切换到 D:\vue\chapter02 目录&#xff0c;在该目录下执行如下命令&#xff0c;创建 项目。 np…

深入理解预处理

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、客户端…