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 互补使用。
相关推荐
苏宸啊6 小时前
IPC管道
linux·c++
BestOrNothing_20157 小时前
ROS2 话题通信实战:消息对象、Publisher 发布器与 Subscriber 订阅器保姆级教程
c++·ros2·subscriber·publisher·话题通信
三品吉他手会点灯8 小时前
C语言学习笔记 - 44.运算符和表达式 - 运算符2 - 除法与取余运算符
c语言·开发语言·笔记·算法
艾iYYY8 小时前
string 类的模拟实现
android·服务器·c语言·c++·算法
为何创造硅基生物8 小时前
C++ virtual void StartNetwork() = 0; // 纯虚:子类必须实现,否则不能 new。
c++
2601_colin8 小时前
Codex插件全流程实战指南
开发语言·经验分享·笔记·微信开放平台
疯狂打码的少年9 小时前
输入输出控制方式:DMA(直接存储器存取)
网络·笔记
知无不研9 小时前
对套接字的深入理解
linux·服务器·网络·c++·socket·网络套接字
cuso4win9 小时前
Feed 流面试笔记
笔记·面试·职场和发展
hai31524754310 小时前
FlashAttention C语言(C++)实现(展示版)
c语言·开发语言·c++·人工智能·算法