当前位置: 首页 > news >正文

C++相关概念和易错语法(23)(set、仿函数的应用、pair、multiset)

1.set和map存在的意义

(1)set和map的底层都是二叉搜索树,可以达到快速排序(当我们按照迭代器的顺序来遍历set和map,其实是按照中序来遍历的,是排过序的)、去重、搜索的目的。

(2)优先级队列priority_queue也有类似的功能,但是它的底层是数组,在插入删除频繁的情况下效率降低,且它的数据结构是堆,在排序上效率还行,但在搜索上就无能为力了。

(3)在C++数据结构重要知识点(1)我就讲过二叉搜索树的特性,在退化的情况下搜索效率低下,所以要引入AVL树、红黑树这样的平衡树来解决问题。set和map就引入了红黑树,使得这两个容器的搜索效率很高。

(4)set和map有什么区别呢?其实就是分别对应key和key-value模型。set是针对key单个数据的二叉搜索树,而map是针对key-value那样的键值对的二叉搜索树。

(5)两种容器

序列式容器:vector、list对存储数据的顺序的要求不高,注意这句话的意思是在序列式容器中换两个数据虽然可能会影响到它的功能(比如本来是降序排列的vector,现在被打乱了),但不影响容器结构本身,vector还叫vector
关联式容器:map、set对数据顺序有强关联性,结构靠数据支撑,如果你随便换两个数据的位置,那么整个容器就崩了,而且无法修复。

2.set

set的底层引入了红黑树,大致和key模型的二叉搜索树一样,是借助二叉树的特性来存放数据,达到排序和搜索的功能的。但是在接口上和我们上篇文章分享的又不一样

(1)模板参数、仿函数的应用

第一个模板参数:要存放数据的类型,可以是int这样的内置类型,也可以是自定义类型

第三个模板参数基本不用管

第二个模板参数:默认是less<T>,在用迭代器遍历时是从小到大,注意我们前面讲的优先级队列priority_queue默认也是less<T>,但对应的是大堆。我们也可以自己写一个仿函数作为set的第二个模板参数,自定义规则,不过自定义仿函数的坑有点多,下面分享一下需要注意的点,加深对仿函数的理解。

先看以下代码

#include <iostream>
#include <string>
#include <set>
using namespace std;template<class T>
struct Compare
{bool operator()(const T& t1, const T& t2) const{return t1 > t2;}
};template<>
struct Compare<string>
{bool operator()(const string& s1, const string& s2) const{return (s1.size() > s2.size()) || (s1.size() == s2.size() && s1 < s2);}
};int main()
{set<int, Compare<int>> s1;s1.insert(1);s1.insert(2);s1.insert(3);set<string, Compare<string>> s2;s2.insert("zzzzzzz");s2.insert("aaaaaaa");s2.insert("bb");for (const auto& e : s1){cout << e << " ";}cout << endl;for (const auto& e : s2){cout << e << " ";}return 0;
}

输出结果是

为什么对于s1是从大排到小呢?为什么s2是这样排的呢?它们是怎样控制的呢?

在默认的情况下是less,对应的是从小排到大,即小的元素在大的元素前面,因此我们可以这样分析

再分析我们的

在写仿函数的时候特别注意举一反三,我上面两张图都提到的“返回true是谁在前”并不适用于所有情况(map和set都遵循),返回true时到底是t1在前还是t2在前要自己判断。借助默认排序方式和仿函数类型可以判断,如果set默认仿函数是greater,而默认访问是从小到大,那么自己写仿函数时就应该遵循“返回true时是t2(第二个函数参数)在前”来写代码了。

我们并不知道STL里面到底是怎样排序的,所以从细节推理出排序结果很重要,看似很简单,但一定不能含糊。

还有个细节:在自己写仿函数时要把重载函数定义为const对象,否则是编译不通过的。

(2)构造函数

总体分为三类:空构造、拷贝构造、迭代器构造

最后的内存池相关的参数不管它,倒数第二个comp只能是显式实例化容器时使用的仿函数,不过一般也不写,因为编译器会自己生成对应的仿函数

(3)insert、pair

insert最常用的就是第一个,第二第三个基本不用。但是返回值pair<iterator, bool>是什么呢?

pair是一个模板类,叫键值对,它有两个成员变量,一个叫first,另一个叫second,first和second构成一一对应的关系,first相当于key,second相当于value,在map中也用到了它。

要创建一个pair对象也很简单

pair和我们之前学的容器不同,pair只是一个存储数据的类型,它的底层非常简单,它存在的意义就是将key和value联系起来,根本不存在增删查改。

作为一个专门用于存储数据的类型,它也有自己的判断大小的方式,也很好理解。当first和second都相同时pair相同,first大的pair就大,first都相同时second大的pair就大。

了解完pair之后我们可以去研究set的insert了

val就是我们想插入set的数据,那么返回的键值对有什么价值呢?

这也是set和map可以实现去重的原因之一,除此以外,像find之类的函数也可以通过insert变相实现了

(4)erase、count、multiset

常用的是第二种,直接删除某个值,返回值是删除的值的个数。这个时候我们就有疑问了,直接用bool不好吗,删除了就是true,删不掉就是false,这是为了multiset准备的

multiset是一个没有去重效果的set,可以用于除去重以外其他功能的实现

值得注意的是,multiset的大部分接口和set没什么两样,但是在set中有的接口设计会考虑去重,比如insert的返回值,而在multiset中这就没有必要了,所以存在一些不同之处

在find中multiset返回的就是第一个出现的val的迭代器

同样的,像count函数也存在erase类似处理的情况

count返回的是val在set中出现次数,就是为了统一set和multiset的接口用法

(5)lower_bound、upper_bound

这两个函数还是比较容易混的,我们先看看下面的代码


int main()
{set<int> s;s.insert(1);s.insert(2);s.insert(3);s.insert(4);s.insert(5);s.erase(s.lower_bound(2), s.upper_bound(4));for (const auto& e : s){cout << e << " ";}return 0;
}

输出结果是

lower_bound和upper_bound返回的是对应值的迭代器吗?如果真是这样,那4就不应该被删掉,且和find就没区别了。

事实上,对于lower_bound而言,它返回的是按迭代器遍历顺序大于等于val的值的迭代器,在上面的代码中2存在,于是就把2对应的迭代器返回了回去,如果2不存在就会向上找。

而upper_bound返回的是按迭代器遍历顺序大于val的值的迭代器,在上面的代码中4虽然存在,但它会找比4大的值,返回的是5的迭代器,因此erase按左闭右开的规则会删掉4。

两种迭代器都是向比自己大的值去找,但lower_bound要找等于自己的,upper_bound不找。在erase中却很好理解,s.erase(s.lower_bound(2), s.upper_bound(4));就是删掉2到4之间的所有值(闭区间)。在所有迭代器的组合使用中,都是左闭右开,lower_bound对应左,upper_bound对应右,这样你就明白为什么这样设计了。

如果找不到符合规则的迭代器,那就会返回end。

(6)find

前面我已经介绍了find,这里为什么还要介绍呢?前面的find是set容器里自带的,而这里我想讨论算法库的find和容器里的find的区别

在算法库中,find前两个参数是迭代器区间,第三个是要查找的值

而在set中,不需要前两个参数了。

似乎两者没什么区别,但在底层上区别就很大了。算法库的find只能根据迭代器不断++来找。在set和map中迭代器的顺序就是中序的顺序。但对于set自带的find而言就不是按照中序来找数据了,而是按照平衡二叉搜索树的特点左小右大来找了,在高度次内就能找到。算法库的时间复杂度是O(N),而自带的find时间复杂度是O(logN)

相关文章:

C++相关概念和易错语法(23)(set、仿函数的应用、pair、multiset)

1.set和map存在的意义 &#xff08;1&#xff09;set和map的底层都是二叉搜索树&#xff0c;可以达到快速排序&#xff08;当我们按照迭代器的顺序来遍历set和map&#xff0c;其实是按照中序来遍历的&#xff0c;是排过序的&#xff09;、去重、搜索的目的。 &#xff08;2&a…...

netty入门-3 EventLoop和EventLoopGroup,简单的服务器实现

文章目录 EventLoop和EventLoopGroup服务器与客户端基本使用增加非NIO工人NioEventLoop 处理普通任务与定时任务 结语 EventLoop和EventLoopGroup 二者大概是什么这里不再赘述&#xff0c;前一篇已简述过。 不理解也没关系。 下面会简单使用&#xff0c;看了就能明白是什么 这…...

通信原理-思科实验五:家庭终端以太网接入Internet实验

实验五 家庭终端以太网接入Internet实验 一实验内容 二实验目的 三实验原理 四实验步骤 1.按照上图选择对应的设备&#xff0c;并连接起来 为路由器R0两个端口配置IP 为路由器R1端口配置IP 为路由器设备增加RIP&#xff0c;配置接入互联网的IP的动态路由项 5.为路由器R1配置静…...

【Vue】vue概述

1、简介 Vue.js&#xff08;简称Vue&#xff09;是一款用于构建用户界面的渐进式JavaScript框架。由前Google高级软件工程师尤雨溪&#xff08;Evan You&#xff09;于2014年创建&#xff0c;是一个独立且社区驱动的开源项目。Vue.js基于标准的HTML、CSS和JavaScript&#xff…...

Docker use experience

#docker command docker load -i <镜像文件.tar> docker run -it -d --name 容器名 -p 宿主机端口:容器端口 -v 宿主机文件存储位置:容器内文位置 镜像名:Tag /bin/bash docker commit -m"提交的描述信息" -a"作者" 容器ID 要…...

Android平台RTSP|RTMP直播播放器技术接入说明

技术背景 大牛直播SDK自2015年发布RTSP、RTMP直播播放模块&#xff0c;迭代从未停止&#xff0c;SmartPlayer功能强大、性能强劲、高稳定、超低延迟、超低资源占用。无需赘述&#xff0c;全自研内核&#xff0c;行业内一致认可的跨平台RTSP、RTMP直播播放器。本文以Android平台…...

数据结构——栈(顺序结构)

一、栈的定义 栈是一种数据结构&#xff0c;它是一种只能在一端进行插入和删除操作的特殊线性表。这一端被称为栈顶&#xff0c;另一端被称为栈底。栈按照后进先出&#xff08;LIFO&#xff09;的原则进行操作&#xff08;类似与手枪装弹后射出子弹的顺序&#xff09;。在计算…...

速盾:cdn能防御ddos吗?

CDN&#xff08;内容分发网络&#xff09;是一种广泛应用于互联网中的技术&#xff0c;它通过将内容分发到全球各地的服务器上&#xff0c;以提高用户在访问网站时的加载速度和稳定性。然而&#xff0c;CDN是否能够有效防御DDoS&#xff08;分布式拒绝服务&#xff09;攻击是一…...

分享 2 个 .NET EF 6 只更新某些字段的方法

前言 EF 更新数据时&#xff0c;通常情况下&#xff0c;是更新全部字段的&#xff0c;但实际业务中&#xff0c;更新全部字段的情况其实很少&#xff0c;一般都是修改其中某些字段&#xff0c;所以为了实现这个目标&#xff0c;很多程序员通常会这样作&#xff1a; 先从数据库…...

vs code解决报错 (c/c++的配置环境 远端机器为Linux ubuntu)

参考链接&#xff1a;https://blog.csdn.net/fightfightfight/article/details/82857397 https://blog.csdn.net/m0_38055352/article/details/105375367 可以按照步骤确定那一步不对&#xff0c;如果一个可以就不用往下看了 目录 一、检查一下文件扩展名 二、安装扩展包并…...

08 字符串和字节串

使用单引号、双引号、三单引号、三双引号作为定界符&#xff08;delimiter&#xff09;来表示字符串&#xff0c;并且不同的定界符之间可以相互嵌套。 很多内置函数和标准库对象也都支持对字符串的操作。 x hello world y Python is a great language z Tom said, "Le…...

vue使用mavonEditor(流程图、时序图、甘特图实现)

mavonEditor 安装mavonEditor $ npm install mavon-editor --save使用 // 全局注册import Vue from vueimport mavonEditor from mavon-editorimport mavon-editor/dist/css/index.css// useVue.use(mavonEditor)new Vue({el: #main,data() {return { value: }}})//局部使用…...

Java实现短信验证码服务

1.首先这里使用的是阿里云的短信服务。 package com.wzy.util;; import cn.hutool.captcha.generator.RandomGenerator; import com.aliyun.dysmsapi20170525.Client; import com.wzy.entity.Ali; import org.springframework.stereotype.Component;/*** Author: 顾安* Descri…...

python中的线程

线程 线程概念 线程 在一个进程的内部, 要同时干多件事, 就需要同时运行多个"子任务", 我们把进程内的这些"子任务"叫做线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中, 是进程的实际运作单位。一条线程指的是进程中一个单一顺序的控制流…...

hcip学习 多实例生成树,VRRP工作原理

一、STP 和 RSTP 解决了什么问题 1、STP&#xff1a;解决了在冗余的二层网络中所出现的环路问题 2、RSTP&#xff1a;在 STP 的基础上&#xff0c;解决了 STP 收敛速度慢的问题&#xff0c;引入了一些 STP 保护机制&#xff0c;使其网络更加稳定 二、MSTP 针对 RSTP 的改进 …...

Docker搭建群晖

Docker搭建群晖 本博客介绍在docker下搭建群晖 1.编辑docker-compose.yml文件 version: "3" services:dsm:container_name: dsmimage: vdsm/virtual-dsm:latestenvironment:DISK_SIZE: "16G"cap_add:- NET_ADMIN ports:- 8080:50…...

【java】BIO,NIO,多路IO复用,AIO

在Java中&#xff0c;处理I/O操作的模型主要有四种&#xff1a;阻塞I/O (BIO), 非阻塞I/O (NIO), 异步I/O (AIO), 以及IO多路复用。下面详细介绍这四种I/O模型的工作原理和应用场景。 1. 阻塞I/O (BIO) 工作原理 阻塞I/O是最传统的I/O模型。在这种模型中&#xff0c;当一个线…...

服务器怎样减少带宽消耗的问题?

择业在使用服务器的过程中会消耗大量的带宽资源&#xff0c;而减少服务器的带宽消耗则可以帮助企业降低经济成本&#xff0c;同时还能够提高用户的访问速度&#xff0c;那么服务器怎样能减少带宽的消耗呢&#xff1f;本文就来带领大家一起来探讨一下吧&#xff01; 企业可以选择…...

linux 报错:bash: /etc/profile: 行 32: 语法错误:未预期的文件结束符

目录 注意错误不一定错在最后一行 i进入编辑 esc退出编辑 &#xff1a;wq 保存编辑退出 &#xff1a;q&#xff01;不保存退出 if [ $# -eq 3 ] then if [ ! -e "$1" ]; then miss1 $1 elif [ ! -e "$2" -a ! -e "$3" ]; then miss2and3…...

MySQL练习(5)

作业要求&#xff1a; 实现过程&#xff1a; 一、触发器 &#xff08;1&#xff09;建立两个表&#xff1a;goods&#xff08;商品表&#xff09;、orders&#xff08;订单表&#xff09; &#xff08;2&#xff09;在商品表中导入商品记录 &#xff08;3&#xff09;建立触发…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

蓝桥杯3498 01串的熵

问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798&#xff0c; 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)

船舶制造装配管理现状&#xff1a;装配工作依赖人工经验&#xff0c;装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书&#xff0c;但在实际执行中&#xff0c;工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下&#xff0c;风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...

GruntJS-前端自动化任务运行器从入门到实战

Grunt 完全指南&#xff1a;从入门到实战 一、Grunt 是什么&#xff1f; Grunt是一个基于 Node.js 的前端自动化任务运行器&#xff0c;主要用于自动化执行项目开发中重复性高的任务&#xff0c;例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...