【C++】介绍STL中list容器的常用接口

news2024/11/15 11:05:31

目录

一、STL中的list简介

二、构造函数

2.1 默认构造函数

2.2 填充构造(用n个相同的值构造)

2.3 迭代器构造 

2.4 拷贝构造和赋值运算符重载 

三、迭代器

3.1 正向迭代器

3.2 反向迭代器

四、容量相关 

4.1 获取list中有效数据的个数

4.2 判断list是否为空 —— empty

五、元素访问

5.1 获取第一个有效节点的数据

5.2 获取最后一个有效节点的数据

六、修改相关

6.1 头插、尾插 —— push_front、push_back

6.2 尾插、尾删 —— pop_front、pop_back 

6.3 在任意位置插入

6.4 在任意位置删除 —— erase

6.5 交换两个list对象

6.6 清空list中的有效节点

七、链表操作

7.1 将一个list上的数据转移到另一个list —— splice

7.2 删除某个特定数据

7.3 给数据排序 —— sort

7.4 去除重复数据 —— unique

7.5 反转数据 —— reverse

八、list与vector的对比 


一、STL中的list简介

1. list是可以在常数时间(O(1))内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代

2. listforward_list非常相似,最主要的不同在于forward_list是单链表,只能向前迭代,这让forward_list更简单高效。

3. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。

4. 与其他的序列式容器(arrayvector等)相比,list通常在任意位置进行插入、移除元素的执行效率更好。

5. 与其他序列式容器相比,listforward_list最大的缺陷是不支持任意位置的随机访问,必须从已知的位置(比如头部或者尾部)迭代到目标位置,而迭代到目标位置需要线性的时间(O(n))开销;除此之外list还需要一些额外的空间,以保存每个节点的相关联系。

【文档描述】


二、构造函数

2.1 默认构造函数

【代码演示】

#include <iostream>
#include <list>
#include <string>
using namespace std;

int main()
{
	//默认拷贝拷贝构造 list<T> lt;(T为list存储的数据类型)
	list<string> ls;

	return 0;
}

【输出结果】

        注:list不像stringvectorlist没有容量capacity这个属性。 


2.2 填充构造(用n个相同的值构造)

【代码演示】

#include <iostream>
#include <list>
using namespace std;

int main()
{
	//填充构造    list (size_type n, const value_type& val = value_type())

	list<int> li(5, 20);

	//如果不指定val的值就会初始化为相应类型的默认值
	//int为0 double为0.0 char为'\0'……
	list<double> ld(3);

	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;

	for (const auto& k : ld)
	{
		cout << k << " ";
	}
	cout << endl;
	return 0;
}

【输出结果】


2.3 迭代器构造 

【代码演示】

#include <iostream>
#include <list>
#include <string>
using namespace std;

int main()
{
	//迭代器构造 list (InputIterator first, InputIterator last) 
    //迭代器区间为左闭右开 -》 [first, last)

	//用数组构造
	int array[] = { 1,2,3,4,5,6 };
	list<int> li(array, array + sizeof(array) / sizeof(array[0]));

	//这个其实是先用数组构造了一个list<int> temp 
    //然后用拷贝构造将temp拷贝给了li_another -》 list<int> li_another(temp)
	list<int> li_another{ 1,2,3,4,5 };

	//可以用别的容器的迭代器来构造list
	string s("Hello list!");
	list<char> lc(s.begin(), s.end());

	//可以用list的迭代器来构造list
	list<char> lc_copy(++lc.begin(), --lc.end());

	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;

	for (const auto& k : li_another)
	{
		cout << k << " ";
	}
	cout << endl;

	for (const auto& k : lc)
	{
		cout << k;
	}
	cout << endl;

	for (const auto& k : lc_copy)
	{
		cout << k;
	}
	cout << endl;
	return 0;
}

【输出结果】


2.4 拷贝构造和赋值运算符重载 

【代码演示】

#include <iostream>
#include <list>
using namespace std;

int main()
{
	//拷贝构造 list (const list& x)
    //赋值运算符重载 list& operator=(const list& x)

	list<int> li{ 1,2,3,4,5 };

	//拷贝构造
	list<int> li_copy(li);

	//赋值运算符重载
	list<int> li_another = li;

	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;	

	for (const auto& k : li_copy)
	{
		cout << k << " ";
	}
	cout << endl;

	for (const auto& k : li_another)
	{
		cout << k << " ";
	}
	cout << endl;
	return 0;
}

【输出结果】 


三、迭代器

【示意图】

3.1 正向迭代器

【代码演示】

#include <iostream>
#include <list>
using namespace std;

int main()
{
	list<int> li{ 1,2,3,4,5 };
	cout << *li.begin() << endl;
	cout << *(--li.end()) << endl;

	return 0;
}

        list的迭代器不能直接使用加法或减法(如+1或-1)来实现迭代器位置的更换,因为list的各个节点并不在一块连续的空间,如果直接通过加减法来实现迭代器位置的更换会造成非法访问。所以我们需要使用重载过后的前置或后置的加加、减减操作符来实现迭代器位置的变更。

 【输出结果】


3.2 反向迭代器

【代码演示】

#include <iostream>
#include <list>
using namespace std;

int main()
{
	list<int> li{ 1,2,3,4,5,6,7 };
	list<int>::reverse_iterator rbit = li.rbegin();
	while (rbit != li.rend())
	{
		//从begin到end 从rbegin到rend都是++
		cout << *rbit << " ";
		++rbit;
	}
	cout << endl;
	return 0;
}

【输出结果】


四、容量相关 

4.1 获取list中有效数据的个数

【代码演示】

#include <iostream>
#include <list>
#include <string>
using namespace std;

int main()
{
	list<string> ls{ "Hello", "lisr", "and", "string", "!" };
	cout << ls.size() << endl;
	return 0;
}

【输出结果】 


4.2 判断list是否为空 —— empty

【代码演示】

#include <iostream>
#include <list>
using namespace std;

int main()
{
	list<int> li;
	cout << li.empty() << endl;

	//尾插一个1
	li.push_back(1);

	cout << li.empty() << endl;
	return 0;
}

【输出结果】


五、元素访问

5.1 获取第一个有效节点的数据

【代码演示】

#include <iostream>
#include <list>
using namespace std;

int main()
{
	list<int> li{ 1,2,3,4,5,6 };
	cout << li.front() << endl;

	return 0;
}

【输出结果】


5.2 获取最后一个有效节点的数据

#include <iostream>
#include <list>
using namespace std;

int main()
{
	list<string> ls{ "Hello", "list!" };
	cout << ls.back() << endl;

	return 0;
}

【输出结果】


六、修改相关

6.1 头插、尾插 —— push_front、push_back

【代码演示】

#include <iostream>
#include <list>
using namespace std;

int main()
{
	list<int> li{ 2,3,4,5 };

	//头插1
	li.push_front(1);

	//尾插6
	li.push_back(6);

	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;

	return 0;
}

【输出结果】


6.2 尾插、尾删 —— pop_front、pop_back 

【代码演示】

#include <iostream>
#include <list>
using namespace std;

int main()
{
	list<int> li{ 1,2,3,4,5,6 };
	//头删
	li.pop_front();

	//尾删
	li.pop_back();

	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;

	return 0;
}

【输出结果】


6.3 在任意位置插入

【代码演示】

#include <iostream>
#include <list>
using namespace std;

int main()
{
	list<int> li{ 1,2,3,5,6,7 };

	list<int>::iterator pos = li.begin();

	//list的迭代器不支持直接加减一个值。
    //比如我们要在5的前面插入一个4,就必须让pos++3次,而不能pos + 3
	//循环的次数可以看成目标位置下标的数值,但不是真正的下标
    //因为list节点并不分布在一块连续的空间
	for (int i = 0; i < 3; ++i)
	{
		++pos;
	}

	li.insert(pos, 4);

	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;
	return 0;
}

【输出结果】

        list的插入操作不像vector的插入操作,在插入数据中如果进行了扩容,vector会发生迭代器失效,而list不会。这是因为vector的扩容是开辟一块更大的空间,将原空间数据拷贝到新空间后再释放原空间,而迭代器pos任然指向旧空间,这样就会对非法空间进行访问。但是list的扩容只是开辟一块空间来存储新的数据,其他数据的位置和关系并不会发生变化,所以并不会发生迭代器失效。


6.4 在任意位置删除 —— erase

#include <iostream>
#include <list>
using namespace std;

int main()
{
	list<int> li{ 1,2,3,4,5,6,7,8 };
	list<int>::iterator pos = li.begin();

	//删除list中的所有偶数
	while (pos != li.end())
	{
        //避免发生迭代器失效
		if (*pos % 2 == 0)
		{
			pos = li.erase(pos);
		}
		else
		{
			++pos;
		}
	}

	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;

	return 0;
}

        虽然list在插入过程中不会发生迭代器失效,但是在删除过程中任然会发生迭代器失效。为了避免这种情况我们就需要利用erase的返回值

        erase的返回值是一个指向被删除数据的下一个数据的迭代器,我们在执行删除操作之后让迭代器pos等于erase的返回值,如果没有执行就正常++,这样就能避免迭代器失效问题了。


6.5 交换两个list对象

【代码演示】

#include <iostream>
#include <list>
using namespace std;

int main()
{
	list<int> li1{ 1, 2, 3, 4 ,5 };
	list<int> li2{ 6, 7, 8 ,9, 10 };

	li1.swap(li2);

	cout << "li1的数据为:";
	for (const auto& k : li1)
	{
		cout << k << " ";
	}
	cout << endl;

	cout << "li2的数据为:";
	for (const auto& k : li2)
	{
		cout << k << " ";
	}
	cout << endl;
	return 0;
}

【输出结果】


6.6 清空list中的有效节点

【代码演示】

#include <iostream>
#include <list>
using namespace std;

int main()
{
	list<int> li{ 1,2,3,4,5 };
	cout << "clear前的数据为:";
	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;

	li.clear();
	cout << "clear后的数据为:";
	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;
	return 0;
}

【输出结果】


七、链表操作

7.1 将一个list上的数据转移到另一个list —— splice

【函数原型】

【代码演示】

#include <iostream>
#include <list>
using namespace std;

int main()
{
	list<int> des1{ 1,2,3,4,5 };
	list<int> des2(des1);
	list<int> des3 = des2;

	list<int> sour1{ 6,7,8,9,10 };
	list<int> sour2(sour1);
	list<int> sour3 = sour2;

	//转移一整个链表
	des1.splice(des1.end(), sour1);

	//转移一个元素
	des2.splice(des2.end(), sour2, sour2.begin());

	//转移一个区间 -》左闭右开
	des3.splice(des3.end(), sour3, ++sour3.begin(), --sour3.end());

	cout << "des1的数据为:";
	for (const auto& k : des1)
	{
		cout << k << " ";
	}
	cout << endl;

	cout << "sour1的数据为:";
	for (const auto& k : sour1)
	{
		cout << k << " ";
	}
	cout << endl;

	cout << "des2的数据为:";
	for (const auto& k : des2)
	{
		cout << k << " ";
	}
	cout << endl;

	cout << "sour2的数据为:";
	for (const auto& k : sour2)
	{
		cout << k << " ";
	}
	cout << endl;

	cout << "des3的数据为:";
	for (const auto& k : des3)
	{
		cout << k << " ";
	}
	cout << endl;

	cout << "sour3的数据为:";
	for (const auto& k : sour3)
	{
		cout << k << " ";
	}
	cout << endl;
	return 0;
}

【输出结果】


7.2 删除某个特定数据

【代码演示】

#include <iostream>
#include <list>
using namespace std;

int main()
{
	list<int> li{ 1,2,3,1,1,5,8,5,4,3 };
	cout << "remove前的数据为:";
	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;

	li.remove(1);
	cout << "remove后的数据为:";
	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;

	return 0;
}

【输出结果】


7.3 给数据排序 —— sort

【代码演示】

#include <iostream>
#include <list>
using namespace std;

int main()
{
	list<int> li{ 32,5,86,12,6,2 };
    
    //list排序通过归并排序实现
	li.sort();
	for (auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;

	return 0;
}

【输出结果】

         注:list用不了algorithm算法库中的sort,因为algorithmsort的参数要求是随机迭代器RandomAccessIterator,而list的迭代器是双向迭代器bidirectional iterator

        除了随机迭代器RandomAccessIterator和双向迭代器bidirectional iterator外还有一种迭代器类型是InputIterator,这个迭代器类型就是只要是迭代器就行。

        不过话虽如此,但并不建议对list进行排序,因为list的数据在空间上是分散开的,并不适合排序,list排序的效率远远不及vector这种在连续空间上储存数据的容器,因此并不建议对list进行排序。


7.4 去除重复数据 —— unique

【代码演示】

#include <iostream>
#include <list>
using namespace std;

int main()
{
	list<int> li{ 1,4,6,4,6,7,8,9,1,3,5 };

	//在去重之前建议先对list进行排序 不然效率实在是太低了 不过排序的效率也很低就是了
	li.sort();
	li.unique();

	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;
	return 0;
}

【输出结果】


7.5 反转数据 —— reverse

【代码演示】

#include <iostream>
#include <list>
using namespace std;

int main()
{
	list<int> li{ 1,2,3,4,5,6 };

	li.reverse();
	for (const auto& k : li)
	{
		cout << k << " ";
	}
	cout << endl;

	return 0;
}

【输出结果】


八、list与vector的对比 

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1409242.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Unity学习笔记】第十一 · 动画基础(Animation、状态机、root motion、bake into pose、blendTree、大量案例)

转载引用请注明出处&#xff1a;&#x1f517;https://blog.csdn.net/weixin_44013533/article/details/132081959 作者&#xff1a;CSDN|Ringleader| 如果本文对你有帮助&#xff0c;不妨点赞收藏关注一下&#xff0c;你的鼓励是我前进最大的动力&#xff01;ヾ(≧▽≦*)o 主…

【Python编程工具】【ssh连接Docker容器】如何使用Docker容器里的python环境,如何调试在容器中的代码

文章目录 方案一览Gateway软件介绍启动容器配置apt源在容器中安装SSH服务器配置SSH服务器生成SSH密钥启动SSH服务为root创建密码连接到容器使用Gateway 方案一览 本篇博客将介绍如何在Docker容器中打开SSH连接服务&#xff0c;以及如何使用JetBrains Gateway软件进行代码调试。…

数据结构与算法-二叉树-路径总和 II

路径总和 II 给你二叉树的根节点 root 和一个整数目标和 targetSum &#xff0c;找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。 叶子节点 是指没有子节点的节点。 示例 1&#xff1a; 输入&#xff1a;root [5,4,8,11,null,13,4,7,2,null,null,5,1], target…

wpf控件Expander集合下的像素滚动

项目场景&#xff1a;Expander集合滚动 如下图&#xff0c;有一个Expander集合&#xff0c;且设置 ScrollViewer.VerticalScrollBarVisibility "Auto" 每个Expaner下包含有若干元素&#xff0c;当打开Expader(即IsExpanded "true"&#xff09;时&#…

企业邮箱遭入侵!印度制药巨头损失超4500万元

近日&#xff0c;印度制药巨头阿尔肯实验室子公司部分员工的企业邮箱遭入侵&#xff0c;导致其子公司被欺诈5.2亿卢比&#xff08;约合人民币4500万元&#xff09;。而根据截至2023年9月的季度财务报告数据&#xff0c;该公司营业收入为263.46亿卢比&#xff0c;净利润为64.65亿…

电脑城衰退的原因是什么?

​电脑城衰退的原因分析 随着科技的飞速发展&#xff0c;电脑城曾经是电子产品交易的热门场所。然而&#xff0c;近年来&#xff0c;电脑城的发展状况不容乐观&#xff0c;正面临着巨大的挑战。究竟是什么原因导致了电脑城的衰退&#xff1f;本文将深入探讨这一问题。 电子商…

05-Seata下SQL使用限制

不支持 SQL 嵌套不支持多表复杂 SQL(自1.6.0版本&#xff0c;MySQL支持UPDATE JOIN语句&#xff0c;详情请看不支持存储过程、触发器部分数据库不支持批量更新&#xff0c;在使用 MySQL、Mariadb、PostgreSQL9.6作为数据库时支持批量&#xff0c;批量更新方式如下以 Java 为例 …

编译安装Nginx和使用五种算法实现Nginx反向代理负载均衡

目录 Ubuntu中安装Nginx 概念介绍 负载均衡 几种负载均衡算法 反向代理 环境规划 配置反向代理 加权负载均衡&#xff08;Weighted Load Balancing&#xff09; 轮询&#xff08;Round Robin&#xff09; IP 哈希&#xff08;IP Hash&#xff09; 最少连接&#xff…

服务器和云桥通SDWAN组网的区别

一、服务器的概念 服务器是一种计算设备&#xff0c;用于存储、处理和提供数据和应用服务。通常&#xff0c;服务器配备高性能处理器、大容量存储器和网络接口&#xff0c;其主要目的是提供计算资源、存储资源以及应用程序的托管。这种设备可以用于托管网站、应用程序、数据库和…

1331:【例1-2】后缀表达式的值

【题目描述】 从键盘读入一个后缀表达式&#xff08;字符串&#xff09;&#xff0c;只含有0-9组成的运算数及加&#xff08;&#xff09;、减&#xff08;—&#xff09;、乘&#xff08;*&#xff09;、除&#xff08;/&#xff09;四种运算符。每个运算数之间用一个空格隔开…

8.8加油站(LC134-M)

算法&#xff1a; 首先如果总油量减去总消耗大于等于零那么一定可以跑完一圈&#xff0c; 每个加油站的剩余量rest[i]为gas[i] - cost[i]。 说明 各个站点的加油站 剩油量rest[i]相加一定是大于等于零的。 i从0开始累加rest[i]&#xff0c;和记为curSum&#xff0c;一旦curS…

合合信息启信数据发布园区金融解决方案,助力银行精准服务“十四五”特色产业

今年冬季寒潮频现&#xff0c;“尔滨”等冰雪之城却凭借着出色的文旅服务&#xff0c;接连火爆“出圈”。现阶段&#xff0c;作为传统工业基地的哈尔滨正积极向第三产业转型。文旅园区具备产业、技术、知识、劳动力密集属性和特定产业集群规模效应&#xff0c;是推动文化与创意…

腾讯云安装Java11(jdk11.0.21)

腾讯云安装Java11(jdk11.0.21) 下载Java11 下载Linux的jdk包Java11下载路径 https://www.oracle.com/java/technologies/javase/jdk11-archive-downloads.html 解压jdk 下载完成后&#xff0c;进入自己想要放到的目录下面&#xff0c;输入tar -zxvf jdk-11.0.21_linux-x64_b…

考研C语言刷题基础篇之数组(一)

目录 第一题&#xff1a;用数组作为参数实现冒泡排序 不用函数的冒泡排序 冒泡排序原理&#xff1a; 错误的数值传参冒泡排序 错误的原因 就是什么是数组名 正确的数组传参的冒泡排序 数组的地址和数组首元素的地址的区别 第一题&#xff1a;用数组作为参数实现冒泡排…

oracle19.22的patch已发布

2024年01月16日,oracle发布了19.22的patch 具体patch如下 Reserved for Database - Do not edit or delete (Doc ID 19202401.9) 文档ID规则如下 19(版本)+年份(202x)+(季度首月01,04,07,10).9 往期patch no信息和下载参考文档 oracle 19C Release Update patch num…

喝葡萄酒要懂得选对杯

喝葡萄酒要懂得选对杯 一、什么是葡萄酒杯&#xff1f; 葡萄酒杯&#xff0c;因其有一个细长的底座而被大众形象的称为高脚杯&#xff0c;但在事实上&#xff0c;高脚杯只是葡萄酒杯中的一种。在葡萄酒文化中&#xff0c;酒杯是其不可缺失的一个重要环节&#xff0c;在西方传统…

《动手学深度学习(PyTorch版)》笔记3.1

Chapter3 Linear Neural Networks 3.1 Linear Regression 3.1.1 Basic Concepts 我们通常使用 n n n来表示数据集中的样本数。对索引为 i i i的样本&#xff0c;其输入表示为 x ( i ) [ x 1 ( i ) , x 2 ( i ) , . . . , x n ( i ) ] ⊤ \mathbf{x}^{(i)} [x_1^{(i)}, x_2…

composer安装hyperf后,nginx配置hyperf

背景 引入hyperf项目用作微服务&#xff0c;使用composer 安装hyperf后&#xff0c;对hyperf进行nginx配置。 配置步骤 因为hyperf监听的是端口&#xff0c;不像其他laravel、lumen直接指向文件即可。所有要监听端口号。 1 配置nginx server {listen 80;//http&#xff1a…

逻辑回归中的损失函数梯度下降

一、引言 逻辑回归中的损失函数通常采用的是交叉熵损失函数&#xff08;cross-entropy loss function&#xff09;。在逻辑回归中&#xff0c;我们通常使用sigmoid函数将线性模型的输出转换为概率值&#xff0c;然后将这些概率值与实际标签进行比较&#xff0c;从而计算损失。 …

2024年mongodb自建三节点副本集详细教程

环境说明 系统centos7.9 自建服务器或云服务器&#xff0c;硬件要求不低于2核2G内存&#xff0c;20G硬盘&#xff0c;文件系统默认是ext4即可。 生产环境最好单独一个磁盘存放数据库&#xff0c;方便数据备份和还原&#xff0c;避免干扰到其他磁盘的运作。 mongodb 4.4.27 …