✅ 核心一句话总结
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 的核心价值,优先级无差别,按需用即可:
-
lst.push_back(值):尾部追加元素 ✅✅✅ -
lst.push_front(值):头部插入元素 ✅✅✅ -
lst.insert(迭代器位置, 值):指定位置插入元素,超快! ✅✅✅ (vector/deque 这里巨慢,list 的王牌) -
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 最核心的使用场景:
-
lst.pop_back():删除最后一个元素 ✅✅✅ -
lst.pop_front():删除第一个元素 ✅✅✅ -
lst.erase(迭代器位置):删除指定位置的单个元素 ✅✅✅ -
lst.clear():清空所有元素,变成空链表 ✅✅✅ -
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+1、it-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 个核心,全是强项)
- 任意位置增删元素,速度都是极致快 → O (1),这是 list 的「本命优势」,vector/deque 做不到;
- 两头增删(push_front/pop_front)也超快,和 deque 持平;
- 增删元素后,迭代器几乎不失效 → 只有被删除的那个迭代器失效,其他都能用,比 vector/deque 的迭代器安全太多;
- 内存灵活:链表是按需分配内存,不会像 vector 那样一次性扩容占多余内存。
✅ 缺点(list 的软肋,为啥不一直用它?2 个核心,避坑)
- 无随机访问,查询慢 → 想找第 n 个元素,只能从头遍历到 n,数据越多越慢;
- 遍历速度比 vector/deque 慢一点 → 链表节点内存不连续,CPU 缓存不友好,但是日常开发几乎感知不到;
- 内存占用比 vector 稍多:每个元素都要存一个节点,节点里有元素值 + 前后指针。
✅ 六、【封神必记】vector /deque/list 三者 选型原则
✅ 一句话总结,无脑套用,永远不会选错!(最核心的知识点)
- 如果你需要 频繁在【中间】增删元素 → 无脑用 list ✅(唯一最优解)
- 如果你只需要 尾部增删、频繁查询 / 随机访问 → 无脑用 vector ✅
- 如果你需要 头部 + 尾部增删、偶尔查询 → 无脑用 deque ✅
- 三者优先级:能 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]
✅ 八、避坑注意事项
- ❌ 禁止用
lst[i]或lst.at(i),list 无下标,编译报错; - ❌ 迭代器不能跳步:比如
lst.begin()+1是错的,只能++lst.begin(); - ✅ 中间增删优先用 list,查询优先用 vector,别搞反了,不然代码效率极低。
✅ 终极总结
- list 是双向链表,任意位置增删都超快,没有下标、不能随机访问;
- 核心操作:push_back/push_front/insert/erase/remove,这些使劲用;
- 选型:需要中间增删→list,尾增删查→vector,头尾增删→deque。