C++ 中 map 字典与 set 集合的使用

在 C++ 中,map 是关联容器的一种,关联容器将关联到一起,并使用键来查找值。这与 python 中的字典类型类似,也是用来存储键-值对(key-value) 形式的数据的,其同样具有遍历、插入、查询、删除等功能;set 与 map 类似,set 是一种只有键的集合,下面将逐一介绍。

目录

1、map 类型的声明

2、pair 类型

3、map 数据的遍历

4、添加元素

5、删除元素

6、访问/查询元素

7、set 类型

8、获取关联容器的大小

9、map 与 set 都是有序容器


1、map 类型的声明

若想在 C++ 中使用 map 类型创建字典,则需要包含 map 头文件,如下:

#include <map>

map 类型的定义如下:

map<KeyType, ValueType> dict;

其中,KeyType 代表的数据类型,ValueType 代表的数据类型。KeyType 默认是 const 类型,即键在确认之后为只读数据。另外,在一个 map 数据中,一个键只能出现一次;而在 multimap 类型数据中,一个键可以出现多次,本次主要讨论 map 数据。看下面一个例子:

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

using namespace std;
int main(void)
{
    map<string, int> dict{{"key1", 2}};  // 定义一个 map 类型数据 
    dict["key2"] = 3;  // 创建并赋值
    dict["key3"] ;
    cout << "length of dict: " << dict.size() << endl;  // 长度
    cout << dict["key1"] << endl;  // 查找
    cout << dict["key2"] << endl;
    cout << dict["key3"] << endl;  // 值默认为0 
}

//======= 运行结果如下 ========//
length of dict: 2
2
3
0

例子中首先声明了一个 map 类型数据 dict ,key 的数据类型为 string ,value 的数据类型为 int。初始化时注意一对键值对要放在一对花括号{}中,并用逗号隔开:

{key, value}

如果键 key 对还未出现在 dict 中,下标运算会创建一个新元素,其值默认为 0 。这里创建了三个键值对,一个key 为 字符串 "key1" ,值为 2;key 为 字符串 "key3" 的键值对由于未被赋值,因此其值默认为 0 。

2、pair 类型

上述 dict 字典中存储的每一个数据都是 pair 类型的数据,即 map 数据是一群 pair 类型的集合。一个 pair 保存两数据成员: first 和 second,分别用来存储一个键值对的 ,其中 first 成员是 const 的,这也解释了上面提到的 键在确认之后为只读数据。pair 的标准库类型定义在头文件 utility 中,在定义一个 pair 时,其默认构造函数会对数据成员进行初始化。一个例子如下:

int main(void)
{
    pair<string, int> a_pair{"key", 1};  // 定义一个 pair 并初始化
    cout << a_pair.first << ": " << a_pair.second << endl;
}

// =========== 运行结果 =========== //
key: 1

3、map 数据的遍历

map 数据的遍历主要有两种方法,一个是直接遍历,另一种是使用迭代器的遍历。

1)直接遍历:

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

using namespace std;
int main(void)
{
    map<string, int> dict;  // 定义一个 map 类型数据
    dict["key1"] = 2;  // 创建并赋值
    dict["key2"] = 3;  // 
    dict["key3"] ;     // 默认值为0
    for(auto c : dict)  // 遍历,c++11 特有循环方式
    {
        cout << c.first << ": " << c.second << endl;
    }
}
//============ 运行结果 ============//
key1: 2
key2: 3
key3: 0

这种遍历方式就是 for 循环直接遍历。每次循环都会从 dict 中提取一个 pair 类型的对象存入 c 中以供后续操作。

2)使用迭代器遍历

map 支持 vector 中的 begin、end、cbegin、cend 操作,因此我们可以使用这些函数获取迭代器,然后使用迭代器来遍历 map。begin、end 指向首元素以及尾元素之后位置(one past the last element)的迭代器,而容器中的元素位于 begin 与 end 之间,不包含 end,即是一个左闭右开的区间:[begin,end)。cbegin、cend 是C++11引入的新标准,返回 const_iterator,只能用来读取迭代器所指向内容的数据,不能修改数据 。一个使用迭代器遍历的例子如下:

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

using namespace std;

int main(void)
{   
    // 定义并初始化一个 map 类型数据 
    map<string, int> dict{{"key1", 1}, {"key2", 2}, {"key3", 3}};  
    auto map_it = dict.begin();  // 获取指向首元素的迭代器
    // 判断范围,比较当前迭代器和尾后元素迭代器
    while (map_it != dict.end())
    {
        cout << map_it->first << ": " << map_it->second << endl;
        map_it++;  // 迭代器递增,移动到下一个元素
    }
}

4、添加元素

map 的 insert 成员函数用来添加一个元素或者一个元素范围,emplace 成员函数用来添加单个元素。使用 insert 方法向 map 中添加元素的方法如下,不过常用的方式就是第一种方式,剩下的仨了解下就行。

dict.insert({"key", 1})    // 或者 dict.emplace({"key", 1})
dict.insert(make_pair("key", 1))
dict.insert(pair<string, int>("key", 1))
dict.insert(map<string, int>::value_type("key", 1))

另外对于一个不存在的键的单一元素的 insert 或者 emplace 的插入操作会返回一个 pair ,告诉我们是否添加成功,该 pair 的 first 成员是一个迭代器,指向具有给定关键字的元素,second 成员是一个 bool 值,指出元素是插入成功还是已存在于 dict 中,如果之前不存在,则返回true;如果先前已存在,则值为 false。

int main(void)
{
    map<string, int> dict{{"key1", 1}};  // 定义一个 map
    auto ret1 = dict.insert({"key2", 1}); // 执行插入操作,若 "key"不存在于dict中,则返回
    cout << "attention: ret1.first is a iterator points the element we insert" << endl;
    cout << ret1.first->first << ":" << ret1.first->second << endl;
    cout << "key2 insert successfully? " << ret1.second << endl;
    auto ret2 = dict.insert({"key1", 3}); //  "key"已存在于dict中,则insert什么也不做
    cout << "attention: ret2.first is a iterator points the element we insert" << endl;
    cout << ret2.first->first << ":" << ret2.first->second << endl;
    cout << "key1 insert successfully? " << ret2.second << endl;
}
//========= 运行结果 ==========//
attention: ret1.first is a iterator points the element we insert
key2:1
key2 insert successfully? 1
attention: ret2.first is a iterator points the element we insert
key1:1
key1 insert successfully? 0

例子中的返回值 pair 的完整类型如下:

pair<map<string, int>::iterator, bool>

但是!上面的 insert 方式还是麻烦了点,不如直接使用文章开头说的下标操作如果键 key 对还未出现在 dict 中,下标运算会创建一个新元素,其值默认为 0

当插入一个元素范围时,可以做如下操作,这时没有返回值。

dict.insert(dict2.begin(), dict2.end());

5、删除元素

map 有三个版本的 erase 操作,分别如下:

1、dict.erase(key)

从 dict 中删除 键 为 key 的元素,返回删除元素的数量,对于 map 类型来说为 1。

2、dict.erase(p)

从 dict 中删除迭代器 p 指定的元素。p 必须指向 dict 中一个真实元素且不能等于 dict.end()。返回一个指向 p 之后的元素的迭代器。

3、dict.erase(b, e)

从 dict 中删除迭代器 b 和 e 之间的元素,返回e。

int main(void)
{
    // 定义并初始化一个 map 类型数据 
    map<string, int> dict{{"key1", 1}, {"key2", 2}, {"key3", 3}, {"key4", 4}, {"key5", 5}};  
    int ret1 = dict.erase("key1");  // 删除键为 "key1" 的元素
    cout << "ret1: " << ret1 << endl;
    auto ret2 = dict.erase(dict.begin());  // 使用迭代器
    cout << "(ret2->first:ret2->second) = " << ret2->first << ":" << ret2->second << endl;
    auto ret3 = dict.erase(dict.begin(), dict.end());  // 清空
    cout << "ret3->first: " << ret3->first << endl;
}
//=========== 运行结果 ============//
ret1: 1
(ret2->first:ret2->second) = key3:3
ret3->first:

6、访问/查询元素

map 提供了一些查找元素的操作如下:

1、dict.find(key)
// 返回一个迭代器 ,指向第一个键为 key 的元素,若 key 不存在于 dict 中,则返回 dict.end()
2、dict.count(key)
// 返回键为 key 的元素的数量,对于 map 来说,返回1
3、dict.lower_bound(key)
// 返回一个迭代器,指向第一个键不小于 key 的元素
4、dict.upper_bound(key)
// 返回一个迭代器,指向第一个键大于 key 的元素
5、dict.equal_range(key)
// 返回一个迭代器pair,表示关键字恩于 key 的元素的范围。如果不存在,则pair的两个成员均为
//dict.end()
6、dict.at(key)
// 返回key对应的引用,如果 key 不存在,则会抛出异常
// 另外,对于 const map 的查找只能用 at, 用[]会抛出异常
7、dict[key]
// 使用下标操作访问元素,如果该元素并不存在于 dict 中,则该操作会创建一个元素,
// 并赋初始值为0或空字符,初始值取决于具体的数据类型。

一般来说,2~5的方法多用于 multimap 类型的数据中。如果只是想查询一个元素是否存在于 dict 中,则使用 find 是最好的选择。

另外需要注意的是,如果使用下标操作dict[key]访问元素,如果该元素并不存在于 dict 中,则该操作会创建一个元素,并赋初始值为0或空字符,初始值取决于具体的数据类型。

与下标[ ]访问不同的是,dict.at(key) 操作返回 key 对应的 value 的引用,如果 key 不存在,则会抛出异常。另外,对于 const map 的查找只能用 at, 用[ ]会编译出错

因此,使用什么样的访问方式需要根据我们的使用环境进行选择。

7、set 类型

set 顾名思义就是集合,即每个元素只包含一个关键字,而不是键值对,set 定义在头文件 <set> 中。定义如下:

set<string> mySet = {"this", "is", "a", "set"};

与 map 类似,set 也有 find()、count()、insert()、emplace() 以及迭代器等操作方式。

set的迭代器只能是只读类型的,遍历方法如下:

set<int> iset={0, 1, 2, 3, 4, 5, 6};
auto set_it = iset.begin();
for(; set_it != iset.end(); ++set_it)
    cout << *set_it << endl;
"""
输出:
0
1
2
3
4
5
6
"""

8、获取关联容器的大小

map 和 set 类型的元素个数都可以使用 size() 方法获取。

    // 定义并初始化一个 map 类型数据 
    map<string, int> dict{{"key1", 1}, {"key2", 2}, {"key3", 3}}; 
    set<string> mySet = {"this", "is", "a", "set"}; 
    auto map_it = dict.begin();  // 获取指向首元素的迭代器
    cout << dict.size() << endl;  // 输出 3
    cout << mySet.size() << endl;  // 输出 4

9、map 与 set 都是有序容器

set 和 map 是基于的结构的,并且内部元素是有序的,即是经过排序的:如果键的类型是数字,则按照数字大小进行排序,如果是 string,则按照字典序进行排序。

  • 12
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

地球被支点撬走啦

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

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

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

打赏作者

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

抵扣说明:

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

余额充值