当前位置: 首页 > 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;建立触发…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

如何更改默认 Crontab 编辑器 ?

在 Linux 领域中&#xff0c;crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用&#xff0c;用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益&#xff0c;允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...

0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化

是不是受够了安装了oracle database之后sqlplus的简陋&#xff0c;无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话&#xff0c;配置.bahs_profile后也能解决上下翻页这些&#xff0c;但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可&#xff0c…...

保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!

目录 补间动画 1.创建资源文件夹 2.设置文件夹类型 3.创建.xml文件 4.样式设计 5.动画设置 6.动画的实现 内容拓展 7.在原基础上继续添加.xml文件 8.xml代码编写 (1)rotate_anim (2)scale_anim (3)translate_anim 9.MainActivity.java代码汇总 10.效果展示 逐帧…...

【Linux】Linux安装并配置RabbitMQ

目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的&#xff0c;需要先安…...