您的位置:首页 > 编程语言 > C语言/C++

【C++基础】STL——常用序列式容器详解

2020-07-30 15:40 369 查看

序列式容器详解

  • <1> array
  • <2> vector
  • <3>deque
  • <4>list
  • forward_list
  • 序列式容器

    所谓STL序列式容器,其共同的特点是不会对存储的元素进行排序,元素排列的顺序取决于存储它们的顺序。不同序列式容器的适用场景不同。常用的序列式容器有:array、vector、deque、list

    序列式容器共有函数:

    下面是序列式容器的共有方法、主要是基于迭代器的一些方法和一些常用的方法。

    迭代器成员函数

    迭代器是什么?

    成员函数 功能
    begin()
    返回指向容器中第一个元素的随机访问迭代器。
    end()
    返回最后一个元素的后一个位置的随机访问迭代器
    rbegin()
    返回指向最后一个元素的随机访问迭代器
    rend()
    返回指向第一个元素之前一个位置的随机访问迭代器。
    cbegin()
    begin()
    功能相同,只不过在其基础上增加了 const 属性,不能用于修改元素。
    cend()
    end()
    功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
    crbegin()
    rbegin()
    功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
    crend()
    rend()
    功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。

    常用其他成员函数

    成员函数 功能
    size()
    返回容器中当前元素的数量,初始值通常为容器构造函数的第二个参数
    empty()
    判断容器是否为空,和通过 size()==0 的判断条件功能相同,但其效率更快
    at(n)
    返回容器中 n 位置处元素的引用
    自动越界检查,越界则抛出
    out_of_range
    异常。
    list容器没有此成员函数
    erase()
    移出一个元素或一段元素
    clear()
    移出所有的元素,容器大小变为 0
    insert()
    在指定的位置插入一个或多个元素
    array由于不能添加元素,不支持
    resize()
    改变实际元素的个数
    array由于固定长度,不支持
    swap()
    交换两个容器中的元素,必须保证这两个容器中存储的元素类型是相同的
    如果是array,还需要保证长度相等
    front()
    返回容器中第一个元素的直接引用
    该函数不适用于空的 array 容器
    back()
    返回容器中最后一个元素的直接应用,该函数同样不适用于空的 array 容器

    <1> array

    array 容器是 C++ 11 标准中新增的序列容器,简单地理解,它就是在 C++ 普通数组的基础上,添加了一些成员函数和全局函数。在使用上,它比普通数组更安全,且效率并没有因此变差。

    array 容器以类模板的形式定义在

    <array>
    头文件,并位于命名空间 std 中

    namespace std{
    template <typename T, size_t N>
    class array;
    }

    初始化

    头文件:

    #include<array>

    创建具有 4 个 int 类型元素的 array 容器,但是array不会初始化默认值,里面的值是不确定的

    std::array<double, 10> values;

    所以要创建初始化为0的array容器要采用下面的方式:

    std::array<double, 10> values {};

    也可以像创建常规数组那样对元素进行初始化:

    std::array<double, 10> values {0.5,1.0,1.5,,2.0};

    array 特有常用函数

    成员函数 功能
    fill(val) 将 val 赋值给容器中的每个元素
    operator[ ] array 重载了[]运算符,意味着array也可以像访问数组一样使用[]访问元素
    data() 返回一个指向容器首个元素的指针。利用该指针,可实现复制容器中所有元素等类似功能

    <2> vector

    vector 容器是 STL 中最常用的容器之一,它和 array 容器非常类似,都可以看做是对 C++ 普通数组的“升级版”。不同之处在于,array 实现的是静态数组(容量固定的数组),而 vector 实现的是一个动态数组,即可以进行元素的插入和删除,在此过程中,vector 会动态调整所占用的内存空间,整个过程无需人工干预。

    该容器擅长在尾部插入或删除元素,在常量时间内就可以完成,时间复杂度为O(1);而对于在容器头部或者中部插入或删除元素,则花费时间要长一些(移动元素需要耗费时间),时间复杂度为线性阶O(n)。原因在于其是基于数组的数据结构。

    vector 容器以类模板 vector( T 表示存储元素的类型)的形式定义在

    <vector>
    头文件中,并位于 std 命名空间中。

    初始化

    头文件:

    #include <vector>

    初始化一个 向量数组 vec

    vector<int> vec;

    可以调用其构造函数指定一个初始容量为10的vector

    vector<int> vec(10);

    或者 利用其成员函数

    reserve()
    改变容量为10

       	vector<int> vec;vec.reserve(10);
    

    也可以像数组一样初始化(C++11 新增)

    vector<int>vec = {1,2,3,4,5};

    vector常用函数

    成员函数 功能
    push_back() 在序列的尾部添加一个元素,复杂度仅O(1)
    pop_back() 移出序列尾部的元素,复杂度仅O(1)
    capacity() 返回当前容量,vector是动态数组,所以有size(实际元素个数)和capacity(容量)两个属性
    operator[ ] 重载了 [ ] 运算符,可以向访问数组中元素那样访问元素
    assign() 用新元素替换原有内容

    <3>deque

    双端队列deque(double-end-queue),和vector非常相似,都是通过动态数组管理内存,支持随机访问,并且和vector用相同的接口。不同点在于,deque的头尾两端都是开放的。因此,deque在头尾插入和删除都是O(1)。

    • deque 容器中存储元素并不能保证所有元素都存储到连续的内存空间中,因为deque中存储的是元素的指针,而非元素本身(详情)
    • deque不支持内存重新分配控制,因此,当有元素插入到容器中间时(非两端),那么这时候指向deque的指针、迭代器和引用都会失效!

    deque 的使用场景:

    1. 需要在容器前后两端插入和删除元素的时候

    2. 不需要指向容器中的元素

    初始化

    deque的初始化方式与vector差不多,有以下几种:
    头文件:

    #include <deque>

    创建一个空的deque容器 (常用方式)

    std::deque<int> deq;

    也可以创建一个 初始长度为10的队列,里面的元素为对应类型的默认值

    deque<int> deq(10);

    还可以创建一个具有 n 个元素的 deque 容器,并为每个元素都指定初始值

    deque<int> deq(10,2);

    deque常用函数

    deque的成员函数用法与vector差不多,最大的差别在于:deque里面没有capacity()函数

    成员函数 功能
    push_back() 在序列的尾部添加一个元素
    push_front() 在序列的头部添加一个元素
    pop_back() 移出序列尾部的元素
    pop_front() 移除容器头部的元素
    emplace_front() 在容器头部生成一个元素。和 push_front() 的区别是,
    该函数直接在容器头部构造元素,省去了复制移动元素的过程。
    emplace_back() 在容器尾部生成一个元素。和 push_back() 的区别是
    该函数直接在容器尾部构造元素,省去了复制移动元素的过程。

    deque 的特殊底层原理

    和 vector 容器采用连续的线性空间不同,deque 容器存储数据的空间是由一段一段等长的连续空间构成,各段空间之间并不一定是连续的,可以位于在内存的不同区域。为了管理这些连续空间,deque 容器用数组存储着各个连续空间的首地址。也就是说, 数组中存储的都是指针,指向那些真正用来存储数据的各个连续空间。

    当 deque 容器需要在头部或尾部增加存储空间时,它会申请一段新的连续空间,同时在 map 数组的开头或结尾添加指向该空间的指针,由此该空间就串接到了 deque 容器的头部或尾部。

    <4>list

    list是一个双向链表(doubly linked list),list的内部结构和vector,array,deque完全不同。list里面有两个指针(anchors),分别指向第一个元素和最后一个元素,每个元素都有指向自己前驱和后驱的指针。基于这样的存储结构,list 容器具有一些其它容器(array、vector 和 deque)所不具备的优势,即它可以在序列已知的任何位置快速插入或删除元素(时间复杂度为O(1))。并且在 list 容器中移动元素,也比其它容器的效率高。

    初始化

    list 的创建方式和deque的方式完全一样:
    头文件:

    #include <list>

    创建一个空的list容器 (常用方式)

    std::list<int> l;

    也可以创建一个 初始长度为10的队列,里面的元素为对应类型的默认值

    list<int> l(10);

    还可以创建一个具有 n 个元素的 deque 容器,并为每个元素都指定初始值为2

    list<int> l(10,2);

    list 常用函数

    成员函数 功能
    push_front() 在容器头部插入一个元素
    pop_front() 删除容器头部的一个元素
    push_back() 在容器尾部插入一个元素
    pop_back() 删除容器尾部的一个元素
    insert() 在容器中的指定位置插入元素。
    emplace_front() 在容器头部生成一个元素。该函数和 push_front() 的功能相同,但效率更高
    emplace_back() 在容器尾部直接生成一个元素。该函数和 push_back() 的功能相同,但效率更高。
    splice() 将一个 list 容器中的元素插入到另一个容器的指定位置。
    remove(val) 删除容器中所有等于 val 的元素。
    remove_if() 删除容器中满足条件的元素。
    sort() 通过更改容器中元素的位置,将它们进行排序。
    merge() 合并两个事先已排好序的 list 容器,并且合并之后的 list 容器依然是有序的。

    forward_list

    forward_list 是 C++ 11 新添加的一类容器,其底层实现和 list 容器一样,采用的也是链表结构,只不过 forward_list 使用的是单链表,而 list 使用的是双向链表

    整理资源来自:C语言中文网

    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: