C++笔记——STL list

在 C++ 的 STL(标准模板库)中,list双向循环链表 容器,它和我们熟悉的vectorarray特性截然不同,擅长处理频繁插入、删除元素的场景,是 C++ 开发中高频使用的序列式容器。

这篇笔记会从核心特性、常用操作、底层原理、适用场景四个维度,帮你彻底掌握 STL list 的使用。

一、list 核心特性(必记)

  1. 底层结构 :双向循环链表,每个元素都包含数据 + 前驱指针 + 后继指针 ,元素在内存中不连续存储
  2. 访问效率不支持随机访问 (不能用[]at()直接访问第 n 个元素),只能通过迭代器从头 / 尾遍历查找。
  3. 插入 / 删除 :在任意位置 插入、删除元素的效率都是O(1)(常数时间),且不会导致迭代器失效(除了指向被删除元素的迭代器)。
  4. 迭代器 :属于双向迭代器 ,只能++/--,不支持+n-n跳跃操作。
  5. 对比 vector
    • vector:内存连续,支持随机访问,尾部插入 / 删除快,中间插入 / 删除慢;
    • list:内存不连续,不支持随机访问,任意位置插入 / 删除都快。

二、list 必备头文件

使用list必须包含头文件,命名空间默认使用std

cpp 复制代码
#include <list>   // 核心头文件
using namespace std;

三、list 常用操作(代码示例)

1. 定义与初始化

list 支持多种初始化方式,和其他 STL 容器用法一致:

cpp 复制代码
// 1. 空list
list<int> l1;

// 2. 指定大小,初始值为0
list<int> l2(5);  // 5个0

// 3. 指定大小+初始值
list<int> l3(5, 10);  // 5个10

// 4. 拷贝初始化
list<int> l4(l3);  // 拷贝l3所有元素

// 5. 区间初始化
int arr[] = {1,2,3,4,5};
list<int> l5(arr, arr+5);  // 用数组初始化

2. 元素访问

list没有 [] 运算符,只能访问首尾元素:

cpp 复制代码
list<int> l = {1,2,3,4,5};

// 访问第一个元素
cout << l.front() << endl;  // 输出1

// 访问最后一个元素
cout << l.back() << endl;   // 输出5

3. 插入元素

list 支持头部、尾部、任意位置插入,效率极高:

cpp 复制代码
list<int> l;

// 1. 尾部插入
l.push_back(10);
l.emplace_back(20);  // 推荐:直接在容器内构造,效率更高

// 2. 头部插入
l.push_front(5);
l.emplace_front(1);  // 推荐

// 3. 任意位置插入(需要迭代器)
auto it = l.begin();
++it;  // 迭代器只能++,不能+1
l.insert(it, 8);  // 在第二个位置插入8

4. 删除元素

删除操作同样高效,注意区分不同删除函数:

cpp 复制代码
list<int> l = {1,3,5,3,7};

// 1. 删除尾部元素
l.pop_back();

// 2. 删除头部元素
l.pop_front();

// 3. 删除指定位置元素
auto it = l.begin();
l.erase(it);  // 删除迭代器指向的元素

// 4. 删除所有指定值的元素
l.remove(3);  // 删除所有值为3的元素

// 5. 清空所有元素
l.clear();
  1. 容量操作
cpp 复制代码
list<int> l = {1,2,3};

// 获取元素个数
cout << l.size() << endl;  // 输出3

// 判断是否为空
cout << l.empty() << endl; // 输出0(false)

// 重新指定大小
l.resize(5);    // 扩大到5个元素,新增元素默认为0
l.resize(2, 10); // 缩小到2个元素,多余元素被删除

6. 迭代器遍历

list 只能用迭代器范围 for遍历,不能用下标:

cpp 复制代码
list<int> l = {10,20,30};

// 方式1:迭代器遍历(推荐)
for(list<int>::iterator it = l.begin(); it != l.end(); ++it){
    cout << *it << " ";  // 输出10 20 30
}

// 方式2:auto简化迭代器
for(auto it = l.begin(); it != l.end(); ++it){
    cout << *it << " ";
}

// 方式3:范围for
for(int num : l){
    cout << num << " ";
}

7. 高级操作

list 独有的高效操作,其他容器很难实现:

cpp 复制代码
list<int> l1 = {1,3,5};
list<int> l2 = {2,4,6};

// 1. 拼接两个list(直接转移节点,不拷贝元素)
l1.splice(l1.end(), l2);  // 把l2所有元素拼接到l1尾部

// 2. 反转链表
l1.reverse();  // {6,5,4,3,2,1}

// 3. 排序(默认升序)
l1.sort();  // {1,2,3,4,5,6}

// 4. 去重(必须先排序,否则只删除连续重复元素)
l1.push_back(6);
l1.unique();  // 删除连续重复的6

四、list 底层原理(简单理解)

list 的每个节点结构大致如下(STL 源码简化版):

cpp 复制代码
template <class T>
struct ListNode {
    T data;          // 数据
    ListNode* prev;  // 前驱指针,指向前一个元素
    ListNode* next;  // 后继指针,指向后一个元素
};
  • 链表通过prevnext串联所有节点,内存分散在堆中;
  • 插入 / 删除只需要修改指针指向,不需要移动大量元素,这是它高效的核心原因。

五、list 适用场景

优先使用list的场景:

  1. 需要频繁在中间位置插入、删除元素;
  2. 不需要随机访问元素,只需要遍历操作;
  3. 对插入 / 删除效率要求极高,且元素个数动态变化大。

不建议使用list的场景:

  1. 需要频繁访问第 n 个元素(用vector);
  2. 内存空间紧张(list 每个节点多存两个指针,内存开销更大)。

六、常见坑点(避坑指南)

  1. 不能用下标访问list[0] 会直接编译报错;
  2. 迭代器不能跳跃it + 2 非法,只能++it--it
  3. 去重前先排序unique()只删除连续重复元素,不排序则去重不彻底;
  4. splice 是转移元素:不是拷贝,执行后原 list 元素会被清空。

总结

  1. STL list 是双向循环链表 ,核心优势是任意位置高效插入 / 删除
  2. 不支持随机访问,迭代器仅支持++/--
  3. 常用操作:push_front/pop_frontinsert/erasesplicesortreverse
  4. 适用频繁增删、无需随机访问的场景,和 vector 互补使用。
相关推荐
彷徨而立2 小时前
【C/C++】在头文件中定义全局变量的方法
c语言·开发语言·c++
脱氧核糖核酸__2 小时前
LeetCode热题100——206.反转链表(迭代法)
c++·leetcode·链表
今天你TLE了吗2 小时前
HelloAgents学习:PartⅠChapterⅠ初识智能体
人工智能·笔记·学习·agent·智能体
小茴香3532 小时前
React学习笔记(一)
笔记·学习·react.js
YaBingSec2 小时前
玄机网络安全靶场:JBoss 5.x_6.x 反序列化漏洞(CVE-2017-12149)
android·网络·笔记·安全·web安全·ssh
小龙报2 小时前
【数据结构与算法】一文拿捏链式二叉树:遍历 + 统计 + 层序 + 完全树
java·c语言·开发语言·c++·人工智能·语言模型·visual studio
做cv的小昊2 小时前
【TJU】研究生应用统计学课程笔记(5)——第二章 参数估计(2.3 C-R不等式)
c语言·笔记·线性代数·机器学习·数学建模·r语言·概率论
量子炒饭大师2 小时前
【优化算法:双指针算法刷题宝典】—— 盛最多水的容器
c++·算法
其实防守也摸鱼2 小时前
MarkText:开源免费的 Markdown 编辑器新星
笔记·pdf·编辑器·免费·工具·调试·可下载