C++关联容器

本文详细介绍了C++关联容器,包括map和set的基本概念、定义、操作以及无序容器的使用。关联容器根据关键字存储和访问元素,提供插入、删除、查找等操作。无序容器使用哈希表管理元素,适用于无序场景。文章强调了关键字类型的要求,如自定义比较操作和哈希函数,以及在关联容器中的操作特性,如map的下标操作和迭代器的使用。

1. 关联容器概述

1.1 关联容器类型

关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的。两个主要的关联容器类型是mapset。map 中的元素是一些关键字—值对,关键字起到索引的作用,值是与关键字相关联的数据。set 中每个元素为一个关键字,set 支持高效的关键字查询操作。
标准库提供8个关联容器:

容器类型 含义 容器类型 含义
map 关联数组,保存关键字—值对 unordered_map 用哈希函数组织的 map
set 关键字即值,即只保存关键字的容器 unordered_set 用哈希函数组织的 set
multimap 关键字可重复出现的 map unordered_multimap 哈希组织的 map,关键字可重复出现
multiset 关键字可重复出现的 set unordered_multiset 哈希组织的 set,关键字可重复出现

类型 map 和 multimap 定义在头文件 map 中,set 和 multiset 定义在头文件 set 中,无序容器则分别定义在相关的头文件 unordered_mapunordered_set 中。

1.2 使用关联容器

虽然大多数人熟悉诸如 vector 和 list 这样的数据结构,但从未使用过关联结构。在学习标准库关联容器类型之前,我们首先来看一个如何使用这类容器的例子。
1.使用map

map <string, size_t> word_count;
string word;
while(cin >> word)
      ++ word_count[word];
for(const auto & w : word_count)
    cout << w.first << "occurs" << w.second <<((w.second>1) ? "times" : "time") << endl;

2.使用set

map <string,size_t> word_count;
set<string>exclude = {
   
   "the","but","and","or"};
string word;
while(cin >> word){
   
   
    // 只统计不在exclude中的单词
     if(exclude.find(word) == exclude.end())
        ++ word_count[word];
}

2. 关联容器定义

关联容器不支持顺序容器的位置相关操作,例如push_frontpush_back。原因是关联容器中的元素是根据关键字存储的,这些操作对关联容器没有意义。而且,关联容器也不支持构造函数或插入操作这些接受一个元素值和一个数量值的操作。

2.1 定义关联容器

当定义一个map时,必须即指明关键字类型又指明值类型,而定义一个set时,只需要指明关键字类型,因为 set 中没有值。每个关联容器都定义一个默认构造函数,它创建一个指定类型的空容器,我们可以将关联容器初始化为另一个同类型容器的拷贝,或是从一个值范围来初始化关联容器。只要这些值可以转化为容器所需类型就可以。

set<string> exclude = {
   
   "the","but","and","or"};
map<string, string>authors = {
   
   
       {
   
   "john","jame"},
       {
   
   "aus","jane"},
       {
   
   "dick","chars"}
 };

1.初始化multimap或multiset
一个 map 或 set 中的关键字必须是唯一的,即一个容器内关键字不会重复。容器multimapmultiset没有此限制,它们都允许多个值对应相同的关键字。如下所示:

vector<int> ivec;
for(vector<int>::size_type i = 0; i != 10; ++i){
   
   
  ivec.push_back(i);
  ivec.push_back(i);
}
set<int> iset(ivec.cbegin(), ivec.cend());
multiset<int> miset(ivec.cbegin(), ivec.cend());
cout << ivec.size() << endl;    // 打印出20
cout << iset.size() << endl;    // 打印出10
cout << miset.size() << endl;   // 打印出20

2.2 关键字类型的要求

关联容器对于包含的关键字类型有一些限制,对于有序容器 map、set 和 multimap、multiset,关键字类型必须定义元素比较的方法。默认情况下,标准库使用关键字类型的小于运算符来比较两个关键字。在 set 集合中,关键字类型就是元素类型;在 map 中,关键字类型是元素的第一部分的类型。
1.有序容器的关键字类型
可以向一个算法提供我们自定义的比较操作,类似的也可以提供自己定义的操作来替代关键字上的小于运算符。实际定义的操作可能是一个复杂的函数,但无论我们怎么定义比较函数,它必须具备如下性质:
1.两个关键字不能同时小于等于对方;如果 K1 小于等于 K2,那么 k2 绝不能小于等于 k1;
2.如果 k1 小于等于 k2,且 k2 小于等于 k3,那么 k1 必须小于等于 k3;
3.如果存在两个关键字,任何一个都不小于等于另一个,那么我们称这两个关键字是等价的。
2.使用关键字类型的比较函数
一个容器中元素的操作类型也是该容器类型的一部分。为了指定使用自定义的操作,必须在定义关联容器类型时提供此操作的类型。自定义的操作类型在尖括号中紧跟着元素类型给出。

#include <iostream>
#include <set>
// 比较函数,按照元素的绝对值大小进行比较
bool compareAbs(int a, int b) {
   
   
    return std::abs(a) < std::abs(b);
int main() {
   
   
    // 使用函数指针作为比较函数
    std::set<int, bool(*)(int, int)> mySet(compareAbs);
    mySet.insert(-5);
    mySet.insert(3);
    mySet.insert(-2);
    mySet.insert(7);
    // 输出容器中的元素,按照绝对值从小到大排序
    for (int num : mySet) {
   
   
        std::cout << num << " ";
    }
    return 0;
}

2.3 pair类型

pair 是 C++ 标准库中定义的一个模板类,用于存储一对值,即键值对,通常用于关联容器的元素类型。pair 提供了两个公共成员变量 first 和 second,用来存储两个值。使用make_pair 需要添加头文件utility

#include <iostream>
#include <utility>
int main() {
   
   
    // 创建一个 std::pair 对象
    std::pair<int, double> myPair(
C++中的关联容器是一类用于存储和管理键值对(Key-Value Pairs)的数据结构,它们提供了高效的查找、插入和删除操作。关联容器基于二叉搜索树或哈希表实现,因此在查找时具有对数时间复杂度(如`map`、`set`)或常数时间复杂度(如`unordered_map`、`unordered_set`)。 ### 关联容器的类型 C++标准库提供了以下主要的关联容器: 1. **`set`** - 存储唯一的键(key),键值本身即为元素。 - 内部结构基于红黑树,键默认按升序排列。 - 键是常量(`const`),不可修改,只能删除后重新插入。 - 示例: ```cpp std::set<int> s; s.insert(10); s.insert(20); ``` 2. **`multiset`** - 与`set`类似,但允许重复键值。 - 示例: ```cpp std::multiset<int> ms; ms.insert(10); ms.insert(10); // 允许重复插入 ``` 3. **`map`** - 存储键值对(`pair<const Key, T>`),键唯一。 - 值通过键进行快速查找。 - 键是常量,但值可修改。 - 示例: ```cpp std::map<std::string, int> m; m["apple"] = 5; m.insert(std::make_pair("banana", 3)); ``` 4. **`multimap`** - 与`map`类似,但允许重复键。 - 示例: ```cpp std::multimap<int, std::string> mm; mm.insert({1, "one"}); mm.insert({1, "uno"}); // 允许重复键 ``` 5. **`unordered_set`** 和 **`unordered_multiset`** - 使用哈希表实现,不保证元素顺序。 - 插入和查找操作平均为常数时间复杂度。 - `unordered_set`不允许重复键,`unordered_multiset`允许。 6. **`unordered_map`** 和 **`unordered_multimap`** - 类似于`map`和`multimap`,但基于哈希表实现。 - 示例: ```cpp std::unordered_map<std::string, int> um; um["apple"] = 5; ``` ### 关联容器的基本操作 - **插入元素** 使用`insert()`方法插入元素,对于`map`也可以使用`operator[]`直接赋值[^3]。 ```cpp std::map<int, std::string> m; m.insert(std::make_pair(1, "one")); m[2] = "two"; // 更简洁的方式 ``` - **查找元素** 使用`find()`方法查找指定键,返回迭代器。 ```cpp auto it = m.find(2); if (it != m.end()) { std::cout << it->second << std::endl; // 输出 "two" } ``` - **删除元素** 使用`erase()`方法删除指定键或迭代器位置的元素。 ```cpp m.erase(2); // 删除键为2的元素 ``` - **遍历元素** 使用迭代器遍历容器,注意关联容器的迭代器是按关键字升序排列的[^2]。 ```cpp for (const auto& kv : m) { std::cout << kv.first << ": " << kv.second << std::endl; } ``` ### 关联容器的特性 - **键的不可变性** 在`map`、`set`等容器中,键是`const`类型,不能被修改,只能通过删除后重新插入来更新键值。 - **排序行为** `map`、`set`等容器默认使用`<`运算符进行排序,可以通过自定义比较器来改变排序规则。 - **效率** - `set`、`map`等基于红黑树实现,查找、插入、删除操作的时间复杂度为`O(log n)`。 - `unordered_set`、`unordered_map`基于哈希表实现,平均时间复杂度为`O(1)`。 ### 示例代码:使用`map`存储和查找数据 ```cpp #include <iostream> #include <map> #include <string> int main() { std::map<std::string, int> word_count; // 插入数据 word_count["apple"] = 3; word_count["banana"] = 2; // 查找数据 auto it = word_count.find("apple"); if (it != word_count.end()) { std::cout << "apple count: " << it->second << std::endl; } // 遍历数据 for (const auto& pair : word_count) { std::cout << pair.first << ": " << pair.second << std::endl; } return 0; } ```
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Litle_Pudding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值