当前位置: 首页 > news >正文

《C++ STL list 完全指南:从基础操作到特性对比,解锁链表容器高效用法》

在这里插入图片描述

🔥草莓熊Lotso:个人主页

❄️个人专栏: 《C++知识分享》 《Linux 入门到实践:零基础也能懂》

✨生活是默默的坚持,毅力是永久的享受!

🎬 博主简介:

在这里插入图片描述


文章目录

  • 前言:
  • 一. list 是什么?先搞懂底层结构
  • 二. list核心接口使用:从构造到修改
    • 2.1 构造函数:初始化一个list
    • 2.2 迭代器:遍历list的 "指针"
    • 2.3 容量与元素访问:判断空、查大小、取首尾
    • 2.4 元素修改:插入,删除,清空
    • 2.5 常用算法与操作:find、sort、unique、reverse
  • 三. list迭代器失效:只在删除时发生
    • 补充知识点:splice的使用
  • 四. list vs vector:该选哪个?
  • 结尾:


前言:

在 C++ STL 容器中,list 是与 vector 并驾齐驱的序列式容器,但二者底层结构截然不同 ——list 基于带头结点的双向循环链表,这使其在 “频繁插入删除” 场景下展现出独特优势。本文将从 list 的基础使用入手,深入讲解核心接口、迭代器特性,并对比 vector 明确其适用场景,帮你彻底掌握这个 “灵活的链表容器”。

在这里插入图片描述


一. list 是什么?先搞懂底层结构

list 的本质是双向循环链表,且带有一个"哨兵位头结点"(不存储有效数据),结构如下:

在这里插入图片描述

  • 双向:每个字节包含前驱指针 (prev) 和后继指针 (next) ,支持向前,向后遍历;
  • 循环:尾节点的 next 指向头结点,头结点的 prev 指向尾结点,形成闭环;
  • 哨兵位头结点:避免插入/删除时判断"是否为空"”是否为头结点“的麻烦,简化代码逻辑。

这种结构决定了 list 的核心特性:任意位置插入/删除效率高(O(1)),但不支持随机访问(访问元素需要遍历,O(N))。
头文件包含:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<list>
#include<algorithm>
using namespace std;

参考文档:list - C++ Reference


二. list核心接口使用:从构造到修改

list 的接口丰富,但重点掌握"构造,迭代器,容量,元素访问,修改’五大类即可满足日常开发需求,以下结合代码示例讲解。

2.1 构造函数:初始化一个list

list 提供4种常用构造方式,覆盖"空容器,n个相同元素,拷贝,区间初始化"场景:

构造函数接口说明代码示例
list()构造空 listlist<int> l1;(空链表,仅含头结点)
list(size_type n, const T& val = T())构造含 n 个 val 的 listlist<int> l2(5, 3);(元素:3,3,3,3,3)
list(const list& x)拷贝构造list<int> l3(l2);(l3 是 l2 的副本)
list(InputIterator first, InputIterator last)用 [first, last) 区间构造int arr[] = {1,2,3}; list<int> l4(arr, arr+3);(元素:1,2,3)

代码演示:

void test_list1()
{//展示其中两个,其实使用起来跟前面的vector差不多list<int> lt1;//无参构造list<int> lt2 = {1,2,3,4,5};list<int>::iterator it2 = lt2.begin();while (it2 != lt2.end()){cout << *it2 << " ";++it2;}cout << endl;for (auto e : lt2){cout << e << " ";}cout << endl;
}int main()
{test_list1();
}

在这里插入图片描述

2.2 迭代器:遍历list的 “指针”

list 的迭代器本质是 “结点指针的封装”,支持正向和反向遍历,核心接口如下(当然也可以使用范围for):

函数声明接口说明特点
begin() / end()正向迭代器:begin() 指向 list 第一个有效元素,end() 指向头结点(尾后位置)对迭代器执行 ++ 操作,迭代器向后移动,用于正向遍历所有有效元素
rbegin() / rend()反向迭代器:rbegin() 指向 list 最后一个有效元素,rend() 指向头结点(反向尾后位置)对迭代器执行 ++ 操作,迭代器向前移动,用于反向遍历所有有效元素

迭代器知识补充:
在这里插入图片描述
在这里插入图片描述

2.3 容量与元素访问:判断空、查大小、取首尾

list 不支持随机访问(没有 operator[]at()),仅提供 “判断空、获取大小、取首尾元素” 的接口:

函数声明接口说明代码示例(基于 l4 = {1,2,3,4}
empty()检测 list 是否为空,空则返回 true,非空返回 falsel4.empty();(返回 false,因 l4 含 4 个有效元素)
size()返回 list 中有效元素的个数,单位为无符号整数(size_typel4.size();(返回 4,对应 l4 中的元素 1、2、3、4)
front()返回 list 第一个有效元素的引用,支持读写操作(可直接修改元素值)l4.front() = 10;(修改后 l4 变为 {10,2,3,4}
back()返回 list 最后一个有效元素的引用,支持读写操作(可直接修改元素值)l4.back() = 40;(修改后 l4 变为 {10,2,3,40}

注意:front()back() 仅在 list 非空时使用,若为空会触发未定义行为(建议先 empty() 判断)。

2.4 元素修改:插入,删除,清空

list 的核心优势在"修改操作",任意位置插入/删除仅需调整指针,效率极高,常用接口如下:

函数声明接口说明代码示例(基于 l4 = {10,2,3,40}
push_front(const T& val)list 头部(第一个个有效元素之前)插入值为 val 的元素l4.push_front(0);(插入后 l4 变为 {0,10,2,3,40}
pop_front()删除 list 的第一个个有效元素l4.pop_front();(删除后 l4 变回 {10,2,3,40}
push_back(const T& val)list 尾部(最后一个有效元素之后)插入值为 val 的元素l4.push_back(50);(插入后 l4 变为 {10,2,3,40,50}
pop_back()删除 list 的最后一个有效元素l4.pop_back();(删除后 l4 变回 {10,2,3,40}
insert(iterator pos, const T& val)在迭代器 pos 指向的位置之前插入值为 val 的元素,返回指向新插入元素的迭代器auto it = l4.begin(); ++it; l4.insert(it, 15);it 初始指向 2,插入后 l4 变为 {10,15,2,3,40}
erase(iterator pos)删除迭代器 pos 指向的元素,返回指向被删除元素下一个元素的迭代器it = l4.begin(); ++it; l4.erase(it);it 指向 15,删除后 l4 变回 {10,2,3,40}
clear()清空 list 中所有有效元素(仅保留头结点),清空后 size() 为 0l4.clear();(清空后 l4 无有效元素,l4.size() 返回 0)

2.5 常用算法与操作:find、sort、unique、reverse

list 提供多个实用成员函数和适配的通用算法,用于处理查找、排序、去重、反转等场景,具体如下:

函数类型函数声明 / 调用方式接口说明代码示例(基于 list<int> l = {3,1,4,1,5,9}注意事项
算法函数(需包含 <algorithm>find(iterator first, iterator last, const T& val)[first, last) 区间查找值为 val 的元素,返回首个匹配元素的迭代器;若未找到,返回 last#include <algorithm> auto it = find(l.begin(), l.end(), 4); // it 指向元素 4(值为4的位置) 1. 属于 STL 通用算法,非 list 成员函数
2. 时间复杂度 O(N),需遍历查找
成员函数sort()list 元素进行升序排序(默认按 < 比较)l.sort(); // 排序后 l = {1,1,3,4,5,9} 1. list 不支持随机访问,不能用 STL 通用 sort 算法,需用自身成员函数
2. 底层通常为归并排序,时间复杂度 O(N log N)
成员函数unique()移除连续重复的元素(只保留第一个),返回指向最后一个有效元素后位置的迭代器l.sort(); // 先排序使重复元素连续:{1,1,3,4,5,9} l.unique(); // 去重后 l = {1,3,4,5,9} 1. 仅移除“连续重复”元素,需先 sort() 使相同元素相邻才能完全去重
2. 时间复杂度 O(N)
成员函数reverse()反转 list 中所有元素的顺序l.reverse(); // 原 l={3,1,4,1,5,9} → 反转后 {9,5,1,4,1,3} 仅调整节点指针方向,时间复杂度 O(N),效率极高

代码演示:

void test_list2()
{list<int> lt1;//无参构造list<int> lt2 = { 1,2,3,4,5 };auto pos = find(lt2.begin(),lt2.end(),3);if (pos != lt2.end()){lt2.insert(pos, 30);//pos没有失效,因为没有扩容lt2.erase(pos);//pos失效了//cout << *pos << endl;}for (auto e : lt2){cout << e << " ";}cout << endl;
}void test_list3()
{list<int> lt2 = { 1,2,4,3,5 };//sort(lt2.begin(), lt2.end());//不支持lt2.sort();for (auto e : lt2){cout << e << " ";}cout << endl;
}void test_list4()
{list<int> lt3 = { 1,2,2,3,3,2,3,4,5 };for (auto e : lt3){cout << e << " ";}cout << endl;lt3.sort();lt3.unique();//去重for (auto e : lt3){cout << e << " ";}cout << endl;
}int main()
{//test_list1();test_list2 ();test_list3();test_list4();
}

在这里插入图片描述


三. list迭代器失效:只在删除时发生

前面说过,此处大家可将迭代器暂时理解成类似于指针,迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。
迭代器失效是使用 list 时的核心注意点,但其失效规则比 vector 简单得多:

  • 插入时: list 插入仅需调整指针,不会移动现有节点,因此所有迭代器都不会失效
  • 删除时: 仅指向 “被删除节点” 的迭代器失效,其他迭代器(指向未删除节点)不受影响。

补充知识点:splice的使用

void test_list5()
{//将4这个节点挪到头位置list<int>lt4 = { 1,2,3,4,5 };for (auto e : lt4){cout << e << " ";}cout << endl;auto pos = find(lt4.begin(), lt4.end(),4);lt4.splice(lt4.begin(), lt4, pos);for (auto e : lt4){cout << e << " ";}cout << endl;}int main()
{//test_list1();//test_list2 ();//test_list3();//test_list4();test_list5();
}

在这里插入图片描述


四. list vs vector:该选哪个?

listvector 是 STL 中最常用的两个序列容器,但适用场景完全不同,核心差异如下表:

vectorlist
底层结构动态顺序表,一段连续空间带头结点的双向循环链表
随机访问支持随机访问,访问某个元素效率O(1)不支持随机访问,访问某个元素效率O(N)
插入和删除任意位置插入和删除效率低,需要搬移元素,时间复杂度为O(N),插入时有可能需要增容,增容:开辟新空间,拷贝元素,释放旧空间,导致效率更低任意位置插入和删除效率高,不需要搬移元素,时间复杂度为O(1)
空间利用率底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高底层节点动态开辟,小节点容易造成内存碎片,空间利用率低,缓存利用率低
迭代器原生态指针对原生态指针(节点指针)进行封装
迭代器失效在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器需要重新赋值否则会失效插入元素不会导致迭代器失效,删除元素时,只会导致当前迭代器失效,其他迭代器不受影响
使用场景需要高效存储,支持随机访问,不关心插入删除效率大量插入和删除操作,不关心随机访问

选择建议:

  • 若需 “快速查改元素”(如数组下标访问)、数据量固定或尾插为主 → 选 vector
  • 若需 “频繁在中间插入 / 删除”(如链表排序、队列实现)、无需随机访问 → 选 list

结尾:

往期回顾:

结语:list 以双向循环链表为骨,凭 “任意位置插入删除高效” 立足,却也因 “不支持随机访问” 有其局限。掌握它的核心接口、迭代器失效规则和find/sort/unique/reverse 等实用操作,再结合场景与 vector 灵活取舍 —— 无需追求 “全能容器”,选对工具,才能让代码更高效。

✨把这些内容吃透超牛的!放松下吧✨
ʕ˘ᴥ˘ʔ
づきらど

在这里插入图片描述

http://www.dtcms.com/a/464925.html

相关文章:

  • 刀客doc:亚马逊广告再下一城,拿下微软DSP广告业务
  • Agent 开发设计模式(Agentic Design Patterns )第 3 章:并行化模式
  • 配电系统接地 | TT, TN-C, TNC-S,TN-S, IT
  • Qemu-NUC980(七):Timer定时器
  • 20251009
  • CanFestival 主站-NMT初始化
  • Transformer基础之注意力机制
  • 模板式网站价格网页设置快捷键
  • 重要通知:spring-ai-hunyuan 已兼容 Spring AI 稳定版!
  • 惊艳的网站工作室网页模板
  • 如何在 Spring Boot 应用中配置多个 Spring AI 的 LLM 客户端
  • 【实时Linux实战系列】实时系统的可观测性:Prometheus 与 Grafana 集成
  • HTML 元素:构建网页的基础
  • HTML应用指南:利用GET请求获取全国中国建设银行网点位置信息
  • AI编程 | 基于飞书知识库+多模态大模型,打造B站视频AI笔记自动生成系统
  • 专门做预售的网站做app需要学什么编程
  • [VoiceRAG] RAG工具集 | attach_rag_tools | _search_tool | _report_grounding_tool
  • ppo笔记2
  • 小九源码-springboot082-java旅游攻略平台
  • 从 Kotlin 编译器 API 的变化开始: 2.2.2X -> 2.3.0-Beta1
  • go中调用合约
  • 用Python可视化国庆期间旅游概况与消费趋势
  • InitLWIP() 初始化
  • Python爬虫实战:获取新浪旅游热门景点排行榜及数据分析
  • C++设计模式之行为型模式:中介者模式(Mediator)
  • 为什么苏州网络进不了网站ps设计网站
  • [C# starter-kit] Domain Entities | `AuditableEntity`基类 | 跟踪变化 | 软删除
  • 深度复盘+完整源码:我把 libuv 的高性能内存池,用现代 C++ 给你扒了个底朝天
  • GUI 自动化与接口自动化:概念、差异与协同落地
  • 网站建设公司是怎么找客户idc网站模板源码下载