C++(标准库):19---STL容器之(关联式容器map、multimap)

一、关联式容器概述

二、map

格式

  • map<key,value>:由“键值(key)与值(value)”两部分组成,这两者形成映射关系
  • 头文件:#include <map>

特点

  • key是唯一的,不可重复;但value可以重复出现
  • key与value必须具有可赋值、可拷贝的性质
  • key的数据类型必须是可比较的,根据key来进行排序(默认为升序)
  • key与value可以是任何类型:int、doule、字符串、结构体、类......
  • map的每一组key与value相当于一个pair容器对象(pair容器,见文章:https://blog.csdn.net/qq_41453285/article/details/100628402

key的比较操作

  • 我们知道key必须是可以比较的,如果key的类型不能进行比较(例如“<”运算符进行比较),则程序出错
  • 如果使我们自定义的类,如果没有提供比较运算符的重载,那么就不能作为关联容器的key使用,因为自定义的类不能进行比较
  • 如果key可以进行比较,且遵循下面图片中的规则:

升序、降序操作

  • map的key默认为升序排序,但是我们也可以通过一些操作来实现降序操作,或者显式的进行升序操作
  • 下面两个标准库函数(less、greater)都在头文件#include <xfunctional>中
  • 下面我们先演示默认的升序排序
map<string, int,std::less<string>> word_count;//显式地升序
//map<string, int,> 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;
}

  • 演示降序排序:
map<string, int,std::greater<string>> 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;
}

初始化

  • 如果没有给出初始化值,就默认为空容器(使用系统的默认值初始化)
  • 手动给出值初始化

下标运算符和at函数

  • map和unordered_map提供下标操作,因为其key唯一;multimap与unordered_multimap不提供下标操作,因为其key不唯一
  • 下标运算符可以用来创建一个键值对或访问键所对应的值
  • 由于下标运算符可能插入一个新元素,我们只可以对非const的map使用下表操作
  • 下标运算符则:

  • 演示案例:
map<string, size_t> word_count;
word_count["a"] = 1; //添加key与value对
word_count["b"] = 2; //添加key与value对
word_count["c"] = 3; //添加key与value对

word_count["a"] = 4; //更改key对应的value
cout << word_count["a"] << endl; //打印4
cout << word_count.at("a") << endl; //打印4

迭代器与遍历

  • 当解引用一个关联容器迭代器时,我们得到的是容器的value_type的值的引用
  • 对于map来说,value_type为一个pair类型,因此得到pair类型之后,我们就可以调用first和second来访问key与value
  • 重点:得到的迭代器,我们可以改变其second(value)的值,但不能改变其first(key)的值(因为为const类型)
map<string, int> word_count;
word_count["Hello"] = 3;
word_count["World"] = 3;

auto map_it = word_count.begin(); //得到第一个元素的迭代器
//*map_it是指向一个pair<const string,size_t>的引用
cout << map_it->first << endl;;  //打印Hello
cout << map_it->second << endl;  //打印3

map_it->first = "new key";//错误,key关键字不可以改变(为const类型)
++map_it->second; //正确,key关键字对应的value可以改变
  • 通过迭代器遍历容器:可以使用iterator或者const_iterator类型的迭代器遍历
map<string, int> word_count;
	word_count["Hello"] = 3;
	word_count["World"] = 3;

	//map_it为map<string, int>::const_iterator类型
	auto map_it = word_count.cbegin();
	while (map_it != word_count.cend()) {
		cout << map_it->first << "" << map_it->second << endl;
		++map_it;
	}

添加元素(insert、emplace)

  • 这里介绍的添加元素方式,适合所有关联容器,后面不再介绍

  • 对于key唯一的,插入时会先判断key是否存在,不存在则插入;存在则什么都不做
  • 对于key不唯一的,每次插入都插入新值

insert有3个版本:

  • 参数为value_type或参数为一对迭代器或者参数为一个初始化列表

演示set的插入

vector<int> ivec = { 2,4,6,8,10 };
set<int> set1;
set1.insert(ivec.cbegin(), ivec.cend()); //迭代器版本

set1.insert({ 1,3,5,7,9 }); //初始化器版本插入

map的插入

  • 由于map的key是pair容器类型,因此map的插入有一些特殊情况
map<string, size_t> word_count;
word_count.insert({ "a",1 });
word_count.insert(make_pair("a",1));
word_count.insert(pair<string,size_t>("a", 1));
word_count.insert(map<string,size_t>::value_type("a", 1));

insert/emplace的返回值

//案例:单词计数

map<string, size_t> word_count; //空的
string word;
while (cin >> word) {
    //插入一个元素,关键字等于word,值为1
    //若word已经存在,insert什么都不做
    auto ret = word_count.insert({ word,1 });
    if (!ret.second) //检查返回值的bool部分,若为false,则word已在word_count中
        ++ret.first->second; //递增计数器
}

展开递增语句 

删除元素(erase)

  • 关联容器定义了3个版本的erase:
    • ①传递一个key_type参数。如果元素存在(删除所有匹配给定关键字的元素,返回实际删除的元素数量);如果元素不存在(函数返回0)
    • ②传递给erase一个迭代器来删除一个元素,函数返回void(与顺序容器类似)
    • ③传递给erase一个迭代器对来删除一个元素范围,函数返回void(与顺序容器类似)

  • 演示案例

map<string, size_t> word_count;
word_count["a"] = 1;
word_count["b"] = 2;
word_count["c"] = 3;

string removal_word = "a";
if (word_count.erase(removal_word))
    cout << "ok:"<< removal_word <<"  removed" <<endl;
else
    cout << "error:" << removal_word << "  not found" << endl;

其他操作

  • 其他关联容器都可以参数这里的知识点

对map使用find代替下标操作

  • 如果使用下标运算符来查找map、unordered_map中的一个关键字,会有一个缺点,就是如果这个关键字不存在,则会插入这个关键字。因此想要寻找一个关键字是否存在map中,最好使用find函数
map<string, size_t> word_count; //空的
//如果不存在,返回map的尾后迭代器
if (word_count.find("abcd") == word_count.end())
    cout <<"abcd is not in the map" << endl;

在multimap、multiset中查找元素

  • multimap、multiset中的key可以重复,并且相同的key会相邻存储,因此如果想要查看相同的key键值,可以使用count和find函数配合查看
multimap<string, string> author; //空的
author.insert({ "C Primer","Bob" });
author.insert({ "C++ Primer","Tom" });
author.insert({ "C++ Primer","Alice" });
author.insert({ "Java","Alan" });

auto entried = author.count("C++ Primer"); //计算出键为“C++ Primer”的数量
auto iter= author.find("C++ Primer");//先找到第一个迭代器位置
while (entried) { //循环遍历
    cout << iter->second<< endl;
    ++iter;
    --entried;
}

lower_bound、upper_bound函数

  • 在key可以重复的容器中,lower_bound返回这个key所对应的第一个位置,upper_bound返回最后一个匹配给定关键字的元素的下一个位置
  • 如果key不存在,lower_bound和upper_bound返回相等的迭代器——指向一个不影响排序的关键字插入位置

  • 根据上面的图片可知,如果key不存在容器中,且大于所有的关键字,则lower_bound返回的就是尾后迭代器
  • 这两个函数可以实现上面在multimap、multiset中查找重复的key。见下面案例
multimap<string, string> author; //空的
author.insert({ "C Primer","Bob" });
author.insert({ "C++ Primer","Tom" });
author.insert({ "C++ Primer","Alice" });
author.insert({ "Java","Alan" });

//beg指向于key为"C++ Primer"的第一个位置,end指向于最后一个位置的下一个位置
for (auto beg = author.lower_bound("C++ Primer"),
    end = author.upper_bound("C++ Primer");
    beg != end; ++beg)
{
    cout << beg->second << endl;
}

equal_range函数

  • 该函数接受一个关键字,然后一个参数为迭代器的pair容器类型
  • 若关键字存在,则pair容器中的第一个迭代器指向于第一个与关键字匹配的元素,第二个迭代器指向最后一个匹配元素之后的位置;若不存在关键字,两个迭代器都指向关键字可以插入的位置
  • 使用来函数也可以实现上面find、lower_bound等函数来遍历相同的key的操作,见下面演示案例
multimap<string, string> author; //空的
author.insert({ "C Primer","Bob" });
author.insert({ "C++ Primer","Tom" });
author.insert({ "C++ Primer","Alice" });
author.insert({ "Java","Alan" });

for (auto pos = author.equal_range("C++ Primer");
pos.first != pos.second; ++pos.first)
{
    cout <<pos.first->second << endl;
}

/*pos为pair<iterator1,iterator2>类型,
iterator1为指向于第一个"C++ Primer"的迭代器,
iterator2指向于最后一个"C++ Primer"的后一个位置*/

演示案例

  •  利用map来输入保存的字符串,然后输出字符串出现的次数
#include <iostream>
#include <map>
#include <string>

using namespace std;

int main()
{
    //key为string类型,value为size_类型
    map<string, size_t> word_count;
    string word;
    while (cin >> word) {
        ++word_count[word];
    }

    for (const auto &w : word_count) {
        cout <<w.first <<":"<<w.second<<((w.second>1)?"times":"time")<< endl;
    }
    return 0;
}

三、multimap

格式

  • 与map相同:由“键值(key)与值(value)”两部分组成,这两者形成映射关系
  • 头文件:#include <map>

特点

  • key可以重复(如果有相同的key,则相邻存储),默认也为升序排序
  • 其它特点与map相同
  • multimap不提供下标操作(下标运算符和at函数),因为key可以重复

升序、降序操作

  • 与map相同,key默认是升序操作,我们也可以将key设置为显式地升序或者设置为降序排序
  • 下面两个标准库函数(less、greater)都在头文件#include <xfunctional>中
  • 演示升序操作
multimap<string, int, std::less<string>> word_count;//升序
//multimap<string, int> word_count;//升序

word_count.insert({ "a",1 });
word_count.insert({ "c",1 });
word_count.insert({ "b",1 });

for (const auto &w : word_count) {
    cout << w.first << "  occurs  " << w.second <<
        ((w.second>1) ? "  times" : "  time") << endl;
}

  • 演示降序操作
multimap<string, int,std::greater<string>> word_count;//降序
	
word_count.insert({ "a",1 });
word_count.insert({ "c",1 });
word_count.insert({ "b",1 });

for (const auto &w : word_count) {
    cout << w.first << "  occurs  " << w.second <<
        ((w.second>1) ? "  times" : "  time") << endl;
}

初始化

  • 如果没有给出初始化值,就默认为空容器

迭代器与遍历

  • 与map相似,见map

添加元素(insert、emplace)

  • 见map笔记处

删除元素(erase)

  • 见map函数,原理相同

其他操作

  • 详细介绍,见map容器处

发布了1594 篇原创文章 · 获赞 1190 · 访问量 57万+

猜你喜欢

转载自blog.csdn.net/qq_41453285/article/details/105483491
今日推荐