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

目录

一、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)
空 间 利 用 率底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高底层节点动态开辟,小节点容易造成内存碎片,空间利用率低, 缓存利用率低
迭 代 器原生态指针对原生态指针(节点指针)进行封装
迭 代 器 失 效在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效;删除时,当前迭代器需要重新赋值否则会失效插入元素不会导致迭代器失效, 删除元素时,只会导致当前迭代器失效,其他迭代器不受影响
使 用 场 景需要高效存储,支持随机访问,不关心插入删除效率大量插入和删除操作,不关心随机访问

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Web前端开发基础】CSS3之空间转换和动画

CSS3之空间转换和动画 目录 CSS3之空间转换和动画一、空间转换1.1 概述1.2 3D转换常用的属性1.3 3D转换&#xff1a;translate3d&#xff08;位移&#xff09;1.4 3D转换&#xff1a;perspective&#xff08;视角&#xff09;1.5 3D转换&#xff1a;rotate3d&#xff08;旋转&a…

使用StrictMode优化Android应用程序的ANR率

使用StrictMode优化Android应用程序的ANR率 本文将解释StrictMode是什么以及如何在Android应用程序中使用它作为ANR观察器。 什么是StrictMode以及为什么使用它&#xff1f; StrictMode是帮助开发人员防止ANR并减少在Android系统中产生ANR的机会的工具之一。 从developer.a…

抖音跳转微信公众号是怎么实现的丨数灵通

抖音是一款非常流行的社交媒体应用程序&#xff0c;用户可以在其中分享短视频和互动内容。许多用户希望通过抖音来引流到他们的微信公众号&#xff0c;以扩大影响力并吸引更多粉丝。以下是一些关于如何在抖音上跳转到微信公众号的科普信息&#xff1a; 1.信息流广告&#xff1a…

elementplus Dialog 对话框设置距离页面顶部的距离

默认为 15vh&#xff0c;当弹窗过于高的时候&#xff0c;这个距离其实是不合适的 <el-dialogv-model"dialogVisible"title"Tips"width"30%":before-close"handleClose"top"6vh"><span>This is a message</s…

leetcode:2859. 计算 K 置位下标对应元素的和(python3解法)

难度&#xff1a;简单 给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。 请你用整数形式返回 nums 中的特定元素之 和 &#xff0c;这些特定元素满足&#xff1a;其对应下标的二进制表示中恰存在 k 个置位。 整数的二进制表示中的 1 就是这个整数的 置位 。 例如&#xf…

如何实现激光雷达运动补偿,这篇就够了

目录 激光雷达为什么会存在运动畸变 激光雷达如何运动去畸变 C++实践激光雷达运动补偿(辅助传感器) 实践激光雷达ICP运动补偿 参考文献 激光雷达为什么会存在运动畸变 首先要理解为什么会产生运动畸变。激光雷达扫描物体形成点云的过程自身伴随着旋转运动,每次激…

嵌入式-stm32-江科大-EXTI外部中断

一&#xff1a;EXTI外部中断&#xff08;external interrupt&#xff09; 1.1 STM32 中断系统 中断是指在主程序运行过程中&#xff0c;出现了特定的中断触发条件&#xff08;中断源&#xff09;&#xff0c;使得CPU暂停当前的程序&#xff0c;转而去处理中断程序&#xff0c;…

Termux结合内网穿透实现无公网ip远程SFTP传输文件

目录 前言 1. 安装openSSH 2. 安装cpolar 3. 远程SFTP连接配置 4. 远程SFTP访问 4. 配置固定远程连接地址 结语 作者简介&#xff1a; 懒大王敲代码&#xff0c;计算机专业应届生 今天给大家聊聊Termux结合内网穿透实现无公网ip远程SFTP传输文件&#xff0c;希望大家能…

【基础算法练习】二分模板

文章目录 二分模板题二分的思想C 版本的二分整数二分模板 Golang 版本的二分整数二分模板 例题&#xff1a;在排序数组中查找元素的第一个和最后一个位置题目描述C 版本代码Golang 版本代码 二分模板题 704. 二分查找&#xff0c;这道题目是最经典的二分查找&#xff0c;使用于…

华为产业链之车载激光雷达

一、智能汽车 NOA 加快普及&#xff0c;L3 上路利好智能感知硬件 1、感知层是 ADAS 最重要的一环 先进驾驶辅助系统 &#xff08;ADAS&#xff0c; Advanced driver-assistance system&#xff09;分“感知层、决策层、执行层”三个层级&#xff0c;其中感知层是最重要的一环…

不同页面加载对爬虫的影响

目录 前言 1. 不同页面加载方式对爬虫的影响 1.1 静态页面加载 1.2 动态页面加载 2. 使用代理IP进行访问 总结 前言 在进行网络爬虫的过程中&#xff0c;不同的网页加载方式可以对爬虫的效率和稳定性产生重要影响。有些网站可能会限制对其服务器的访问频率&#xff0c;如果…

怎么把ico图片转png?图片格式转换的快捷方法

ICO是一种常用的图标文件格式&#xff0c;广泛用于软件应用的图标设计&#xff0c;然而&#xff0c;ICO格式的图片分辨率通常较低&#xff0c;因此在某些平台上无法满足上传要求&#xff0c;为了解决这个问题&#xff0c;我们通常需要将ICO格式转换为常见的PNG格式或其他常用格…

GuitarPro和Earmaster那个适合新手

许久没发文了&#xff0c;最近在网上刷到了一位音乐UP主从容Free&#xff0c;他把自己对GuitarPro和Earmaster这2款软件的使用感受进行了详细分享&#xff0c;还没看过的朋友可以戳下面的链接跳转到小破站看完整的&#xff1a; 我不允许还有人不知道这个学吉他的神器&#xff…

YOLO 自己训练一个模型

一、准备数据集 我的版本是yolov8 8.11 这个目录结构很重要 ultralytics-main | datasets|coco|train|val 二、训练 编写yaml 文件 # Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..] path…

抖音VR直播:沉浸式体验一键打通360度精彩

随着5G技术的发展&#xff0c;VR直播近年来也逐步进入到大众的视野中&#xff0c;相比于传统直播&#xff0c;VR直播能够提供更加丰富的内容和多样化的互动方式&#xff0c;让观众更有沉浸感和参与感。现如今&#xff0c;抖音平台也上线了VR直播&#xff0c;凭借沉浸式体验和有…

无公网IP实现远程访问MongoDB文件数据库【内网穿透】

最近&#xff0c;我发现了一个超级强大的人工智能学习网站。它以通俗易懂的方式呈现复杂的概念&#xff0c;而且内容风趣幽默。我觉得它对大家可能会有所帮助&#xff0c;所以我在此分享。点击这里跳转到网站。 文章目录 前言1. 安装数据库2. 内网穿透2.1 安装cpolar内网穿透2…

pytorch与tensorflow如何选择?

目录 1.动态图和静态图1.1 tensorflow是静态图1.2 pytorch动态图 2. 易用性3. 编程语言4. 性能和扩展性5. 社区支持和生态系统 1.动态图和静态图 1.1 tensorflow是静态图 如上图&#xff1a; 定义计算图&#xff08;公式&#xff0c;包括定义变量x,y ,zx*y&#xff09;给公式…

开源客户沟通平台Chatwoot账号激活问题

安装docker docker-compose 安装git clone https://github.com/chatwoot/chatwoot 下载之后根目录有一个docker-compose.production.yaml将其复制到一个目录 重命名 docker-compose.yaml 执行docker-compose up -d 构建 构建之后所有容器都安装好了 直接访问http://ip:3…

嵌入式软件工程师面试题——2025校招社招通用(C/C++)(三十八)

说明&#xff1a; 面试群&#xff0c;群号&#xff1a; 228447240面试题来源于网络书籍&#xff0c;公司题目以及博主原创或修改&#xff08;题目大部分来源于各种公司&#xff09;&#xff1b;文中很多题目&#xff0c;或许大家直接编译器写完&#xff0c;1分钟就出结果了。但…

【正点原子STM32】STM32基础知识(F1F4F7H7 STM32系统框架、寻址范围、存储器映射的存储器功能划分、寄存器映射)

一、STM32系统框架 1.1、Cortex M内核 & 芯片1.2、F1系统架构1.3、F4系统架构1.4、F7系统架构1.5、H7系统架构 二、STM32的寻址范围&#xff1f; 三、存储器映射 存储器功能划分&#xff08;F1为例&#xff09;STM32F1存储器映射图 四、寄存器映射 寄存器基础知识STM3…