list的使用
- 一.list基本的结构
- 1.环状双向链表
- 2.哨兵节点
- 3.迭代器
- 4.节点结构
- 5.链表遍历
- 6.迭代器失效
- 二.list的基本使用
- 1.`test01`函数:主要测试`std::list`的初始化方式及遍历
- 2.`test02`函数:主要测试`std::list`的常用成员函数操作
- 3.测试结果如下
- 三.list的其他操作(了解)
- 2. remove(按值移除)
- 3. remove_if(按条件移除)
- 4. unique(移除相邻重复)
- 5. merge(合并有序链表)
- 6. reverse(反转元素顺序)
一.list基本的结构
以上图片展示了一个 C++ 标准库中的 std::list 容器的内部结构:
1.环状双向链表
std::list
是环状双向链表。- 最后一个节点的
next
指向头节点。 - 头节点的
prev
指向最后一个节点。
2.哨兵节点
- 图中红色方框标注的是哨兵节点。
- 哨兵节点不存储实际数据。
- 哨兵节点的
next
指向第一个数据节点。 - 哨兵节点的
prev
指向最后一个数据节点。
3.迭代器
begin()
返回指向第一个数据节点的迭代器。end()
返回指向哨兵节点的迭代器。- 迭代器通过
_M_node
指针访问节点。
4.节点结构
- 每个节点包含数据值(如图中 0、2、3、4 等)。
- 包含指向前一个节点的指针(
prev
)。 - 包含指向后一个节点的指针(
next
)。
5.链表遍历
- 正向遍历:从
begin()
开始,通过next
指针依次访问节点。 - 逆向遍历:从
end()
开始,通过prev
指针依次访问节点。
6.迭代器失效
- 当链表结构改变时(如插入或删除节点),迭代器可能失效。
- 失效的迭代器不能再用于访问链表。
二.list的基本使用
对C++ 标准库中 std::list 容器部分成员函数的测试。展示了std::list 的多种操作方式,包括初始化、插入元素、调整大小、删除元素等。
#include<iostream>
#include<list>
#include<vector>
using namespace std;// 测试 list 的初始化和基本遍历
void test01()
{// 默认构造函数创建空列表list<int> lt1;// 使用初始化列表构造列表list<int> lt2 = { 0,1,2,3,4,5 };// 创建包含10个元素的列表,每个元素初始化为1list<int> lt3(10, 1);// 从数组中前5个元素构造列表int a[] = { 5,4,3,2,1,0 };list<int> lt4(a, a + 5);// 正确遍历方法:基于范围的for循环cout << "lt4: ";for (auto e : lt4){cout << e << " ";}cout << endl;
}// 测试 list 的插入、修改和删除操作
void test02()
{// 使用初始化列表构造并插入元素list<int> lt1 = { 0,1,2,3,4,5 };lt1.insert(lt1.begin(), 666); // 在头部插入元素// 复制构造list<int> lt2(lt1);lt2.push_back(999); // 在尾部添加元素lt2.resize(10, 77); // 调整大小,不足部分用77填充cout << "lt2: ";for (auto e : lt2){cout << e << " ";}cout << endl;// 复制列表并调整大小(截断)list<int> lt3 = lt2;lt3.resize(5, 0); // 调整为5个元素,超出部分截断cout << "lt3: ";for (auto e : lt3){cout << e << " ";}cout << endl;// 删除特定值的所有元素list<int> lt4(lt2);lt4.remove(77); // 删除所有值为77的元素cout << "lt4: ";for (auto e : lt4){cout << e << " ";}cout << endl;
}int main() {test01();cout << endl;test02();return 0;
}
以下是对代码中test01
和test02
函数测试内容的分要点梳理:
1.test01
函数:主要测试std::list
的初始化方式及遍历
-
初始化测试
- 默认构造:
list<int> lt1;
(创建空列表) - 初始化列表构造:
list<int> lt2 = {0,1,2,3,4,5};
(用初始化列表直接赋值元素) - 指定大小和值构造:
list<int> lt3(10, 1);
(创建包含10个元素、每个元素值为1的列表) - 数组范围构造:
list<int> lt4(a, a + 5);
(用数组a
中从a
到a+5
的元素初始化列表,即取前5个元素5,4,3,2,1
)
- 默认构造:
-
遍历测试
- 使用基于范围的for循环遍历
lt4
并输出元素,验证初始化结果是否正确。
- 使用基于范围的for循环遍历
2.test02
函数:主要测试std::list
的常用成员函数操作
-
插入操作
lt1.insert(lt1.begin(), 666);
:在lt1
的开头位置插入元素666
。
-
复制构造
list<int> lt2(lt1);
:通过复制构造函数,创建lt1
的副本lt2
。
-
尾部添加
lt2.push_back(999);
:在lt2
的末尾添加元素999
。
-
调整大小
lt2.resize(10, 77);
:将lt2
的大小调整为10,若原大小不足,用77
填充剩余位置。lt3.resize(5, 0);
:将lt3
的大小调整为5,若原大小超出,截断多余元素(填充值0
在此处无效,因仅截断不新增)。
-
删除元素
lt4.remove(77);
:删除lt4
中所有值为77
的元素。
-
遍历输出
- 所有列表均通过基于范围的for循环遍历输出,验证上述操作的结果是否正确。
3.测试结果如下
lt4: 5 4 3 2 1 lt2: 666 0 1 2 3 4 5 999 77 77
lt3: 666 0 1 2 3
lt4: 666 0 1 2 3 4 5 999
三.list的其他操作(了解)
### 1. splice(转移元素)
- 核心功能:把一个
list
里的元素(单个、区间或整个链表)转移到另一个list
里,原list
对应元素会被移除。 - 关键特点:
- 高效:直接调整链表指针,不用拷贝元素,处理大数据量时速度优势明显。
- “搬家式” 转移:原
list
被转移的元素位置会 “空出来”,目标list
把这些元素 “接过去” 。
- 典型场景:需要在不同链表间快速调整元素归属,比如链表拼接、拆分逻辑,像合并订单链表时,把临时链表的订单直接转移到主链表 。
2. remove(按值移除)
- 核心功能:遍历链表,把所有值和指定值一样的元素都删掉。
- 关键特点:
- 精准定值删除:简单直接,给定一个具体值(如
5
),就能清理链表里所有该值的元素。 - 遍历式删除:逐个检查元素值,匹配就删除,适合明确知道要删什么值的场景。
- 精准定值删除:简单直接,给定一个具体值(如
- 典型场景:数据清理时,删除链表中特定标识的元素,比如用户链表删除 ID 为
001
的用户记录 。
3. remove_if(按条件移除)
- 核心功能:依据自定义条件(像元素大小、是否满足某种规则),删除符合条件的元素。
- 关键特点:
- 灵活自定义:用 lambda 表达式、函数对象等设定条件(比如删偶数、删大于某个阈值的数 ),应对复杂删除需求。
- 逻辑拓展性强:不管是简单的数值判断,还是涉及元素对象属性的复杂逻辑,都能实现删除。
- 典型场景:业务规则筛选删除,比如订单链表删除金额小于
1
元的无效订单,学生链表删除成绩不及格的记录 。
4. unique(移除相邻重复)
- 核心功能:去掉链表中相邻的重复元素,只留一个。若要整体去重,一般得先排序让重复元素相邻。
- 关键特点:
- 相邻限定:只处理 “紧挨着” 的重复,没排序的话,分散的重复元素删不掉。
- 简化存储:适合对连续重复数据做 “压缩”,像统计链表中连续相同数值的个数后,保留一个代表值 。
- 典型场景:处理传感器连续采集的重复数据、文本处理中连续重复的字符(排序后去相邻重复 ),比如日志链表清理连续重复的状态记录 。
5. merge(合并有序链表)
- 核心功能:把两个已经有序的链表合并成一个新的有序链表,原被合并的链表会被清空。
- 关键特点:
- 前提条件:两个链表自身得是有序的(升序或降序,需保持一致 ),否则合并结果会混乱。
- 归并特性:常和归并排序搭配,拆分后合并有序子链表;也用于整合多个有序链表结果。
- 典型场景:归并排序算法里合并阶段、合并多个按时间排序好的日志链表,比如服务器把不同时段有序的操作日志链表合并 。
6. reverse(反转元素顺序)
- 核心功能:把链表中元素的顺序完全颠倒,第一个变最后一个,最后一个变第一个。
- 关键特点:
- 指针调整实现:通过修改链表节点间的指针指向,改变元素遍历顺序,不用额外空间拷贝。
- 逻辑简单直接:一步操作就能实现逆序,满足逆序处理数据需求。
- 典型场景:链表数据需要逆序展示(如聊天记录从最新到最早展示时反转 )、数学运算中链表存储数字的逆序处理(像链表存
1->2->3
代表123
,反转后计算321
)。
以上这些要点清晰梳理了每个函数 “能干什么、有啥特点、啥时候用”,方便理解 list
这些操作在实际场景的价值 。