C++STL之list

听说std::vector频繁插入有性能问题,那么std::list是std::vector的替代品?非也非也,所谓"寸有所长,尺有所短,物有所必,事有所当", 没有绝对的谁替代谁,作为工具人更正确的做法是根据特定的场景选用特定的工具而已。

老套路,先来两个关于list的权威参考:cppreference

又或者这个也行cplusplus

list的实现原理

list的底层是双向链表结构,因而list可以支持前后双向迭代,双向链表中每个元素存储在独立节点中,这些节点不像vector那样需要连续的内存,在节点中通过指针指向其前一个元素和后一个元素来进行链接。

因为list中的每个节点没有强制的关联性,基本是相互独立的,因此list通常可以在任意位置进行插入、移除元素并获得较好的性能,最差的情况就是在尾部进行插入删除操作,因为这需要迭代一遍。

也正是因为list的每个节点不具备连续关联性,因此它不支持任意位置的访问,而支持任意位置的随机访问,这恰恰是vector的最大优势。

或许世界就是这么难两全,有舍才有得...

list基本使用

对于list的构造比较简单,我们直接上代码:

c 复制代码
int main() {
    // 构造空的list
    std::list<int> list_01;
    // 构造list中包含2个值为3的元素
    std::list<int> list_02(2,3);
    // 使用的list_02的区间构造list
    std::list<int> list_03(list_02.begin(),list_02.end());
    // 是否为空
    std::cout << "list_01是否为空:" << list_01.empty() << std::endl;
    std::cout << "list_03的size:" << list_03.size() << std::endl;
    return 0;
}

对于list的迭代这里就不多说了,无脑使用auto的for即可。

对于list元素的访问我们可以内部函数frontback,这两个函数返回一个引用,一次既可以访问到对应值,也可以修改值:

c 复制代码
int main() {
    // 构造list中包含2个值为3的元素
    std::list<int> list_01(2,3);
    // 修改
    list_01.front() = 6;
    list_01.back() = 7;
    for (auto value:list_01) {
        std::cout << "value:" << value << std::endl;
    }
    return 0;
}

对于list的插入元素,我们可以使用哪些函数呢?最常用的有push_backpush_frontinsert这三个,其中push_backpush_front意如字面,就是 在尾部和头部插入元素的意思。而insert则支持在任意位置插入。

c 复制代码
using namespace std;

int main() {
    // 构造list中包含2个值为3的元素
    std::list<int> list_01(2,3);
    // 尾部插入
    list_01.push_back(6);
    // 头部插入
    list_01.push_front(7);
    auto insertPos = list_01.begin();
    insertPos++;
    // 特定位置插入
    list_01.insert(insertPos,5);
    for (auto value:list_01) {
        std::cout << "value:" << value << std::endl;
    }
    return 0;
}

上面的示例代码输出如下:

对于list的删除操作比较多,我们直接看代码注释:

c 复制代码
using namespace std;

int main() {
    // 构造list中包含10个值为3的元素
    std::list<int> list_01(10,3);
    // 尾部删除
    list_01.pop_back();
    // 头部删除
    list_01.pop_front();
    // 删除所有为3的元素
//    list_01.remove(3);
    // 按照条件删除
    list_01.remove_if([=](const int value){
        // 删除值为2的元素
        return value == 2;
    });
    
    // 删除特定位置的元素
    list_01.erase(list_01.begin());

    for (auto value:list_01) {
        std::cout << "value:" << value << std::endl;
    }
    // 清空
    list_01.clear();
    return 0;
}

需要注意跌是在调用list的pop_frontpop_back需要保证list不为空,否则会跑出异常。

list与vector的对比

vector与list都是STL中非常重要的序列容器,由于两个容器的底层结构不同,导致其特性以及应用场景不同,笔者把它们的区别整理如下表:

功能 vector list
底层结构 动态顺序表,一段连续空间 带头节点的双向循环链表
随机访问 支持随机访问,访问某个元素效率O(1) 不支持随机访问,访问某个元素效率O(N)
插入和删除 任意位置插入和删除效率低,需要搬移元素,时间复杂度为O(N),插入时可能需要增容。 增容:开辟新空间,拷贝元素,释放旧空间,导致效率更低 任意位置插入和删除效率高,不需要搬移元素,时间复杂度为O(1),不存在增容问题
空间利用率 底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高 底层节点随机动态开辟,小节点容易造成内存碎片,空间利用率低,缓存利用率低
迭代器 原生态指针 对原生态指针(节点指针)进行封装
迭代器失效 在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器需要重新赋值否则会失效 插入元素不会导致迭代器失效,删除元素时,只会导致当前迭代器失效,其他迭代器不受影响
使用场景 需要高效存储,支持随机访问,不关心插入删除效率 大量插入删除操作,不关心随机访问

关注我,后期不定期更新...

相关推荐
别NULL3 小时前
机试题——疯长的草
数据结构·c++·算法
CYBEREXP20084 小时前
MacOS M3源代码编译Qt6.8.1
c++·qt·macos
ZSYP-S5 小时前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
yuanbenshidiaos5 小时前
c++------------------函数
开发语言·c++
yuanbenshidiaos5 小时前
C++----------函数的调用机制
java·c++·算法
唐叔在学习5 小时前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
ALISHENGYA5 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法
tianmu_sama5 小时前
[Effective C++]条款38-39 复合和private继承
开发语言·c++
羚羊角uou5 小时前
【C++】优先级队列以及仿函数
开发语言·c++
姚先生975 小时前
LeetCode 54. 螺旋矩阵 (C++实现)
c++·leetcode·矩阵