第10章 关联容器

本文详细介绍了C++ STL中的关联容器,包括map、set、multimap和multiset的使用方法,涵盖了pair类型介绍、map和set的基本操作、元素的添加删除及查找,以及在实际文本查询程序中的应用。

       关联容器支持通过键来高效地查找和读取元素。基本关联容器:map和set。set仅包含一个键。multimap和multiset支持同一个键多次出现在容器中。

10.1、引言:pair类型

       pair类型,定义在utility头文件中。创建和初始化如:pair<T1,T2> p1(v1,v2);或make_pair(v1,v2)赋给pair类型的变量。

       对于pair类型,也可考虑typedef简化其声明:如,typedef pair<string,string> Author;

       对于pair类,可以直接访问其数据成员:其成员都是公有的。

10.3、map类型

       在使用关联容器时,它的键不但有一个类型,而且还有一个相关的比较函数。默认情况下,标准库使用键类型定义的<操作符来实现键的比较。对于键类型,唯一的约束就是必须支持<操作符

       map<K,V>::value_type是存储元素的键以及值的pair类型,而且键为const(但它的值成员可以修改)。key_type是键类型;mapped_type是值类型;

       用下标访问不存在的元素将导致在map容器中添加一个新的元素,它的键即为该下标值,如果没给该元素赋值,则值进行默认初始化,如int则初始化为0

       注:与vector和string类型不同,map下标操作符返回一个mapped_type类型的值,而map迭代器返回value_type类型的值,包含const key_type和mapped_type类型成员的pair对象。

10.3.5、map::insert的使用

       map<string,int>word;

       word["hello"]++;

       pair<string,int> word2("hello",33);

       word.insert(word2);//这边word2没有插入成功,wordhello值还是1。即在插入时,如果键在m中已经存在,则保持m不变。该函数返回一个pair类型对象,包含指向键为e.first的元素的map迭代器,以及一个bool类型的对象,表示是否插入了该元素,如:pair<map<string,int>::iterator, bool> ret=函数返回。++ret.first->second实现该值递增,->优先级高于++。

       还有如:insert(beg,end);beg和end是标记元素范围的迭代器,即插入该范围内的所有元素,遵循前闭后开原则。

10.3.6、查找并读取map中的元素

       m.count(k):返回m中k的出现次数。它对于map键来说,0,1可以反应一个键是否存在。

       m.find(k):如果m容器中存在按k索引的元素,则返回指向该元素的迭代器。如果不存在,则返回超出末端迭代器。如用:find函数!= 容器.end()来判断。

10.3.7、从map对象中删除元素

       map容器的erase操作返回void。而顺序容器的erase操作则返回一个迭代器,指向被删除元素后面的元素。

       m.erase(b,e)从m中删除一段范围内的元素,遵循前闭后开原则。

10.3.8、map对象的迭代遍历

       map同样提供begin和end运算。

10.4、set类型

       以一段范围的元素初始化set对象,或在set对象中插入一组元素时,对于每个键,事实上都只添加了一个元素。

       set中添加元素:(1)insert一个元素;(2)调用insert时,提供一对迭代器实参,插入其标记范围内所有的元素。

       set中获取元素:(1)find运算,找到返回对应元素的迭代器,否则为end()迭代器。(2)count运算,返回1或0。

       set的键也是const,不能通过找到的迭代器修改键值

10.5、multimap和multiset类型

       multimap不支持下标运算(显然,某个键对应多个值),其余与map操作相同。

10.5.1、元素的添加和删除

       带有一个参数的erase版本将删除拥有该键的所有元素,并返回删除元素的个数。而带有一个或一对迭代器参数的版本只删除指定的元素,并返回void类型

10.5.2、在multimap和multiset中查找元素

       在multimap中,同一个键所关联的元素必然相邻存放

       案例:假设有作者与书名映射,希望找到并输出某个作者写的所有书的书名。方法一,使用find和count操作。首先调用count确定某作者所写的书籍数目,然后调用find获得指向第一个该键所关联的元素迭代器。方法二,用lower_bound(k)(返回一个迭代器,指向键不小于k的第一个元素)和upper_bound(k)(返回一个迭代器,指向键大于k的第一个元素)。如果该键没有关联的元素,则lower_bound和upper_bound返回相同的迭代器,都指向同一个元素或同时指向multimap的超出末端位置。它们都指向在保持容器元素顺序的前提下,该键应被插入的位置。equal_range(k)返回一个迭代器的pair对象,它的first成员等价于m.lower_bound(k),second成员等价于m.upper_bound(k)。

10.6、容器的综合应用:文本查询程序

       设计程序的一个良好习惯是首先将程序所涉及的操作列出来。明确需要提供的操作,有助于建立需要的数据结构和实现这些行为。(看似常识,有时却不会去执行···)

       问题:查询给出的单词所在的行号及每行内容

       方法:(1)使用vector<string>类型的对象存储整个文件的副本,文件的每行是该对象的一个元素。在输出某一行时,只需以行号为下标获取该行所在的元素即可。(2)将每个单词所在的行号存储在一个set容器对象中。(3)使用一个map容器将每个单词与一个set容器对象关联起来,该set容器对象记录此单词所在的行号。具体就是对每行的每个单词,用map[word].insert(行号);即可。
<think>我们正在讨论序列容器和关联容器是否为同一概念。根据引用[1][2][3][4]的内容,我们可以总结如下:1.**序列容器(Sequence Containers)**:-按照线性顺序存储元素(引用[2]提到"序列容器是一线性结构")-元素顺序由插入顺序决定(引用[2]:"可以按照元素插入的顺序进行访问和遍历")-典型代表:`vector`,`deque`,`list`, `forward_list`, `array`(引用[2])-支持在特定位置插入/删除元素(如前端、后端、任意位置)2.**关联容器(AssociativeContainers)**:-通过关键字(key)而非位置索引来访问元素(引用[4]中提到的`set`和`map`等)-元素通常按关键字排序(有序关联容器)或通过哈希组织(无序关联容器)-典型代表:`set`,`map`, `multiset`, `multimap`(有序),以及`unordered_set`, `unordered_map`等(无序) **核心区别**: -**存储方式**:序列容器按物理顺序存储,关联容器按逻辑关系(关键字)存储。- **访问方式**:序列容器通过位置(下标/迭代器)访问,关联容器通过关键字访问。 -**内在结构**:序列容器通常为线性结构(数组/链表),关联容器通常为树结构(红黑树)或哈希表。 **结论**:序列容器和关联容器**不是同一概念**,它们是C++标准库中两不同的容器分。###详细对比表|特性|序列容器|关联容器||--------------|--------------------------|--------------------------| |**元素顺序**|插入顺序决定|关键字排序决定(有序)| |**访问方式**|位置索引/迭代器|关键字查找|| **底层结构** |数组/链表/双端队列|红黑树/哈希表|| **典型操作** |`push_back()`, `pop_front()` |`find()`, `insert(key)` || **时间复杂度** |随机访问:$O(1)$(`vector`)<br>插入删除:$O(n)$(平均)|查找:$O(\logn)$(有序)<br>或$O(1)$(哈希)| |**典型应用**|需要顺序处理的场景|需要快速查找/去重的场景|###示例说明**序列容器(`deque`)**(引用[3]): ```cppstd::deque<int>dq; dq.push_back(10);//尾部插入dq.push_front(5);//头部插入intfront =dq.front();//访问头部元素``` **关联容器(`set`)**(引用[4]): ```cppstd::set<std::string> names; names.insert("Alice");//按字符串排序插入if(names.find("Bob")!= names.end()){//通过关键字查找}``` ###总结序列容器和关联容器在C++中解决不同场景的问题:-当需要**保持元素插入顺序**时(如队列、列表),使用序列容器。 -当需要**高效按键查找/去重**时(如字典、集合),使用关联容器。---### §§相关问题 §§1.如何选择使用`vector`还是`list`作为序列容器? 2.关联容器的红黑树实现有哪些性能优势?3.无序关联容器(如`unordered_map`)的哈希冲突如何处理? 4.序列容器能否实现关联容器的按键查找功能?</think>### 序列容器 vs 关联容器:本质区别分析 序列容器和关联容器**不是同一概念**,它们是 C++ 标准库中两完全不同的容器型。以下是核心区别: | **特性** | **序列容器** | **关联容器** | |------------------|------------------------------------------|------------------------------------------| | **元素组织方式** | 按物理插入顺序存储(线性结构)[^2] | 按逻辑关系存储(关键字索引)[^4] | | **访问方式** | 通过位置索引(下标/迭代器)访问 | 通过关键字(key)访问 | | **底层实现** | 数组/链表等线性结构(如 vector, list) | 树结构/哈希表(如红黑树、哈希桶)[^4] | | **排序特性** | 保持插入顺序 | 自动按键排序(有序容器)或无特定顺序(无序容器) | | **典型操作** | `push_back()`, `pop_front()`, `front()`[^3] | `find()`, `insert(key)`, `equal_range()` | | **时间复杂度** | 随机访问:$O(1)$(vector)<br>插入删除:$O(n)$ | 查找:$O(\log n)$(有序)<br>或 $O(1)$(哈希) | #### 关键差异说明: 1. **存储逻辑不同** - 序列容器(如 vector)存储元素时**严格保留插入顺序** ```cpp std::vector<int> vec{3,1,2}; // 顺序始终为 [3,1,2] ``` - 关联容器(如 set)存储时**自动按键排序** ```cpp std::set<int> s{3,1,2}; // 自动排序为 {1,2,3} ``` 2. **访问机制本质区别** - 序列容器依赖**物理位置** ```cpp std::cout << vec[0]; // 输出第一个插入的元素 3 ``` - 关联容器依赖**逻辑关键字** ```cpp std::cout << s.find(2); // 直接查找关键字 2 ``` 3. **底层数据结构** - 序列容器使用连续内存(array/vector)或链表(list)[^2] - 关联容器使用平衡树(set/map)或哈希表(unordered_set)[^4] #### 典型应用场景对比: - **序列容器适用场景** - 需要保持元素插入顺序(如日志记录) - 频繁在首尾增删(deque 满足 `push_back`/`pop_front`[^3]) - 随机访问需求(vector) - **关联容器适用场景** - 快速关键字查找(字典应用) - 自动去重和排序(如词频统计) - 范围查询(如查找 10~20 分的学生) > 引用[1]和[3]中提到的 `queuedeque` 和 `list` 属于序列容器,而引用[4]描述的 `set` 属于关联容器,这进一步证明它们是不同分。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值