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

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

相关推荐
南玖yy7 分钟前
数据结构C语言练习(两个队列实现栈)
c语言·数据结构·算法
长流小哥13 分钟前
可视化开发:用Qt实现Excel级动态柱状图
开发语言·c++·qt·ui
loser~曹28 分钟前
基于快速排序解决 leetcode hot215 查找数组中第k大的数字
数据结构·算法·leetcode
Dream it possible!34 分钟前
LeetCode 热题 100_打家劫舍(83_198_中等_C++)(动态规划)
c++·算法·leetcode·动态规划
zhouziyi070139 分钟前
【蓝桥杯14天冲刺课题单】Day 8
c++·算法·蓝桥杯
SylviaW0843 分钟前
python-leetcode 62.搜索插入位置
数据结构·算法·leetcode
丶Darling.44 分钟前
26考研 | 王道 | 数据结构 | 第四章 串
数据结构·考研·kmp
愚润求学1 小时前
【C++】vector常用方法总结
开发语言·c++·vector
赤秀2 小时前
C++模板初阶
开发语言·c++
深圳厨神2 小时前
浅谈数据结构
数据结构