C++ map和set的应用

news2025/1/12 21:03:37

1. 关联式容器

我们已经接触过STL中的部分容器,比如:vector、list、deque、 forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。那什么是关联式容器?它与序列式容器有什么区别?

关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是结构的键值对,在数据检索时比序列式容器效率更高。

2. 键值对

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代 表键值,value表示与key对应的信息。比如:现在要建立一个英汉互译的字典,那该字典中必然 有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应 该单词,在词典中就可以找到与其对应的中文含义。

SGI-STL中关于键值对的定义:

template <class T1, class T2>
struct pair
{
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair(): first(T1()), second(T2())
{}
pair(const T1& a, const T2& b): first(a), second(b)
{}
};

3. 树形结构的关联式容器

根据应用场景的不同,STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构。树型结 构的关联式容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。下面一依次介绍每一个容器。

3.1 set

3.1.1 set的介绍

set文档

1. set是按照一定次序存储元素的容器;

2. 在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们;

3. 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序;

4. set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对子集进行直接迭代;

5. set在底层是用二叉搜索树(红黑树)实现的。

注意:

1. 与map/multimap不同,map/multimap中存储的是真正的键值对,set中只放 value,但在底层实际存放的是由构成的键值对;

2. set中插入元素时,只需要插入value即可,不需要构造键值对;

3. set中的元素不可以重复(因此可以使用set进行去重);

4. 使用set的迭代器遍历set中的元素,可以得到有序序列;

5. set中的元素默认按照小于来比较;

6. set中查找某个元素,时间复杂度为:log_2 n;

7. set中的元素不允许修改(为什么?);

8. set中的底层使用二叉搜索树(红黑树)来实现。

3.1.2 set的使用

1. set的模板参数列表

T: set中存放元素的类型,实际在底层存储的键值对;

Compare:set中元素默认按照小于来比较;

Alloc:set中元素空间的管理方式,使用STL提供的空间配置器管理。

2. set的构造

 

函数声明功能介绍
set (const Compare& comp = Compare(), const Allocator& = Allocator() );构造空的set
set (InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& = Allocator() );

用[first, last)区 间中的元素构造 set

set ( const set<Key, Compare, Allocator>& x); set的拷贝构造
3. set的迭代器
函数声明功能介绍
iterator begin()返回set中起始位置元素的迭代器
iterator end()返回set中最后一个元素后面的迭代器
const_iterator cbegin() const返回set中起始位置元素的const迭代器
const_iterator cend() const返回set中最后一个元素后面的const迭代器
reverse_iterator rbegin()返回set第一个元素的反向迭代器,即end
reverse_iterator rend()返回set最后一个元素下一个位置的反向迭代器, 即rbegin
const_reverse_iterator crbegin() const返回set第一个元素的反向const迭代器,即cend
const_reverse_iterator crend() const返回set最后一个元素下一个位置的反向const迭 代器,即crbegin
4.set的容量
函数声明功能介绍
bool empty ( ) const检测set是否为空,空返回true,否则返回true
size_type size() const返回set中有效元素的个数

5. set修改操作
函数声明功能介绍
pair <iterator, bool> insert ( const value_type& x )在set中插入元素x,实际插入的是构成的 键值对,如果插入成功,返回,如果插入失败,说明x在set中已经 存在,返回
void erase ( iterator position )删除set中position位置上的元素
size_type erase ( const key_type& x )删除set中值为x的元素,返回删除的元素的个数
void erase ( iterator first, iterator last )删除set中[first, last)区间中的元素
void swap ( set<Key, Compare, Allocator>& st );交换set中的元素
void clear ()将set中的元素清空
iterator find ( const key_type& x ) const返回set中值为x的元素的位置
size_type count ( const key_type& x ) const返回set中值为x的元素的个数
6. set的使用举例
#include <set>
void TestSet()
{
    // 用数组array中的元素构造set
    int array[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0, 1, 3, 5, 7, 9, 2, 4, 
6, 8, 0 };
 set<int> s(array, array+sizeof(array)/sizeof(array));
 cout << s.size() << endl;
 // 正向打印set中的元素,从打印结果中可以看出:set可去重
 for (auto& e : s)
 cout << e << " ";
 cout << endl;
 // 使用迭代器逆向打印set中的元素
 for (auto it = s.rbegin(); it != s.rend(); ++it)
 cout << *it << " ";
 cout << endl;
 // set中值为3的元素出现了几次
 cout << s.count(3) << endl;
}

3.2 map

3.2.1 map的介绍

map文档

1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元 素;

2. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的 内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型 value_type绑定在一起,为其取别名称为pair: typedef pair value_type;

3. 在内部,map中的元素总是按照键值key进行比较排序的;

4. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序 对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列);

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

6. map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。

3.2.2 map的使用

1. map的模板参数说明

key: 键值对中key的类型 T: 键值对中value的类型;

Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递);

Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的空间配置器;

2. map的构造
函数声明功能介绍
map()
构造一个空的map
3. map的迭代器
函数声明功能介绍
begin()和end()begin:首元素的位置,end最后一个元素的下一个位置
cbegin()和cend()与begin和end意义相同,但cbegin和cend所指向的元素不能修改
rbegin()和rend()反向迭代器,rbegin在end位置,rend在begin位置,其 ++和--操作与begin和end操作移动相反
crbegin()和crend()与rbegin和rend位置相同,操作相同,但crbegin和crend所指向的元素不能修改
4. map的容量与元素访问
函数声明功能简介
bool empty ( ) const检测map中的元素是否为空,是返回 true,否则返回false
size_type size() const返回map中有效元素的个数
mapped_type& operator[] (const key_type& k)返回去key对应的value

注意:在元素访问时,有一个与operator[]类似的操作at()(该函数不常用)函数,都是通过key找到与key对应的value然后返回其引用,不同的是:当key不存在时,operator[]用默认value与key构造键值对然后插入,返回该默认value,at()函数直接抛异常。

5. map中元素的修改
函数声明功能简介
pair <iterator, bool> insert ( const value_type& x )在map中插入键值对x,注意x是一个键值 对,返回值也是键值对:iterator代表新插入 元素的位置,bool代表释放插入成功
void erase ( iterator position )删除position位置上的元素
size_type erase ( const key_type& x )删除键值为x的元素
void erase ( iterator first, iterator last )删除[first, last)区间中的元素
void swap ( map<Key, T, Compare, Allocator>& mp )交换两个map中的元素
void clear ( )将map中的元素清空
iterator find ( const key_type& x )在map中插入key为x的元素,找到返回该元 素的位置的迭代器,否则返回end
const_iterator find ( const key_type& x ) const在map中插入key为x的元素,找到返回该元素的位置的const迭代器,否则返回cend
size_type count ( const key_type& x ) const返回key为x的键值在map中的个数,注意 map中key是唯一的,因此该函数的返回值 要么为0,要么为1,因此也可以用该函数来检测一个key是否在map中
#include <string>
#include <map>
void TestMap()
{
 map<string, string> m;
 // 向map中插入元素的方式:
 // 将键值对<"peach","桃子">插入map中,用pair直接来构造键值对
 m.insert(pair<string, string>("peach", "桃子"));
 // 将键值对<"peach","桃子">插入map中,用make_pair函数来构造键值对
 m.insert(make_pair("banan", "香蕉"));
 
 // 借用operator[]向map中插入元素
    /*
 operator[]的原理是:
  用<key, T()>构造一个键值对,然后调用insert()函数将该键值对插入到map中
  如果key已经存在,插入失败,insert函数返回该key所在位置的迭代器
  如果key不存在,插入成功,insert函数返回新插入元素所在位置的迭代器
  operator[]函数最后将insert返回值键值对中的value返回
 */
    // 将<"apple", "">插入map中,插入成功,返回value的引用,将“苹果”赋值给该引
用结果,
 m["apple"] = "苹果";
 // key不存在时抛异常
//m.at("waterme") = "水蜜桃";
 cout << m.size() << endl;
 // 用迭代器去遍历map中的元素,可以得到一个按照key排序的序列
 for (auto& e : m)
 cout << e.first << "--->" << e.second << endl;
 cout << endl;
 // map中的键值对key一定是唯一的,如果key存在将插入失败
 auto ret = m.insert(make_pair("peach", "桃色"));
 if (ret.second)
 cout << "<peach, 桃色>不在map中, 已经插入" << endl;
 else
 cout << "键值为peach的元素已经存在:" << ret.first->first << "--->"
<< ret.first->second <<" 插入失败"<< endl;
 // 删除key为"apple"的元素
 m.erase("apple");
 if (1 == m.count("apple"))
 cout << "apple还在" << endl;
 else
 cout << "apple被吃了" << endl;
}

【总结】

1. map中的的元素是键值对;

2. map中的key是唯一的,并且不能修改;

3. 默认按照小于的方式对key进行比较;

4. map中的元素如果用迭代器去遍历,可以得到一个有序的序列;

5. map的底层为平衡搜索树(红黑树),查找效率比较高O(log_2 N);

6. 支持[]操作符,operator[]中实际进行插入查找。

3.3 multiset

3.3.1 multiset的介绍

multiset文档

1. multiset是按照特定顺序存储元素的容器,其中元素是可以重复的;

2. 在multiset中,元素的value也会识别它(因为multiset中本身存储的就是组成 的键值对,因此value本身就是key,key就是value,类型为T),multiset元素的值不能在容器中进行修改(因为元素总是const的),但可以从容器中插入或删除;

3. 在内部,multiset中的元素总是按照其内部比较规则(类型比较)所指示的特定严格弱排序准则进行排序;

4. multiset容器通过key访问单个元素的速度通常比unordered_multiset容器慢,但当使用迭代器遍历时会得到一个有序序列;

5. multiset底层结构为二叉搜索树(红黑树)。

注意:

1. multiset中再底层中存储的是的键值对;

2. mtltiset的插入接口中只需要插入即可;

3. 与set的区别是,multiset中的元素可以重复,set是中value是唯一的;

4. 使用迭代器对multiset中的元素进行遍历,可以得到有序的序列;

5. multiset中的元素不能修改;

6. 在multiset中找某个元素,时间复杂度为O(log_2 N);

7. multiset的作用:可以对元素进行排序。

3.3.2 multiset的使用

#include <set>
void TestSet()
{
  int array[] = { 2, 1, 3, 9, 6, 0, 5, 8, 4, 7 };
 
 // 注意:multiset在底层实际存储的是<int, int>的键值对
 multiset<int> s(array, array + sizeof(array)/sizeof(array[0]));
 for (auto& e : s)
 cout << e << " ";
 cout << endl;
 return 0;
}

3.4 multimap

3.4.1 multimap的介绍

multimap文档

1. Multimaps是关联式容器,它按照特定的顺序,存储由key和value映射成的键值对,其中多个键值对之间的key是可以重复的;

2. 在multimap中,通常按照key排序和惟一地标识元素,而映射的value存储与key关联的内 容。key和value的类型可能不同,通过multimap内部的成员类型value_type组合在一起, value_type是组合key和value的键值对: typedef pair value_type;

3. 在内部,multimap中的元素总是通过其内部比较对象,按照指定的特定严格弱排序标准对 key进行排序的;

4. multimap通过key访问单个元素的速度通常比unordered_multimap容器慢,但是使用迭代 器直接遍历multimap中的元素可以得到关于key有序的序列;

5. multimap在底层用二叉搜索树(红黑树)来实现。

注意:multimap和map的唯一不同就是:map中的key是唯一的,而multimap中key是可以重复的。

3.4.2 multimap的使用

multimap中的接口可以参考map,功能都是类似的。

注意:

1. multimap中的key是可以重复的;

2. multimap中的元素默认将key按照小于来比较;

3. multimap中没有重载operator[]操作(同学们可思考下为什么?);

4. 使用时与map包含的头文件相同。

3.5 在OJ中的使用

1.前K个高频单词

class Solution {
public:
    struct kvCom
    {
        bool operator()(const pair<string, int>& kv1, const pair<string, int>& kv2)
        {
            return kv1.second > kv2.second || (kv1.second == kv2.second && kv1.first < kv2.first);
        }
    };

    vector<string> topKFrequent(vector<string>& words, int k) 
    {
        map<string, int> m;
        for (auto& e : words)
        {
            m[e]++;
        }
        vector<pair<string, int>> v(m.begin(), m.end());
        sort(v.begin(), v.end(), kvCom());
        vector<string> ret;
        vector<pair<string, int>>::iterator it = v.begin();
        while (k--)
        {
            ret.push_back(it->first);
            it++;
        }
        return ret;
    }
};

2.两个数组的交集

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
   // 先去重
        set<int> s1;
        for(auto e : nums1)
       {
            s1.insert(e);
       }
        set<int> s2;
        for(auto e : nums2)
       {
            s2.insert(e);
       }
        
        // set排过序,依次比较,小的一定不是交集,相等的是交集
        auto it1 = s1.begin();
        auto it2 = s2.begin();
        vector<int> ret;
        while(it1 != s1.end() && it2 != s2.end())
       {
            if(*it1 < *it2)
           {
                it1++;
           }
            else if(*it2 < *it1)
           {
                it2++;
           }
            else
           {
                ret.push_back(*it1);
                it1++;
                it2++;
           }
       }
        return ret;
   }
};

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

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

相关文章

如何用Java语言实现远程控制多路照明

如何用Java语言实现远程控制多路照明呢&#xff1f; 本文描述了使用Java语言调用HTTP接口&#xff0c;实现控制多路照明。通过多路控制器&#xff0c;可独立远程控制多路照明。 可选用产品&#xff1a;可根据实际场景需求&#xff0c;选择对应的规格 序号设备名称1智能WiFi控…

hcia datacom课程学习(7):直连路由、静态路由

直连路由路由器接口上的网络&#xff08;接口配置了IP地址并且开启&#xff09;静态路由管理员手工添加的网络动态路由路由器之间动态学习形成的网络 1.直连路由 每当给路由器的一个接口配置了ip&#xff0c;路由表中就会产生对应的直连路由 配置路由接口ip的命令&#xff1…

编辑个人百度百科词条的小窍门,让你一次通过!

个人百度百科词条展示的是个人专业素养和成就&#xff0c;能够提升个人品牌形象&#xff0c;增加社交圈子中的竞争力。个人百度百科词条编辑攻略&#xff1a;如何通过审核&#xff1f;大家好&#xff01;今天伯乐网络传媒来分享一个超实用的经验——如何编辑个人百度百科词条才…

VM安装openwrt

openwrt是什么 OPENWRT是一种嵌入式操作系统&#xff0c;旨在取代原始路由器 固件 &#xff0c;并提供更多功能和自定义选项。 它构建在Linux内核之上&#xff0c;并由一系列 软件 包组成&#xff0c;以满足各种网络需求。 下载 官网下载地址&#xff1a;Index of /releases…

力扣-2259移除指定数字得到的最大结果

思路&#xff1a; 1. def removeDigit(self, number: str, digit: str) -> str:&#xff1a;这是一个类方法&#xff0c;接受两个参数 number 和 digit&#xff0c;分别表示输入的数字字符串和要移除的数字字符&#xff0c;返回一个字符串。 2. n len(number)&#xff1a…

内网穿透工具(zerotier, tailscale)

内网穿透工具 zerotier tailscale 一、Zerotier1. Zerotier 网页注册2. Zerotier 网页使用(1) Zerotier 网页配置 3. Zeritier 终端配置(1) Zeritier 终端下载(2) Zeritier 终端连接 二、Tailscale1. 进入主页2. Tailscale 网页注册登录3. Tailscale 网页添加设备4. Tailscale …

解决方案 SHUTDOWN_STATE xmlrpclib.py line: 794 ERROR: supervisor shutting down

Supervisor操作命令 重新加载 Supervisor 配置&#xff1a; sudo supervisorctl reread sudo supervisorctl update sudo supervisorctl restart all这将重新读取 Supervisor 的配置文件&#xff0c;更新进程组&#xff0c;然后重启所有进程。 查看 Supervisor 日志&#xff1…

【系统架构师】-选择题(一)

1、信息系统规划方法中&#xff0c;关键成功因素法通过对关键成功因素的识别&#xff0c;找出实现目标所需要的关键信息集合&#xff0c;从而确定系统开发的 &#xff08;优先次序&#xff09; 。关键成功因素来源于组织的目标&#xff0c;通过组织的目标分解和关键成功因素识别…

南京林业大学阮宏华团队揭示干旱对杨树人工林土壤微生物残留物碳的影响

全球强度干旱事件的频繁发生&#xff0c;对陆地生态系统土壤有机碳库可能产生显著影响。土壤微生物残体碳是土壤有机碳库的重要组成部分&#xff0c;微生物残体碳的变化对土壤有机碳库产生显著影响。然而&#xff0c;土壤中微生物残体碳对干旱的响应及其机制还不清楚。 基于此…

实现基于RAG的QA应用程序

实现基于RAG的Q&A应用程序 LLM 支持的最强大的应用程序之一是复杂的 问答 &#xff08;Q&A&#xff09; 聊天机器人。这些应用程序可以 回答有关特定来源信息的问题。这些应用程序 使用一种称为检索增强生成 &#xff08;RAG&#xff09; 的技术。 什么是检索增强生成…

TCP/IP常用协议栈图解

1.引言 最近看了一些计算机网络的课程&#xff0c;总结借鉴了一些TCP/IP常用协议&#xff0c;罗列在以下图中&#xff0c;以便有一个整体观。 2.图解 先上图 3.总结 TCP/IP协议是实际用的计算机网络通信的标准协议栈&#xff0c;自上而下分为应用层&#xff0c;传输层&#xf…

世界读书日 | 开发者必读书单重磅来袭,华为云DTSE专家天团力荐

春色恰如许&#xff0c;读书正当时。 读书&#xff0c;就像解锁一把神秘钥匙&#xff0c;为开发者洞开新世界的大门&#xff0c;赋予他们破译复杂难题的能力、挑战未知领域的勇气。书页翻动间&#xff0c;开发者得以站在巨人的肩膀上&#xff0c;汲取前人经验&#xff0c;积蓄…

BBS前后端混合项目--01

总路由 # urls.py """BBS1 URL ConfigurationThe urlpatterns list routes URLs to views. For more information please see:https://docs.djangoproject.com/en/3.2/topics/http/urls/ Examples: Function views1. Add an import: from my_app import views2…

AI测试干货!实例讲解AI自动生成测试用例

1.引言 在软件开发过程中&#xff0c;测试用例的生成是至关重要的一步。测试用例是用于验证软件功能、性能和安全性的具体测试方法&#xff0c;是确保软件质量的关键手段。 随着人工智能&#xff08;AI&#xff09;技术的发展&#xff0c;基于AI的测试用例生成技术逐渐成为未…

docker容器内彻底移除iptables服务的实现方法

背景 我创建的容器使用的是centos6的标准镜像&#xff0c;所以内置了iptables服务。容器启动后iptables服务默认就启动了。iptables设置的规则默认是所有流量都无法通行。而对于服务器的管理使用的是宿主机的防火墙。这样就导致在实现用iptables动态给容器添加端口映射时不成功…

Some App Tech Support

Getting Support: &#xfeff; mail: 842529824qq.com wx: g842529824 &#xfeff; or leave comment below.

研究发现:90%的Java服务容易第三方漏洞的影响

Datadog最新发布的“2024年DevSecOps状况”报告显示&#xff0c;Java服务在受Datadog最新发布的“2024年DevSecOps状况”报告显示&#xff0c;Java服务在受到第三方漏洞影响方面最为严重。 报告指出&#xff0c;90%的Java服务存在一个或多个严重或高危漏洞&#xff0c;这些漏…

Google Play App Store API 获取谷歌安卓应用商城app数据接口

iDataRiver平台 https://www.idatariver.com/zh-cn/ 提供开箱即用的谷歌安卓应用商城google play app store数据采集API&#xff0c;供用户按需调用。 接口使用详情请参考Google Play App Store接口文档 接口列表 1. 获取指定app的基础信息 参数类型是否必填默认值示例值描…

《剑指 Offer》专项突破版 - 面试题 113、114 和 115 : 详解拓扑排序(C++ 实现)

目录 前言 面试题 113 : 课程顺序 面试题 114 : 外星文字典 面试题 115 : 重建序列 前言 拓扑排序是指对一个有向无环图的节点进行排序之后得到的序列。如果存在一条从节点 A 指向节点 B 的边&#xff0c;那么在拓扑排序的序列中节点 A 出现在节点 B 的前面。一个有向无环…

javaweb-数据库

数据库管理系统&#xff08;DataBase Management System&#xff0c;简称DBMS&#xff09; MySQL 官网&#xff1a;MySQL :: Developer Zone 安装 官网下载地址&#xff1a;MySQL :: Download MySQL Community Server (Archived Versions) 图形化工具 通常为了提高开发效…