zoukankan      html  css  js  c++  java
  • [知识点] 1.3.1 STL简介

    总目录 > 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>(1, "Zhangsan")); 
    a.insert(pair <int, string>(2, "Lisi")); 
    a.insert(pair <int, string>(3, "Wangwu")); 

      方法二(以value_type形式):

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

      方法三(以数组形式):

    a[1] = "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 = 1; 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(1);
    int x = map.count(1);
    
    map <int, string> :: iterator ity;
    ity = map.find(4);
    int y = map.count(4);

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

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

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

    5、上界下界

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

     1 map <int, int> a;
     2 
     3 int main() {
     4     a[1] = 1, a[2] = 2, a[3] = 3, a[4] = 4;
     5     map <int, int> :: iterator it;
     6     for (int i = 1; i <= 4; i++) {
     7         it = a.lower_bound(i);
     8         printf("%d ", it -> first);
     9     }
    10     for (int i = 1; i <= 4; i++) {
    11         it = a.upper_bound(i);
    12         printf("%d ", it -> first);
    13     }
    14     return 0;
    15 }

      输出结果为“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(1);
    it = a.find(2);
    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 = 1; 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 0;
    }
  • 相关阅读:
    Security headers quick reference Learn more about headers that can keep your site safe and quickly look up the most important details.
    Missing dollar riddle
    Where Did the Other Dollar Go, Jeff?
    proteus 与 keil 联调
    cisco router nat
    router dhcp and dns listen
    配置802.1x在交换机的端口验证设置
    ASAv931安装&初始化及ASDM管理
    S5700与Cisco ACS做802.1x认证
    playwright
  • 原文地址:https://www.cnblogs.com/jinkun113/p/10367638.html
Copyright © 2011-2022 走看看