深入理解C++关联式容器:set、multiset、map和multimap详解

news2024/12/24 22:58:29

序列式容器 与 关联式容器

我们知道:

  • C++ 中,我们将 vector、list、queue 这种底层为线性序列的数据结构叫做 序列式容器,其存储的就是元素本身
  • 关联式容器键-值对的形式存储数据。每个键在容器中必须是唯一的,而值则与相应的键关联。

键-值对

<key,value> 结构,在C++标准库中,std::pair 是一个模板类,其存储的就是键值对。其定义如下:

namespace std {
    template <class T1, class T2>
    struct pair {
        typedef T1 first_type;
        typedef T2 second_type;

        T1 first;
        T2 second;

        pair();
        pair(const T1& x, const T2& y);
        template<class U, class V> pair(const pair<U, V>& p);
    };
}

一般键是唯一且不可重复的标识符,用于查找和访问对应的值。值可以是任何数据类型,包括整数、字符串、对象等


树形结构的关联式容器

C++ 中,标准库提供了 std::map 、std::set 、std::multimap、std::multimap 四个基于红黑树实现的关联式容器。下面依次介绍其性质与使用:


std::set

Cplusplus 官网set文档

性质

上面的cpp文档详细的介绍了set这个容器,下面我们对其性质进行总结概括:

  1. 有序性set 中的元素按照其key值自动被排序。默认情况下使用元素的 < 操作符(小于)比较,也可以通过自定义比较函数来指定排序规则。插入 / 删除 新元素时,set 会自动确保元素的有序性,并且不会插入重复的元素。

  2. 唯一性:对于set,元素的值(value)即是键(key),而且每个value必须是唯一的。

  3. 不可修改性:set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。

  4. 底层实现:通常基于红黑树实现,因此插入、删除和查找操作的平均时间复杂度都是 O(log n)。

  5. 迭代器支持:set 提供了正向迭代器,可以用于遍历集合中的元素。

Attention:

  • set在创建时为std::set<value> myset;,只存放value,其底层上存放的是<value, value> 的键值对。实际实现中,只存储了键,而值部分为空。
  • 我们知道set中的元素不可直接修改,为什么?
    • 由于 std::set 是一种基于红黑树实现的关联容器,它要求元素在容器中保持有序性和唯一性。因此,直接修改元素可能会破坏红黑树的结构,导致容器不再符合要求。

使用

模板参数列表

下面是C++标准库中的 set 模板参数列表

template<
    class Key, // Key(键)类型
    class Compare = std::less<Key>, // 比较函数类型
    class Allocator = std::allocator<Key> // 分配器类型
> class set;

分别解释:

  • Keystd::set 中存储的元素的类型。set 中的元素按照键值自动被排序,并且每个元素在中都是唯一的
  • Compare可选的比较函数类型,用于指定元素的比较规则。默认情况下,使用 < 操作符进行比较。
  • 可选的分配器类型用于分配内存和管理容器中的元素

应用

数据去重std::set会自动对元素进行排序并移除重复的元素

std::vector<int> numbers = {4, 1, 2, 2, 3, 4};
std::set<int> uniqueNumbers(numbers.begin(), numbers.end());
// 现在uniqueNumbers中包含的是去重后的元素{1, 2, 3, 4}

快速查找std::set内部基于红黑树实现,因此查找操作非常高效,时间复杂度为O(log n)

std::set<std::string> names = {"Alice", "Bob", "Charlie", "David"};
if (names.find("Bob") != names.end())  // 查找Bob
{
    std::cout << "Bob 在set中" << std::endl;
}

迭代器的使用可以通过迭代器来访问集合中的元素

#include <iostream>
#include <set>

int main() {
    std::set<int> mySet = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};

    // 使用迭代器遍历 set 中的所有元素
    for (auto it = mySet.begin(); it != mySet.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    // 反向迭代器
    for (auto rit = mySet.rbegin(); rit != mySet.rend(); ++rit) {
        std::cout << *rit << " ";
    }
    std::cout << std::endl;

    // 使用迭代器查找特定元素
    int target = 5;
    auto findIt = mySet.find(target);
    if (findIt != mySet.end()) {
        std::cout << target << "找到了" << std::endl;
    } else {
        std::cout << target << "没找到" << std::endl;
    }

    return 0;
}

std::map

cplusplus 官网文档

性质

上面的cpp文档详细的介绍了map这个容器,下面我们对其性质进行总结概括:

  1. 有序性map 中的元素按照key的大小顺序进行排序,默认情况下采用升序方式。这使得在 map 中进行范围遍历时,能够以键的顺序访问元素。

  2. 唯一键map 中的键是唯一的,如果插入一个已经存在的键,则会覆盖原有的键对应的值。

  3. 查找效率:由于 map 基于红黑树实现,对于元素的查找、插入和删除操作具有较高的效率,时间复杂度为 O(log n)。

  4. 键-值对映射:map 提供了键和值之间的映射关系,通过键可以快速查找对应的值,这使得 map 适合用于需要快速查找和检索值的场景。

Attention:

  • map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。

模板参数列表

template <class Key, class T, class Compare = std::less<Key>, class Allocator = std::allocator<std::pair<const Key, T>>>
class map;

这里是对每个模板参数的解释:

  • Key键的类型,用于唯一标识每个元素。

  • T值的类型,与键相关联的数据类型。

  • Compare可选参数,用于比较键的函数对象类型,默认情况下采用 std::less<Key>,表示采用 < 操作符进行键的比较。

  • Allocator可选参数,内存分配器的类型,默认情况下采用 std::allocator<std::pair<const Key, T>>,表示使用标准分配器

应用

我们下面用map进行一些应用:字典、计数器

字典

#include <iostream>
#include <map>
#include <string>

int main() {
	// 创建字典
    std::map<std::string, std::string> dictionary;

    // 添加词条到字典
    dictionary["apple"] = "苹果";
    dictionary["banana"] = "香蕉";
    dictionary["cat"] = "猫";

    // 查找词条
    std::string word = "";
    getline(word);
    if (dictionary.find(word) != dictionary.end()) {
        std::cout << word << ": " << dictionary[word] << std::endl;
    } else {
        std::cout << "字典中未查到该词" << std::endl;
    }

    return 0;
}

上面的代码中,我们使用map<string, string> 来模拟字典,key,value分别对应一个单词的英文和中文含义。

计数器

#include <iostream>
#include <map>

int main() {
    std::map<int, int> counter;

    // 统计数字出现的次数
    int numbers[] = {5, 3, 7, 5, 3, 9, 5};
    for (int num : numbers) {
        counter[num]++;
    }

    // 输出数字及其出现的次数
    for (const auto& pair : counter) {
        std::cout << pair.first << " occurs " << pair.second << " times" << std::endl;
    }

    return 0;
}

计数器通过将value值设为int类型,可以统计值为key的元素的出现个数。


std::multiset

cpluscplus官网文档

性质

multiset 的 模板参数列表 与 set 是相同的,但在性质上有所差别。

这里我们先看 多重集(Multiset)和集合(Set)之间 的区别

  1. 元素重复性multiset 允许元素重复出现,而 set 中每个元素都是唯一的。

  2. 插入操作:在 set 中,插入已经存在的元素会被忽略,因为元素不能重复;而在 multiset 中,可以插入重复的元素,每个元素将会被单独计数

  3. 查找操作:在 set 中,由于元素唯一,查找特定元素的速度较快;而在 multiset 中,由于元素可能重复,查找特定元素需要遍历多个相同元素

  4. 应用场景:set 适合用于需要保持元素唯一性的场景,比如存储不重复的关键字;而 multiset 则适用于需要统计元素重复次数的场景,比如统计数据集中各个元素的出现频率。

使用

当有以下情况时,我们会使用 multiset 而不是set:

  1. 需要存储重复元素的情况
  2. 不需要维护元素的唯一性
  3. 需要快速查找和删除重复元素

下面的代码展示了,multiset的使用:

int main() {
    std::multiset<int> numbers;

    // 插入元素
    numbers.insert(10);
    numbers.insert(20);
    numbers.insert(30);
    numbers.insert(20);  // 插入重复的元素

    // 输出元素
    std::cout << "该multiset包含: ";
    for (const auto& num : numbers) {
        std::cout << " " << num;
    }
    std::cout << std::endl;

    // 统计特定元素的重复次数
    int target = 20;
    int count = numbers.count(target);
    printf("值为%d的元素共有%d个\n", target, count);

    // 删除指定元素
    numbers.erase(target);

    // 再次输出元素
    printf("删除元素%d后,multiset共包含:", target);
    for (const auto& num : numbers) {
        std::cout << " " << num;
    }
    std::cout << std::endl;

    return 0;
}

代码输出结果:

在这里插入图片描述


std::multimap

cpluscplus官网文档

性质

这里我们主要关心 multimap 与 map 的区别(性质 及 操作):

  • 键的唯一性map中的键是唯一的,每个键只能对应一个值。而multimap允许多个键有相同的值,一个键可以对应多个值

  • 插入顺序: map按照键的大小顺序进行排序,并保持该顺序。multimap不对键进行排序,插入的顺序被保留

  • 查找操作在map中,由于键的唯一性,使用find()函数查找一个键时,如果找到了就返回该键对应的值,如果没找到则返回end()迭代器。而在multimap中,find()函数返回指向第一个匹配的键-值对的迭代器,如果没有匹配的键,则返回end()迭代器。

  • 删除操作: 在map中,使用erase()函数删除一个键时,如果该键存在,则删除该键对应的键-值对,并将其从容器中移除。而在multimap中,erase()函数会删除所有与给定键匹配的键-值对

总的来说,multimap 提供了一种允许重复键并自动排序的数据结构,适用于需要按照键进行检索且允许重复键的情况下使用。

应用

我们利用multimap 一个key可以对应多个value的特性,下面的示例使用 multimap 构建了一个学生表:

#include <iostream>
#include <map>
#include <string>

int main() {
    std::multimap<std::string, std::string> studentCourses;

    // 添加学生和他们的课程
    studentCourses.insert(std::make_pair("Alice", "Math"));
    studentCourses.insert(std::make_pair("Alice", "Physics"));
    studentCourses.insert(std::make_pair("Bob", "Chemistry"));
    studentCourses.insert(std::make_pair("Bob", "Biology"));
    studentCourses.insert(std::make_pair("Bob", "Math"));

    // 输出每个学生选修的课程
    for (const auto& pair : studentCourses) {
        std::cout << pair.first << " takes " << pair.second << std::endl;
    }

    return 0;
}

输出如下:

在这里插入图片描述

结尾

上面介绍的关联式容器,在做OJ题时也会经常用上,在理解其使用场景后做题,可以很好的运用它们。

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

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

相关文章

基于连续Hopfield神经网络优化——旅行商问题优化计算

大家好&#xff0c;我是带我去滑雪&#xff01; 利用神经网络解决组合优化问题是神经网络应用的一个重要方面。所谓组合优化问题&#xff0c;就是在给定约束条件下&#xff0c;使目标函数极小&#xff08;或极大&#xff09;的变量组合问题。将Hopfield网络应用于求解组合优化问…

Centos7 升级到 Centos8 教程以及关于dnf包管理工具的若干问题解决方案

目录 为什么升级一、参考文档二、升级步骤三、安装git编码错误缓存问题安装git依赖冲突问题解决办法 为什么升级 jenkins 2.4版本需要CentOS8 一、参考文档 点我 二、升级步骤 1.安装epel源 yum -y install epel-release2.安装rpmconf和yum-utils yum -y install rpmco…

死锁(JAVA)

死锁在多线程代码中是非常严重的BUG&#xff0c;一旦代码中出现死锁就会导致线程卡死。 当单个线程连续两次对同一个对象进行加锁操作时&#xff0c;如果该锁是不可重入锁就会发生死锁&#xff08;线程卡死&#xff09; 两个线程两把锁&#xff0c;如果出现这种情况也是会发生…

瑞吉外卖Day02

小张推荐:瑞吉外卖Day01&#xff0c;瑞吉外卖Day03 1.登陆功能 1.1结果封装类 导入返回结果类R此类是一个通用结果类。服务端响应的所有结果最终都会包装成此种类型返回给前端页面 注意属性名&#xff0c;莫要写错&#xff0c;不然与前端很难对接&#xff01;&#xff01;&…

Docker 中的端口

Docker 中的端口 0.0.0.0:8080->80/tcp &#xff0c;主机&#xff08;即运行 Docker 的机器&#xff09;监听8080端口&#xff0c;如果有请求转发到容器的 80 端口上去。 详细解释一下&#xff1a; 0.0.0.0:8080->80/tcp &#xff1a;这是一个端口映射规则。 0.0.0.0:80…

Leetcode—202.快乐数【简单】

2023每日刷题&#xff08;二十八&#xff09; Leetcode—202.快乐数 快慢指针思想 通过手玩2&#xff0c;可以发现 会走入一个循环&#xff0c;并且fast和slow会在一个数字相遇&#xff0c;以下也大概花了一下推倒出来了。如果slow不是因为1和fast相等的&#xff0c;就说明它…

消息队列中的事务是什么呢?

消息队列中的事务是什么呢&#xff1f; 说到事务&#xff0c;肯定会优先想到数据库中的事务。在数据库中需要事务&#xff0c;是为了保证数据的一致性、完整性、持久性和隔离性。它可以将数据库中的一组操作合并为一个不可分割的工作单元&#xff0c;要么全部执行成功&#xf…

​软考-高级-系统架构设计师教程(清华第2版)【第4章 信息安全技术基础知识(P160~189)-思维导图】​

软考-高级-系统架构设计师教程&#xff08;清华第2版&#xff09;【第4章 信息安全技术基础知识&#xff08;P160~189&#xff09;-思维导图】 课本里章节里所有蓝色字体的思维导图

【沐风老师】3dMax使用克隆修改器插件创建旋转楼梯教程

3DMAX克隆修改器插件&#xff0c;它通过增量平移、旋转和缩放输入几何体来创建对象的副本。在某些方面&#xff0c;它类似于 3dMax 的内置“阵列”工具&#xff0c;但有一个主要优点 -克隆修改器是完全参数化的&#xff0c;因此您可以随时更改重复项的数量及其分布。其他功能包…

mapboxGL中的底图切换

概述 底图切换&#xff0c;这么简单的功能还要写一篇文章&#xff1f;值得的&#xff0c;为什么这么说呢&#xff1f;因为mapboxGL的矢量底图有上百个&#xff0c;不同的底图用的样式、图层的名称、图层的内容、字体库、图标库都不一样&#xff0c;尤其是当地图上已经叠加了很…

展会预告 | 图扑邀您相约高交会-全球清洁能源创新博览会

第二十五届中国国际高新技术成果交易会(简称“高交会”)将在深圳盛大开幕。高交会由商务部、科学技术部、工业和信息化部、国家发展改革委、农业农村部、国家知识产权局、中国科学院、中国工程院和深圳市人民政府共同举办。是目前中国规模最大、最具影响力的科技类展会。 图扑软…

【LittleXi】C程序预处理、编译、汇编、链接步骤

【LittleXi】C程序预处理、编译、汇编、链接步骤 C程序 #include<stdio.h> int main(){int x1,y1;printf("xy%d",xy); }1、预处理 将头文件引入进来、除去注释、宏定义下放 执行指令 g -E esc.c -o esc.i 2、编译 将处理好的代码编译为汇编代码.s 执行…

AI优秀企业案例——机器人流程自动化:达观数据RPA

通过学习业内领先公司的最佳实践&#xff0c;我们可以更好地将它们应用到我们自己的公司和业务中。特别是第三部分&#xff0c;提供了大量应用案例&#xff0c;让我们一起期待看到这些案例的结尾。 1.简介 达观数据是一家专注于智能文本机器人的国家高新技术企业&#xff0c;…

Flink SQL -- 反压

1、测试反压&#xff1a; 1、反压&#xff1a; 指的是下游消费数据的速度比上游产生数据的速度要小时会出现反压&#xff0c;下游导致上游的Task反压。 2、测试反压&#xff1a;使用的是DataGen CREATE TABLE words (word STRING ) WITH (connector datagen,rows-per-second…

C# 使用AForge调用摄像头

C# 使用AForge调用摄像头 安装AForge使用AForge控件示例代码 AForge官网 安装AForge Visual Studio 2022>项目>管理NuGet程序包&#xff0c;搜索AForge并依次安装作者为AForge.NET的多个关联组件。 使用AForge控件 安装AForge组件完成后&#xff0c;工具箱会新增AF…

【反编译系列】一、反编译 .so 文件(IDA Pro)

文章目录 【反编译系列】一、反编译 .so 文件&#xff08;IDA Pro&#xff09;1. 介绍2. 反编译Reference 【反编译系列】一、反编译 .so 文件&#xff08;IDA Pro&#xff09; 1. 介绍 .so 文件是共享对象文件&#xff08;Shared Object file&#xff09;的一种形式&#xf…

【Cookie 和 session 的区别】

会话&#xff08;Session&#xff09; 跟踪是Web程序中常用的技术&#xff0c;用来跟踪用户的整个会话。cookie和session都是用来跟踪浏览器用户身份的会话方式。Cookie通过在客户端记录信息确定用户身份&#xff0c;Session通过在服务器端记录信息确定用户身份。 我们目前使用…

网络之路25:VLAN进阶实验-Super VLAN

正文共&#xff1a;1333 字 13 图&#xff0c;预估阅读时间&#xff1a;1 分钟 目录 网络之路第一章&#xff1a;Windows系统中的网络 0、序言 1、Windows系统中的网络1.1、桌面中的网卡1.2、命令行中的网卡1.3、路由表1.4、家用路由器 网络之路第二章&#xff1a;认识企业设备…

【verilog】verilog语法刷题知识点总结

verilog语法刷题知识点总结 1.状态机2.任务和函数的区别3.case&#xff0c;casez和casex4.随机数产生关键字5.运算符优先级6.运算符的特殊注意点及特殊运算符(1)移位运算符(2)等式运算符(3)动态位宽截取运算符(4)求余运算符&#xff08;%&#xff09; 7.testbench知识点8.乘法器…

CTFSHOW -SQL 注入

重新来做一遍 争取不看wp web171 基本联合注入 拿到题目我们已经知道了是sql注入 所以我们可以直接开始 第一题 不会难道哪里去 所以我们直接进行注入即可 1 and 12-- 1 and 11-- 实现闭合 -1unionselect1,2,3--%2b 查看字段数-1unionselect1,database(),3--%2b 查看数据…