• C++ 序容器


    一,顺序容器类型

    1,分类

    容器名特点
    vector可变大小数组,支持快速随机访问,在尾部之外的位置插入或删除元素可能很慢
    deque双端队列,支持快速随机访问,在头尾位置插入/删除速度很快
    list双向链表,只支持双向顺序访问,在list中任何位置进行插入/删除操作速度都很快
    forward_list单向链表,只支持单向顺序访问,在链表任何位置进行插入/删除操作速度都很快
    array固定大小数组,支持快速随机访问,不能添加或删除元素
    string与vector相似的容器,专门用于保存字符,随机访问速度快,在尾部插入删除速度快

    2,容器特点

    string和vector等将元素保存在连续的内存空间中,由元素的下标计算其地址时速度非常快,但在容器中间添加或删除元素非常耗时,因为要移动插入和删除位置之后的所有元素,来保持连续存储。

    • list和forward_list 可以在容器任意位置添加和删除操作都很快,但不支持随机访问,访问时需要遍历整个容器。
    • deque支持快速的随机访问,但在deque中间位置添加或删除元素的代价很高,在两端添加删除元素很快。
    • array大小固定,更加安全的数组类型,不支持添加和删除元素以及改变容器大小的操作。
    • forward_list 速度和单向链表相当,但是没有size操作。

    3,选择容器的基本原则

    1)大多数情况下选用vector,除非有特殊需求
    2)程序有很多小的元素,且空间额外开销很重要时不要使用list或forward_list
    3)需要随机访问时,选用vector或deque
    4)在容器中间插入或删除元素,使用list或forward_list
    5)需要在头尾位置插入或删除元素,不会在中间插入或删除操作,使用deque

    二,容器库概览

    使用容器时都需要导入对应的头文件,例如list头文件在list中,vector头文件在vector中
    例如:

        list<double> list; //保持double的list
        deque<double> deq;//保存double的deque
    
    • 1
    • 2

    顺序容器几乎可以保存任意类型的元素,在尖括号内指定元素类型。

    1,容器操作的一些限制

    在这里插入图片描述

    2,迭代器

    迭代器有公共的接口,如果一个迭代器提供某个操作,那所有提供相同操作的迭代器都对这个操作的实现方式时相同的。

    2.1,区间迭代器(begin,end)
    • 他们指向同一个容器中的元素,或容器中最后一个元素之后的位置
    • 使用时反复递增begin至end
      注:
      1)如果begin与end相等,则范围为空
      2)begin与end不等,则范围内至少包含一个元素,begin指向该范围中的第一个元素
      3)对begin递增若干次使得begin == end
      例如:
        vector<int> m_vec = {1,2,3,4,5,6,7,8,9};
        auto begin = m_vec.begin();
        //vector<int>::iterator beign = m_vec.begin(); //等价于auto begin的方式,使用了类型推导
        auto end = m_vec.end();
        while (begin != end)
        {
            cout << "current value:" << *begin<
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    使用区间迭代器遍历打印vector中的元素。
    例如:

       //通过list<string>定义一个迭代器类型
        list<string>::iterator iter;
        //通过vector<int>定义一个difference_type类型
        vector<int>::difference_type cout;
    
    • 1
    • 2
    • 3
    • 4
    2.2,begin和end多版本类型

    返回带r的版本返回反向迭代器,以c开头的版本则返回const迭代器
    例如:

        list<string> a = {"Milton","Shakespeare","Austen"};
    
        auto it1 = a.begin();
        auto it2 = a.rbegin();
    
        auto it3 = a.cbegin();
        auto it4 = a.crbegin();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注:当不需要写访问时,应使用cbegin和cend

    3,容器定义和初始化

     vector<int> v1; //默认构造函数
        vector<int> v2(v1);//以拷贝的方式使用v1的值给v2默认初始化
        vector<int> v3 = v2;//和v2的方式一样
        vector<int> v4{ 1,2,3,4,5 };
        vector<int> v5 = { 1,2,3,4,5 };//v4和v5等价
        vector<int> v6(v5.begin(), v5.end());
        vector<int> v7(8);//默认8个元素,都为默认值
        vector<int> v8(8, 1);//默认8个元素,默认值都为1
        auto v8_begin = v8.begin();
        auto v8_end = v8.end();
        while (v8_begin != v8_end)
        {
            cout << "v8 current value:" << *v8_begin << endl;
            v8_begin++;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    注:在使用拷贝时,两个容器的类型及其元素类型必须匹配。
    使用时,只有顺序容器的构造函数才接收大小参数,关联容器并不支持。

    4,赋值和swap

    赋值运算符,将左边容器中的全部元素替换为右边容器中的元素的拷贝。
    例如:

        array<int,10> a1 = {0,1,2,3,4,5,6,7,8,9};
        array<int,10> a2 = a1;//将a2的内容替换为a1中元素的拷贝
        a2 = { 1,2,3 };//赋值后,a2的大小为3
    
    • 1
    • 2
    • 3

    使用时需要先导入array的头文件

    4.1,容器赋值运算
     a1 = a2                将a1中元素替换为a2中元素的拷贝,a1和a2必须具有相同的类型
     a1 = {1,2,3,4,...}     将a1中元素替换为初始列表中元素的拷贝(array 不适用)
     swap(a1,a2)          交换a1和a2中的元素,a1和a2必须具备相同的类型, swap通常比从a1向a2拷贝快得多
    a1.swap(a2)
    assign操作不适用于关联容器和array
    seq.assign(b,e)        将seq中的元素替换为迭代器b和e所表示的范围中的元素。迭代器b和e不能指向seq中的元素
    seq.assign(il)           将seq中的元素替换为初始化il中的元素
    seq.assign(n,t)         将seq中的元素替换为n个值为t的元素
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    注:顺序容器array除外,assign允许从一个不同但相容得类型赋值,或者从容器得一个子序列赋值。assign操作用参数所制定得元素替换左边容器中所有元素。
    例如:

       list<string> list(1);
        list.assign(10, "Hello");//十个元素都是hello
    
    • 1
    • 2
    4.2,swap

    操作交换两个相同类型容器的内容,调用swap后,两个容器中的元素将会交换

        vector<string> v1(10);
        vector<string> v2(20);
        swap(v1, v2);
    
    • 1
    • 2
    • 3

    注:除了array外,交换两个容器的操作保证很快,元素本身并未交换,交换了两个人容器的内部数据结构

    三,顺序容器操作

    1,添加元素

    除了array以外,所有标准容器都有灵活的内容管理,在运行是,可以动态添加或删除元素来改变容器大小。

    操作描述
    c.push_back(t);c.emplace_back(args)在c的尾部创建一个值为t或由args创建的元素。返回void
    c.push_front(t);c.emplace_front(args)在c的头部创建一个值为t或由args创建的元素。返回void
    c.insert(p,t) ;c.emplace(p,args)在迭代器p指向的元素之前创建一个值为t或由args创建的元素。返回指向新添加的元素的迭代器。
    c.insert(p,n,t)在迭代器p指向的元素之前插入n个值为t的元素,返回指向新添加的第一个元素的迭代器。若n为0,则返回p
    c.insert(p,b,e)将迭代器b和e指定的范围内的元素插入到迭代器p指向的元素之前。b和e不能指向c中的元素。返回指向新添加的第一个元素的迭代器。若返回为空,则返回p
    c.insert(p,il)il是一个花括号包围的元素值列表。将这些给定值插入到迭代器p指向的元素之前,返回指向新添加的第一个元素的迭代器,若列表为空,则返回p。

    注:
    1)fordword_list有自己转悠版本的insert和emplace,不支持push_back和emplace_back
    2)vector和string不支持push_front和emplace_front。
    3)向一个vector,string或deque插入元素会使所有指向容器的迭代器,引用和指针失效。
    向一个vector或string添加元素可能引起整个对象存储空间的重新分配,并将元素从旧的空间移动到新分配的内存空间中。
    当用一个对象初始化容器时,或将一个对象插入到容器中时,实际上放入到容器中的是一个对象值得拷贝,而不是对象本身。

    2,访问元素

    每个顺序容器都有一个front函数,除forward_list之外所有顺序容器都有一个back函数,分别返回首元素和尾元素。
    在使用之前需要检查容器内是否有元素,如果没有元素,则访问操作得结果是未定义得。
    例如:

        vector<string> v1(10);
        if (!v1.empty())
        {
            auto vl1 = v1.front();
            auto vl2 = v1.back();//forward_list不支持
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    顺序容器中访问元素的操作

    功能描述
    v.back()返回v中尾元素的引用,若v为空,函数行为未定义
    v.front()返回v中首元素的引用,若v为空,函数行为未定义
    v[n]返回v中下标为n的元素的引用,n是一个无符号整数
    c.at(n)返回下标为n的元素的引用,如果下标越界,则抛出异常->out_of_rang

    at和下标操作只适合用于string,vector,deque和array
    back不适用于forward_list;

    3,删除元素

    非array容器也有多种删除元素的方式,操作完成后会改变array容器的大小

    功能描述
    v.pop_back()删除v中尾元素,函数返回void
    v.pop_front()删除v中首元素
    v.erase§删除迭代器p所指向的元素,返回一个指向呗删除元素之后的元素的迭代器,若p指向尾元素,则返回尾后迭代器。
    v.erase(b,e)删除迭代器b和e所指定范围内的元素,返回一个指向最后一个呗删元素之后的元素的迭代器
    v.clear()删除v中所有的元素,返回void

    注:
    forward_list 有特殊的erase,不支持pop_back;vector和string 不支持pop_front。
    在删除元素时,必须要做好检查,保证删除的元素是存在的。

    4,forward_list操作

    它是一个单向的链表,执行添加和删除元素的操作是通过改变给定元素之后的元素来完成。

    功能描述
    lst.before_begin()返回指向链表首元素之前不存在的元素的迭代器。
    lst.cbefore_begin()返回一个const_iterator
    lst.insert_after(p,t)在迭代器p之后的位置插入元素。t是一个对象,n是数量
    lst.insert_after(p,n,t) ; lst.insert_after(p,b,e);lst.insert_after(p,il)b和e 是表示范围的一堆迭代器,il是一个花括号列表。返回一个指向最后一个插入元素的迭代器。
    emplace_after(p,args)使用args在p指定的位置之后创建一个元素,返回一个指向这个新元素的迭代器。若p为尾后迭代器,则函数行为未定义
    lst.erase_after(p);lst.erase_after(b,e)删除p指向的位置之后的元素,或删除b之后直到e之间的元素,返回一个指向被删除元素之后元素的迭代器。

    例如:

        forward_list<int> flst = {0,1,2,3,4,5,6};
    
        auto prev = flst.before_begin();
        auto curr = flst.begin();
    
        while (curr != flst.end())
        {
            if (*curr % 2)
            {
                curr = flst.erase_after(prev);
            }
            else {
                prev = curr;
                ++curr;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    5,改变容器的大小

    除了array不支持resize,可以使用resize来增大或缩小容器。如果当前大小大于所需要的大小,容器后部的元素会被删除;如果当前大小小于新大小,会将新元素添加到容器后部。
    例如:

        list<int> ilist(10, 42);
        ilist.resize(15);//将5个为0的元素添加到ilist的末尾
        ilist.resize(25, -1);//将10个值为-1的元素添加到ilist的末尾
        ilist.resize(5);//从ilist末尾删除20个元素s
    
    list.resize(n)               调整list的大小为n个元素,若n
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    6,vector扩容

    为了防止频繁的扩容,vector会在每次扩容时,分配需求更大的内存空间,预留这些空间作为备用,减少扩容带来的移动元素的性能问题。提升效率
    c.shrink_to_fit() 请将capacity减少为于size相同大小
    c.capacity() 不重新分配内存空间的话,c可以保存多少元素
    c.reserve(n) 分配至少能容纳n个元素的内存空间
    注:
    1)shrink_to_fit只适用于vector,string和deque
    2)capacity和reserve只适用于vector和string.
    reserve并不改变容器中元素的数量,它仅影响vector预先分配多大的内存空间。
    当需要的内存空间超过当前容量,reserve调用才会改变vector的容量。如果需求大小小于当前容量,至少分配于需求一样大的内存空间。

  • 相关阅读:
    Spring Cloud Sleuth 整合 Zipkin 进行服务链路追踪
    uni-app 下载文件 预览 + 保存自定义目录
    angular之formgroup
    使用VScode搭建Rust开发环境
    autoware.ai感知随笔--地面滤波
    Docker入门指南
    【数据结构】830+848真题易错题汇总(10-23)
    ASEMI代理力特二极管LSIC2SD120A05,肖特基LSIC2SD120A05
    流批一体在京东的探索与实践
    内容平台加码旅游:谁是下一个网红城市
  • 原文地址:https://blog.csdn.net/zdc9023/article/details/126388485