总目录 > 1 语言基础 > 1.3 C++ 进阶 > 1.3.1 STL 简介

前言

鉴于最近不少次都要用到 map 我却总是出各种 bug,于是决定写一篇总结来巩固一下。

这篇文章虽名为 STL 容器之 map,但其实包含了整个 STL 及其容器的概念与用途,以及诸如vector, set 等基本容器的许多常规操作,可以作为一篇 STL 容器的总领性文章。

然后,现在完全改成一篇介绍 STL 容器及算法简介的文章辣!这样整个 STL 部分就显得更成体系了。

更新记录

UPDATE - 20200421:将原有介绍 map 的部分后移,这篇文章主要是对 STL 容器和算法的一些简要介绍。

UPDATE(20190416):写完vector和set之后,发现不少内容全部引导到map上了……于是进行了一定的描述补充与更正。

子目录列表

1、什么是 STL

2、STL 容器

3、STL 算法

1.3.1 STL 简介

1、什么是 STL

STL 全称 Standard Template Library,中文名标准模板库,目的是标准化组件,属于 C++ 语言的一部分,所以并不用额外安装其他东西。其被组织为如下 13 个头文件:

<algorithm>, <deque>, <functional>, <iterator>, <vector>, <list>, <map>, <memory.h>, <numeric>, <queue>, <set>, <stack>, <utility>

STL 可分为六大部分:容器 (containers), 迭代器 (iterators), 空间配置器 (allocator), 配接器 (adapters), 算法 (algorithms), 仿函数 (functors),本文主要介绍容器和算法。

2、STL 容器

STL 容器本质是一些常用的数据结构的模板,简化操作。容器分为如下几种,并列出几种最常用的:

① 序列式容器

> vector 向量:动态顺序表,可从后端增加元素;

> list 列表:可双向遍历的链表;

还有:deque 双端队列;array 数组;forward_list 单向列表。

② 关联式容器

> set 集合:有序存储互异(无相同)元素;

> map 映射:由 {key 键, value 值} 对组成的集合,key 互异;

还有:multiset 多重集合,multimap 多重映射。

③ 容器适配器

本质并不是容器,没有迭代器等其他容器具备的特点。

> stack 栈:LIFO;

> queue 队列:FIFO;

(关于栈与队列的介绍,请参见 7.1 栈,队列与链表

> priority_queue 优先队列(堆):以某种关系决定次序的队列。

它们的声明形式是一样的:

容器类型名 <变量类型名, ...> 容器名

如:

map <int, string> a;

priority_queue <double> q;

关于所有容器的共有函数请参见:<施工中>

3、STL 迭代器

迭代器(iterator)适用于访问或检查 STL 容器内元素的对象,类似于指针,但封装性更好,访问格式也是统一的。

这里不单独介绍,使用方法参见:<施工中>

4、STL 算法

STL 共计提供了超过 100 个实现算法的模板函数,基本都包含在 <algorithm> 库中。它们往往是在元素范围上操作,如果范围定义为 [l, r],则其实际查询或修改的范围为 [l, r)。下面大致列出部分常用算法,所有算法请参见:https://zh.cppreference.com/w/cpp/algorithm

① 查询类

find 顺序查找:find(l, r, val);

find_end 逆序查找:find_end(l, r, val);

② 修改类

reverse 翻转:reverse(l, r),适用于数组和字符串;

swap 交换:swap(a, b);

shuffle 随机打乱:shuffle(l, r);

unique 去重:unique(Iterator first, Iterator last),适用于各类容器。

③ 划分类

④ 排序类

sort 排序:sort(l, r, cmp),cmp 为自定义的状态函数,关于这一点请参见 1.3.6 类与对象 中的重载运算符部分。

关于 sort 更具体的一些内容,请参见 2.4 排序十讲 中的快速排序部分。

stable_sort 稳定排序:stable_sort(l, r, cmp),保证想等元素排序后相对位置与原序列一致;

nth_element 找第 n 大元素:nth_element(l, r, cmp)。

⑤ 二分搜索类

在有序序列中使用。

binary_search 二分查找:binart_search(l, r, val);

lower_bound 返回第一个大于等于 val 的元素的迭代器:lower_bound(l, r, val);

upper_bound 返回第一个大于 val 的元素的迭代器:upper_bound(l, r, val)。

关于二分,请参见:2.5 二分

⑥ 集合类

⑦ 最小 / 大值类

max 最大值:max(a, b);

min 最小值:min(a, b)。

下方是原 map 部分,暂时放在这里。

四、map的用途

  I miss the taste of the sweeter life...哦对不起串了。

  从名字就很好理解作用了——映射,令一物与另一物之间建立起一一映射的关系,以便获得将不方便查找的信息。

  举个例子,一个很简单的问题,读入n个长度为m的字符串,再询问q次某某字符串是否在其中。初学者选择字符一一比对,O(nmq);这是有人说,聪明人看到字符串早就去Hash了,可以,but,为保证正确性,哈希值同样会很大,开一个数组去存怕是无内存上限。

  那好,我们将得到的哈希值编一个号,比如某巨大的数我们将它视作1,另一个巨大的数编成2,再把编号与值一一对应的关系给记录下来,用什么记录?

  map:正是在下。

五、map的声明与功能

  上面提了,map用途在于自动建立编号与值的对应。那么STL提供了哪些函数供我们来捣鼓里面的东西呢?

  函数清单:insert, begin, end, size, rbegin, rend, find, count, lower_bound, upper_bound, erase。

  其实这些函数中大部分也是STL容器中通用的函数,下面会有详细的使用介绍,适用于但不仅限于map。

1、构造

map <int, string> a;

  简单明了,前者为键值(key),后者为值(value),此处为整形&字符串的映射,两者亦可为任何其他支持的类型。

2、插入数据

  方法一(以pair形式):

a.insert(pair <int, string>(, "Zhangsan"));
a.insert(pair <int, string>(, "Lisi"));
a.insert(pair <int, string>(, "Wangwu"));

  方法二(以value_type形式):

a.insert(map <int, string> :: value_type(, "Zhangsan"));  

  方法三(以数组形式):

a[] = "Zhangsan";

  需要注意的是,方法一二无法覆盖数据,而方法三可以,即如果键值为1的map已经被映射到“Zhangsan”,可以通过数组形式直接修改。

3、数据遍历

  这里引入新概念——迭代器(iterator)。先声明:

map <int, string>::iterator it; 

  使用方法也很简易:

for (it = a.begin(); it != a.end(); it++)
   cout << it -> first << ' ' << it -> second << endl;

  这个是前向迭代器,同样还有反向迭代器,如下:

map <int, string> :: reverse_iterator rit;

for (rit = a.rbegin(); rit != a.rend(); rit++)
cout << rit -> first << ' ' << rit -> second << endl;

  同样地,还有用数组的方式遍历:

int s = a.size();
for (int i = ; i <= s; i++)
cout << i << ' ' << a[i] << endl;

  其中,size函数是用来获取map中有多少项数据。

  注意点:在遍历时我们用了begin()和end(),两者类型均为迭代器。我们发现遍历起始点为begin(),当迭代器不等于end()时继续,这意味着begin()为开头标识,它包括了数据内容,即第一项数据;end()为结尾标识,但它是最后一项数据后面的一个独立的标识,本身并不表示任何数据。

4、查找数据

  ①判断是否出现用count函数

  ②判断出现在哪里用find函数

map <int, string> :: iterator itx;
itx = map.find();
int x = map.count(); map <int, string> :: iterator ity;
ity = map.find();
int y = map.count();

  count函数只返回0或1,如上述代码中,x = 1, y = 0;

  find函数返回的是一个迭代器,如上述代码中,itx -> first = 1, itx -> second = "Zhangsan";

  而如果没有该数据的话,则返回的是it = a.end()(末尾标识)。

5、上界下界

  lower_bound()返回要查找关键字的下界,即值 >= 给定元素的第一个位置,upper_bound()返回上界,即值 > 给定元素的第一个位置。

 map <int, int> a;

 int main() {
a[] = , a[] = , a[] = , a[] = ;
map <int, int> :: iterator it;
for (int i = ; i <= ; i++) {
it = a.lower_bound(i);
printf("%d ", it -> first);
}
for (int i = ; i <= ; i++) {
it = a.upper_bound(i);
printf("%d ", it -> first);
}
return ;
}

  输出结果为“1 2 3 4 2 3 4 4”,其中upper_bound(4)由于不存在,故it = a.end(),而为什么it -> first输出4,这是个哲学问题,引用哲学家Cab的话说,“从结论上说访问end的first是会得到size,然而是undefined behavior(未定义行为)”。所以大可不必纠结这东西了。

6、删除元素

  这里介绍两种删除方法:单删除,区间删除。

  单删除即直接删除一项,传入参数可以为键值,也可以为一个迭代器。

map <int, string> :: iterator it;
a.erase();
it = a.find();
a.erase(it);

  区间删除只能为迭代器。听上去美,但是毕竟这是map不是for,当你的数据并不是美好的从1到n连续编号时,你只能拿它作clear的操作了,也就是:

a.erase(a.begin(), a.end());

  从这里又可得知一点,erase两个参数也是左闭右开的关系,即包括前者不包括后者。

7、排序问题

  map中的元素是自动按键值升序排序,无论是数还是字符串。但如果键值为一个结构体,我们需要重载小于符号。如下小程序中,我们将囊括学号姓名两项数据的结构体作为键值,成绩作为值,然后需要以学号进行升序排序:

#include <bits/stdc++.h>
using namespace std; #define MAXN 35 struct Stu {
int id;
string str;
bool operator < (Stu const& x) const {
return id < x.id;
}
}; map <Stu, int> a; int n, id, o;
string str; int main() {
cin >> n;
for (int i = ; i <= n; i++) {
cin >> id >> str >> o;
a.insert(pair <Stu, int> ((Stu) {id, str}, o));
}
map <Stu, int> :: iterator it;
for (it = a.begin(); it != a.end(); it++)
cout << it -> first.id << ' ' << it -> first.str << ' ' << it -> second << endl;
return ;
}

[知识点]C++中STL容器之map的更多相关文章

  1. [知识点]C++中STL容器之vector

    零.STL目录 1.容器之map 2.容器之vector 3.容器之set 一.前言 关于STL和STL容器的概念参见STL系列第一篇——map(见上).今天介绍第二个成员——vector. 二.用途 ...

  2. [知识点]C++中STL容器之set

    零.STL目录 1.容器之map 2.容器之vector 3.容器之set 一.前言 继上期的vector之后,我们又迎来了另一个类数组的STL容器——set. 二.用途与特性 set,顾名思义,集合 ...

  3. STL容器之map

    [1]map容器 map 是关联容器.容器中的每一个元素都是由一个键值和一个数据值组成的. set 是一个集合它以其元素作为键值(同一个键值只能出现一次),且默认以升序排列. list 是一个顺序容器 ...

  4. C++ STL容器之 map

    map 是一种有序无重复的关联容器. 关联容器与顺序容器不同,他们的元素是按照关键字来保存和访问的,而顺序元素是按照它们在容器中的位置保存和访问的. map保存的是一种 key - value 的pa ...

  5. C++ 关联容器之map插入相同键元素与查找元素操作

    一.插入相同键元素操作 (1)insert方法 在map中的键必须是唯一的,当想map中连续插入键相同但值不同的元素时,编译和运行时都不会发生任何错误,系统会忽略后面的对已存在的键的插入操作,如 ma ...

  6. iBinary C++STL模板库关联容器之map/multimap

    目录 一丶关联容器map/multimap 容器 二丶代码例子 1.map的三种插入数据的方法 3.map集合的遍历 4.验证map集合数据是否插入成功 5.map数据的查找 6.Map集合删除元素以 ...

  7. STL关联式容器之map和multimap

    一,map和multimap的概念 1.map和multimap的基本知识 map是标准的关联式容器,一个map是一个键值对序列,即(key,value)对.它提供基于key的快速检索能力. map中 ...

  8. STL容器之vector

    [1]模板类vector 模板类vector可理解为广义数组.广义数组,即与类型无关的数组,具有与数组相同的所有操作. 那么,你或许要问:既然C++语言本身已提供了一个序列式容器array,为什么还要 ...

  9. Java容器之Map接口

    Map 接口: 1. 实现 Map 接口的类是用来存储键-值(key-value)对: 2. Map 接口的实现类有 HashMap 和 TreeMap 等: 3. Map 类中存储的键-值对,通过键 ...

随机推荐

  1. selenium +java 多个类公用driver问题

    问题点:太久没有写selenium代码,居然把driver公用的问题忘记了,即:每写一个测试类,执行过程中都会新建一个窗口,这样应该说是非常不专业的. 大概想了一个方法,虽然看起来也不怎么专业,但感觉 ...

  2. Zabbix远程执行命令

    原文发表于cu:2016-06-14 Zabbix触发器(trigger)达到阀值后会有动作(action)执行:发送告警信息或执行远程命令. 本文主要配置验证zabbix执行远程命令. 一.环境 S ...

  3. JAVA学习笔记--匿名内部类

    匿名内部类,即没有名字的内部类. 我们在编写JAVA程序时,往往要创建很多类,类是可以被重复使用的.但有时,我们创建了一个类,却只需要使用该类一次,那么单独为其编写一个类就显得有些麻烦,这时可以使用匿 ...

  4. 使用 MPI for Python 并行化遗传算法

    前言 本文中作者使用MPI的Python接口mpi4py来将自己的遗传算法框架GAFT进行多进程并行加速.并对加速效果进行了简单测试. 项目链接: GitHub: https://github.com ...

  5. 《Linux内核与分析》第五周

    20135130王川东 一.给MenuOS增加time和time-asm命令 命令:1.强制删除:rm menu -rf 2.克隆:git clone (后跟需要克隆数据所在的位置) 3.自动编译,自 ...

  6. 2018软工实践—Alpha冲刺(1)

    o## 队名 火箭少男100 组长博客 林燊大哥 作业博客 Alpha 冲鸭! 成员冲刺阶段情况 林燊(组长) 过去两天完成了哪些任务 协调各成员之间的工作,对多个目标检测及文字识别模型进行评估.实验 ...

  7. 404_NOTE_Foung_软工6

    目录 NABCD分析引用 N(Need,需求): A(Approach,做法): B(Benefit,好处): C(Competitors,竞争): D(Delivery,交付): 初期 中期 个人贡 ...

  8. Windows Forms编程实战学习:第二章 欢迎使用Visual Studio

    第二章 欢迎使用Visual Studio 1,AssemblyInfo文件 包含程序集的属性,向应用程序添加元数据 [assembly:<attribute>(<setting&g ...

  9. iOS- 全方位解析.crash文件崩溃报告

    1.前言 想来每个iOS攻城狮,都免不了要接触.crash文件 那么什么是.crash文件? iOS app的所有崩溃记录都会记录在设备上,所以对于和我一样没有集成让用户发送崩溃报告功能的iOS开发者 ...

  10. emacs编译整个emacs.d目录

    $emacs 在emacs查看里面,输入: C-u M-x byte-recompile-directory 然后输入 ~/.emacs.d 即可.