第四篇 STL-list

✅ 核心一句话总结

list 是 C++ 的双向循环链表 ,和 vector (动态数组)、deque (双端数组) 完全不同;✅ 它的王牌优点链表特性 → 任意位置增 / 删元素,速度都贼快 (包括开头、中间、结尾);✅ 它的致命缺点没有下标、不支持随机访问 ,不能像数组一样用 list[i] 取值,这是和 vector/deque 最大的区别!

一句话区分三者:vector = 尾增删快、查快;deque = 头尾增删快、查快;list = 【所有位置】增删都快、查慢。

✅ 必备前置:头文件

固定写法,记死,用 list 必须加:

复制代码
#include <list>
using namespace std; // 不加就写 std::list

✅ 一、创建 / 初始化

零记忆成本,所有写法和前两个容器一模一样,直接抄:

复制代码
// 1. 空list 【最常用】
list<int> lst;

// 2. 指定长度+默认值:5个元素,每个都是0
list<int> lst(5, 0);

// 3. 直接赋值初始化
list<int> lst = {1,2,3,4,5};

// 4. 拷贝另一个list
list<int> lst1 = {1,2,3};
list<int> lst2(lst1);

// 5. 指定长度,元素随机值(少用)
list<int> lst(5);

✅ 二、核心增删改查

⚠️【首要必记・雷区】list 没有下标!!

❌ 绝对不能写 lst[0]lst.at(1) 这种代码,编译直接报错!✅ list 是链表,内存不连续,没有「下标」的概念,只能通过迭代器 / 遍历 访问元素!

✅ 【增】添加元素(4 个,全高频,全是优势,随便用!)

list 所有增操作效率都极高(链表的特性,不用移动元素),这是 list 的核心价值,优先级无差别,按需用即可:

  1. lst.push_back(值) :尾部追加元素 ✅✅✅

  2. lst.push_front(值):头部插入元素 ✅✅✅

  3. lst.insert(迭代器位置, 值)指定位置插入元素,超快! ✅✅✅ (vector/deque 这里巨慢,list 的王牌)

  4. lst.insert(迭代器位置, n, 值):指定位置插入 n 个相同值(比如插 3 个 666)

    list<int> lst;
    lst.push_back(3); // lst: [3]
    lst.push_front(1); // lst: [1,3]
    lst.insert(++lst.begin(), 2); // 中间插2 → lst: [1,2,3]
    lst.insert(lst.end(),2,6); // 尾部插2个6 → lst: [1,2,3,6,6]

✅ 【删】删除元素(5 个,全高频,同样全是优势,list 的核心价值)

所有删操作也都是极致快,没有元素移动的开销,比 vector/deque 的删除好用 10 倍,这是 list 最核心的使用场景:

  1. lst.pop_back() :删除最后一个元素 ✅✅✅

  2. lst.pop_front():删除第一个元素 ✅✅✅

  3. lst.erase(迭代器位置):删除指定位置的单个元素 ✅✅✅

  4. lst.clear() :清空所有元素,变成空链表 ✅✅✅

  5. lst.remove(值)list 独有神仙用法 → 直接删除链表中【所有等于这个值】的元素 ✅✅✅

    list<int> lst = {1,2,2,3,2,4};
    lst.pop_front(); // 删第一个 → [2,2,3,2,4]
    lst.pop_back(); // 删最后一个 → [2,2,3,2]
    lst.remove(2); // 删除所有值为2的元素 → [3] 【超级好用,不用自己遍历删】

✅ 【改 + 查】取值 / 修改(无下标,2 种方式,必记,唯一的小麻烦)

这是 list 唯一的缺点,没有下标,取值 / 改值只能用以下方式,没有捷径,记熟就行:

✔️ 查 / 改 首尾元素(最简单,高频!✅✅✅)

和 vector/deque 写法完全相同,直接用:

复制代码
lst.front();  // 获取第一个元素
lst.back();   // 获取最后一个元素

示例:

复制代码
list<int> lst = {1,2,3};
int a = lst.front(); // a=1
lst.back() = 300;    // 最后一个元素改成300 → [1,2,300]
✔️ 查 / 改 中间元素(唯一方式:迭代器 / 遍历)

因为没有下标,想改中间的元素,只能先遍历找到位置,再修改,比如范围 for 循环(最简单):

复制代码
list<int> lst = {1,2,3,4};
// 遍历+修改:所有元素乘2
for(int &num : lst){
    num *= 2;
}
// 此时lst: [2,4,6,8]

✅ 三、必用状态查询

复制代码
lst.empty();  // 判断是否为空,空=true,非空=false ✅✅✅
lst.size();   // 返回元素个数(长度) ✅✅✅

✔️ 重要:list 没有 capacity ()!链表是一个节点一个节点存的,有多少元素就占多少内存,没有「容量」的概念,不用记这个!


✅ 四、遍历 list

❌ 绝对不能用:普通 for 循环(i 从 0 到 size ())

因为 list 没有下标 lst[i],这种写法直接编译报错,新手最容易踩这个坑,记住就好!

✔️ 方式 1:范围 for 循环(C++11,最简洁、最常用、懒人首选)✅✅✅

99% 的场景用这个,写法和 vector/deque 一模一样,无脑写就行,最省心:

复制代码
list<int> lst = {1,2,3,4,5};
for(int num : lst){
    cout << num << " "; // 输出:1 2 3 4 5
}

想修改元素?加个 & 就行:for(int &num : lst)

✔️ 方式 2:迭代器遍历(面试会考,记格式即可)

写法和 vector/deque 几乎一样,唯一区别:list 的迭代器只能 ++/-- ,不能 it+1it-2(因为没有随机访问)

复制代码
list<int> lst = {1,2,3,4,5};
for(list<int>::iterator it = lst.begin(); it != lst.end(); it++){
    cout << *it << " "; // *it 取当前元素,输出同上
}

✅ 五、list 核心特性

✅ 优点(list 的价值,为啥要用它?3 个核心,全是强项)

  1. 任意位置增删元素,速度都是极致快 → O (1),这是 list 的「本命优势」,vector/deque 做不到;
  2. 两头增删(push_front/pop_front)也超快,和 deque 持平;
  3. 增删元素后,迭代器几乎不失效 → 只有被删除的那个迭代器失效,其他都能用,比 vector/deque 的迭代器安全太多;
  4. 内存灵活:链表是按需分配内存,不会像 vector 那样一次性扩容占多余内存。

✅ 缺点(list 的软肋,为啥不一直用它?2 个核心,避坑)

  1. 无随机访问,查询慢 → 想找第 n 个元素,只能从头遍历到 n,数据越多越慢;
  2. 遍历速度比 vector/deque 慢一点 → 链表节点内存不连续,CPU 缓存不友好,但是日常开发几乎感知不到;
  3. 内存占用比 vector 稍多:每个元素都要存一个节点,节点里有元素值 + 前后指针。

✅ 六、【封神必记】vector /deque/list 三者 选型原则

✅ 一句话总结,无脑套用,永远不会选错!(最核心的知识点)

  1. 如果你需要 频繁在【中间】增删元素 → 无脑用 list ✅(唯一最优解)
  2. 如果你只需要 尾部增删、频繁查询 / 随机访问 → 无脑用 vector ✅
  3. 如果你需要 头部 + 尾部增删、偶尔查询 → 无脑用 deque ✅
  4. 三者优先级:能 vector 不用 deque,能 deque 不用 list,除非必须用 list 的中间增删优势!

✅ 七、list 专属神仙小技巧

list 自带很多好用的成员函数,都是为链表量身定做的,效率极高,不用调用 STL 的通用算法,记下来直接用:

复制代码
list<int> lst = {5,2,9,1,3};

// 1. 排序:list自带sort(),默认从小到大 ✅✅✅
lst.sort(); // 排序后:[1,2,3,5,9]
lst.sort(greater<int>()); // 从大到小排序 → [9,5,3,2,1]

// 2. 去重:删除链表中所有重复元素 ✅✅✅
lst = {1,2,2,3,3,3};
lst.unique(); // 去重后:[1,2,3]

// 3. 反转链表:所有元素倒序 ✅✅✅
lst.reverse(); // 比如[1,2,3] → [3,2,1]

✅ 八、避坑注意事项

  1. ❌ 禁止用 lst[i]lst.at(i),list 无下标,编译报错;
  2. ❌ 迭代器不能跳步:比如 lst.begin()+1 是错的,只能 ++lst.begin()
  3. ✅ 中间增删优先用 list,查询优先用 vector,别搞反了,不然代码效率极低。

✅ 终极总结

  1. list 是双向链表,任意位置增删都超快,没有下标、不能随机访问;
  2. 核心操作:push_back/push_front/insert/erase/remove,这些使劲用;
  3. 选型:需要中间增删→list,尾增删查→vector,头尾增删→deque。
相关推荐
HalvmånEver1 小时前
Linux:深入剖析 System V IPC上(进程间通信八)
linux·运维·数据库·c++·system v·管道pipe
空山新雨后、2 小时前
从 CIFAR 到 ImageNet:计算机视觉基准背后的方法论
人工智能·深度学习·算法·计算机视觉
m0_748250032 小时前
C++ Web 编程
开发语言·前端·c++
YuTaoShao2 小时前
【LeetCode 每日一题】712. 两个字符串的最小ASCII删除和——(解法三)状态压缩
算法·leetcode·职场和发展
liliangcsdn2 小时前
LLM训练中batchsize与过拟合和泛化的关系
人工智能·算法·机器学习
承渊政道2 小时前
C++学习之旅【C++String类介绍】
c语言·c++·vscode·学习
muddjsv2 小时前
什么是算法?——现代视角下的一次凝视
算法
laplace01232 小时前
智能体经典范式构建
算法·langchain·大模型·agent
小雨下雨的雨2 小时前
Flutter鸿蒙共赢——色彩的流变:流体梯度网格与现代视觉重构
算法·flutter·华为·重构·交互·harmonyos·鸿蒙