首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【C++STL】被 List 接口的各种方法搞晕了?关键核心就这几个!

【C++STL】被 List 接口的各种方法搞晕了?关键核心就这几个!

作者头像
用户11960591
发布2025-12-23 15:49:26
发布2025-12-23 15:49:26
1440
举报

前言:在上一篇文章中,我们深入探讨了vector的底层原理。今天,让我们一起来认识另一个重要容器 - list。理解一个容器的最佳方式是从它的接口入手,下面我们就从list的基本使用方法开始讲解。

一、list的介绍

对于一个容器我们首先要了解它的底层长什么样,才能更好的去认识这个容器。像vector它的底层实际上就是一个顺序表,而C++的list的底层实际上就是一个带头双向链表。

在这里插入图片描述
在这里插入图片描述

以上就是整个链表的结构,但要注意这里的结点不是像vector那样是连续的,list每个结点之间是不连续的!接下来就来看看list的一些核心接口。

二、list的使用

2.1构造相关的接口

只要是学习容器我们一般都从它的构造相关的接口开始看,然后再逐步过渡到,迭代器,增删查改,算法这些接口。

构造函数接口说明

构造函数签名

功能描述

list(size_type n, const value_type& val = value_type())

构造一个包含n个值为val的元素的list,若val未指定则使用默认值

list()

构造一个空的list

list(const list& x)

拷贝构造函数,通过另一个list对象x构造新list

list(InputIterator first, InputIterator last)

通过迭代器区间[first, last)中的元素构造list

注意事项:

  • 默认值行为val参数未提供时,使用value_type的默认构造值(如int0,类类型调用默认构造函数)。
  • 迭代器区间[first, last)需为有效的输入迭代器范围,左闭右开。
  • 拷贝构造:新listx的元素完全独立,深拷贝。

代码示例:

代码语言:javascript
复制
void list_test1()
{
	//默认构造
	list<int> lit1;

	//n个val构造
	list<int> lit2(10, 1);

	//初始化列表构造
	list<int> lt3 = { 1,2,3,4,5,6,1,1,1,1};
	
	//拷贝构造 lit4(lit3)
	list<int> lit4(lit3);

	//迭代器区间构造
	//list<int> lit5(lit2.begin()+3, lit2.end());list的迭代器不支持+这种操作后面会讲
	list<int> lit5(lit2.begin(), lit2.end());

	
	int a[] = { 1,2,3,4,5,6,7 };
	list<int> lit6(a, a + 3);


	for (auto e : lit5)
	{
		cout << e<<" ";
	}
	cout << endl;
}

2.2 list的迭代器相关接口

函数名

返回值类型

描述

begin

迭代器

返回指向容器第一个元素的迭代器

end

迭代器

返回指向容器末尾(最后一个元素的下一个位置)的迭代器

rbegin

反向迭代器 (reverse_iterator)

返回指向容器最后一个元素的反向迭代器(即end位置)

rend

反向迭代器 (reverse_iterator)

返回指向容器第一个元素前一个位置的反向迭代器(即begin位置)

对于list迭代器的使用与前面类似这里就不给代码示例了。实际上C++设计的这种迭代器就是一种展现C++封装的特性,这使得我们不用去关系各种容器的底层是怎么封装的,只管使用这些迭代器就行。 因为这些迭代器虽然在底层的实现上可能不同但是在使用上功能和用法都是相似的,所以这也是迭代器更底层解耦的体现!

list迭代器的补充:

在这里插入图片描述
在这里插入图片描述

看到这里相信很多读者朋友又会有疑问:为什么要将这些迭代器区分开?不同容器的迭代器之间有什么不同?是按照什么标准来区分的?下面就通过一个表格来回答这些问题:

迭代器类型

支持的操作

适用的数据结构

特性说明

单向迭代器

++

单链表、哈希表(如 unordered_map)

仅能沿一个方向移动,用于遍历单向结构,无法回退。

双向迭代器

++、--

红黑树(如 map)、双向链表(如 list)

可双向移动,支持向前和向后遍历,适用于需要双向操作的场景。

随机迭代器

++、--、+、-、[]

string、vector、双端队列 deque

支持随机访问,可通过下标或指针算术直接访问任意位置元素,迭代灵活性最高。

注意事项:

  • 几乎所有的迭代器都支持:++*!= 这些通用的操作!
  • 单向迭代器可以看成双向迭代器特例双向迭代器可以看成随机迭代器特例,也就是单向迭代器能用的算法双向能用;随机迭代器也能用。双向迭代器能用的算法,随机迭代器也能用!
代码语言:javascript
复制
list<int> lit5(lit2.begin()+3, lit2.end());

再来看看这段代码,之所以不能这样使用是因为list首先的迭代器是双向迭代器,双向迭代器不支持+这种操作!

2.3 空间与元素访问相关接口

函数名

返回值类型

功能描述

empty

bool

检测list是否为空,是返回true,否则返回false

size

size_t(或等效整型)

返回list中有效节点的个数

front

T&(模板类型引用)

返回list的第一个节点中存储值的引用

back

T&(模板类型引用)

返回list的最后一个节点中存储值的引用

注意事项:

  • frontback通常要求list非空,否则可能引发未定义行为(如断言错误或异常)。
  • size的时间复杂度需根据实现确定(如O(1)或O(n))。
  • 实际接口可能包含const重载版本(如const T& front() const)。

代码示例:

代码语言:javascript
复制
void list_test2()
{
	list<int> lit1(10,1);
	lit1.push_back(10);

	//size为11 就是有效结点个数
	cout << lit1.size() << endl;
	if (!lit1.empty())
	{
		for (auto e : lit1)
		{
			cout << e << " ";
		}
	}
	cout << endl;
	lit1.front()=2;//返回的是首元素的引用可以修改
	lit1.back() = 100;//返回的是最后一个元素的引用
	for (auto e : lit1)
	{
		cout << e << " ";
	}
}

2.4 list修改相关的接口使用

操作

语法示例

功能描述

时间复杂度

push_front

list.push_front(val)

在链表头部插入值为 val 的元素

O ( 1 ) O(1) O(1)

pop_front

list.pop_front()

删除链表第一个元素

O ( 1 ) O(1) O(1)

push_back

list.push_back(val)

在链表尾部插入值为 val 的元素

O ( 1 ) O(1) O(1)

pop_back

list.pop_back()

删除链表最后一个元素

O ( 1 ) O(1) O(1)

insert

list.insert(pos, val)

在迭代器 pos 位置插入值为 val 的元素

O ( 1 ) O(1) O(1)

erase

list.erase(pos)

删除迭代器 pos 位置的元素

O ( 1 ) O(1) O(1)

swap

list1.swap(list2)

交换两个链表的所有元素

O ( 1 ) O(1) O(1)

clear

list.clear()

清空链表中所有有效元素

O ( n ) O(n) O(n)

O(1)

pop_frontlist.pop_front()删除链表第一个元素

O(1)

push_backlist.push_back(val)在链表尾部插入值为 val 的元素

O(1)

pop_backlist.pop_back()删除链表最后一个元素

O(1)

insertlist.insert(pos, val)在迭代器 pos 位置插入值为 val 的元素

O(1)

eraselist.erase(pos)删除迭代器 pos 位置的元素

O(1)

swaplist1.swap(list2)交换两个链表的所有元素

O(1)

clearlist.clear()清空链表中所有有效元素

O(n)

注意事项:

  • 迭代器有效性inserterase 操作需传入有效的迭代器位置。
  • 异常安全pop_frontpop_back 在空链表上调用可能导致未定义行为。
  • 复杂度clear 的时间复杂度为
O(n)

,其余操作均为常数时间

O(1)

代码示例:

代码语言:javascript
复制
void list_test4()
{
	list<int> lit1;
	lit1.push_back(2);//尾插
	lit1.push_back(20);
	lit1.push_front(10);//头插
	lit1.push_front(100);
	for (auto e : lit1)
	{
		cout << e << " ";
	}
	cout << endl;
	lit1.pop_back();//尾删
	lit1.pop_front();//头删
	/*for (auto e : lit1)
	{
		cout << e << " ";
	}
	cout << endl;*/

	//在pos位置插入
	auto it = find(lit1.begin(), lit1.end(), 20);
	lit1.insert(it, 200);//插入200 
	
	auto it2 = find(lit1.begin(), lit1.end(), 200);
	//lit1.erase(it2);//删除200 引发迭代器失效!
	//将迭代器更新一下 能避免失效
	it2=lit2.erase(it2);
	for (auto e : lit1)
	{
		cout << e << " ";
	}
	cout << endl;



	list<int> lit2(10,2);
	list<int> lit3(10,20);
	lit2.swap(lit3);
	for (auto e : lit2)
	{
		cout << e << " ";
	}
	cout << endl;
}

迭代器失效问题:

vector一样list在执行删除操作时也会引发迭代器失效。这里我们先简单的将list的迭代器理解成指针,那么迭代器失效的原因就是指向了被删除的结点,此时的迭代器就相当于一个野指针!而由于链表是一个循环链表所以插入是不会引起迭代器失效的。所以与vector一样使用erase的返回值更新迭代器就能避免失效的问题!

三、总结

以上就是本篇文章的所有内容了,衷心感谢您的阅读!这篇文章凝聚了大量心血,如果觉得有帮助,请不吝点赞支持。有任何疑问,欢迎随时私信交流探讨。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-12-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、list的介绍
  • 二、list的使用
    • 2.1构造相关的接口
    • 2.2 list的迭代器相关接口
    • 2.3 空间与元素访问相关接口
    • 2.4 list修改相关的接口使用
  • 三、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档