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),不存在增容问题
空间利用率 底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高 底层节点随机动态开辟,小节点容易造成内存碎片,空间利用率低,缓存利用率低
迭代器 原生态指针 对原生态指针(节点指针)进行封装
迭代器失效 在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器需要重新赋值否则会失效 插入元素不会导致迭代器失效,删除元素时,只会导致当前迭代器失效,其他迭代器不受影响
使用场景 需要高效存储,支持随机访问,不关心插入删除效率 大量插入删除操作,不关心随机访问

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

相关推荐
代码雕刻家5 分钟前
课设实验-数据结构-单链表-文教文化用品品牌
c语言·开发语言·数据结构
rjszcb34 分钟前
一文说完c++全部基础知识,IO流(二)
c++
小字节,大梦想1 小时前
【C++】二叉搜索树
数据结构·c++
吾名招财1 小时前
yolov5-7.0模型DNN加载函数及参数详解(重要)
c++·人工智能·yolo·dnn
我是哈哈hh2 小时前
专题十_穷举vs暴搜vs深搜vs回溯vs剪枝_二叉树的深度优先搜索_算法专题详细总结
服务器·数据结构·c++·算法·机器学习·深度优先·剪枝
憧憬成为原神糕手2 小时前
c++_ 多态
开发语言·c++
郭二哈2 小时前
C++——模板进阶、继承
java·服务器·c++
挥剑决浮云 -2 小时前
Linux 之 安装软件、GCC编译器、Linux 操作系统基础
linux·服务器·c语言·c++·经验分享·笔记
丶Darling.2 小时前
LeetCode Hot100 | Day1 | 二叉树:二叉树的直径
数据结构·c++·学习·算法·leetcode·二叉树
labuladuo5203 小时前
Codeforces Round 977 (Div. 2) C2 Adjust The Presentation (Hard Version)(思维,set)
数据结构·c++·算法