【STL】list的使用

这篇具有很好参考价值的文章主要介绍了【STL】list的使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

系列文章

学习C++途中自然绕不过STL,在这个系列文章之中

我们讲了string的使用和string的模拟实现,以及vector的使用、vector的模拟实现。

感兴趣的可以翻翻看。


目录

系列文章

前言

默认成员函数

构造函数

拷贝构造

赋值重载

迭代器

容量查询

数据访问

数据修改

assign

头插头删尾插尾删

指定位置插入删除

迭代器失效

交换

其他操作

splice

remove

合并

去重

逆置

排序


前言

☘️list 是 STL 中又一重要容器,而 list 其实就是对链表的封装。一提到链表,相信大家第一反应就是单链表,而单链表的缺点又是显而易见的:尾插过于麻烦、只能单向访问等等。但实际上list中封装的链表并非普通的单链表,而是之前讲过的带头双向循环链表。

☘️其能够优化除随机访问之外,单链表的大部分缺点,而且再次经过封装因此使用起来十分地方便。

【STL】list的使用

☘️这次主要讲讲 list 该如何使用,更多实现的细节等到模拟实现的时候再讲。

默认成员函数

☘️使用list前记得要带头文件<list>,否则就无法使用list了。 

构造函数

☘️跟以前学习的容器一样,list 的构造函数还是老三样。

  • 创建空链表
  • 创建n个值为val的节点的链表
  • 以一个迭代器区间进行构造

【STL】list的使用

 ☘️根据不同的写法初始化后的结果也有所不同。

int main()
{
	list<int> l1;                 //空表
	list<int> l2(5, 3);           //33333
	int arr[] = { 1,2,3,4,5,6 };
	list<int> l3(arr, arr + 6);   //123456
	return 0;
}

拷贝构造

☘️参数同样为 list 的自然就是拷贝构造了,该拷贝为深拷贝,节点内部的值都相同,但节点的地址不同。

【STL】list的使用

☘️以一个已经初始化过的 list 来初始化一个新的 list,最后打印出来的结果都应是相同的。

int main()
{
	list<int> l1(5, 3);
	list<int> l2(l1); 
	//list<int> l2 = l1;   //也可以这样写
    for (auto i : l2)
	{
		cout << i << " ";
	}
	cout << endl;
	return 0;
}

 【STL】list的使用

赋值重载

☘️赋值重载则是用于两个已经存在的 list 之间进行赋值的操作,若其中一个还未初始化,那么实际上调用的是拷贝构造。

【STL】list的使用

☘️通过这串代码,我们便可以清晰地看到赋值前后 list 的变化。

int main()
{
	list<int> l1(5, 3);
	list<int> l2(3, 2);
	cout << "原来的l1" << ": ";
	for (auto i : l1) cout << i << " ";
	cout << endl;
	l1 = l2;
	cout << "现在的l1" << ": ";
	for (auto i : l1) cout << i << " ";
	cout << endl;
	return 0;
}

 【STL】list的使用

迭代器

☘️虽然我们平时只是讲迭代器,但是根据其能力的不同,迭代器也被分作三类。

  • 单向迭代器:只能++   例如 unorder_map 的迭代器
  • 双向迭代器:可以++还能--    例如 list 的迭代器
  • 随机迭代器:++、--、+、-   例如 vector 的迭代器

☘️之所以是双向迭代器,得益于 list 的底层结构是一个带头双向循环链表,使得每一个节点都能访问自己的上下两个节点

【STL】list的使用

☘️通过调用 begin 这个函数,我们能拿到指向 list 第一个有效数据的迭代器

☘️再调用 end 就能拿到标志 list 结尾的迭代器,便有了边界,就可以完成对 list 的遍历了。

【STL】list的使用     【STL】list的使用 

 ☘️先构建一个 list,之后拿到 begin 的迭代器,直到 end 前打印当前节点的值。注意: 这个区间是左闭右开的。

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9 };
	list<int> l(arr, arr + 9);
	list<int>::iterator it = l.begin();
    //auto it = l.begin();   //还可以这样写
	while (it != l.end())    //[begin,end)
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	return 0;
}

☘️反向迭代器的使用也是类似,只不过调用的函数换成了 rbegin 和 rend 而已。

☘️值得注意的一点是,反向迭代器的迭代使用的也是 ++。但迭代区间一样是 [rbegin,rend)

【STL】list的使用   【STL】list的使用

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9 };
	list<int> l(arr, arr + 9);
	list<int>::reverse_iterator it = l.rbegin();  //类型名改变
	while (it != l.rend())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	return 0;
}

容量查询

【STL】list的使用

☘️由于链表并没有扩容的概念,因此并没有 reserve 这个函数,而 resize 则是作为修改的函数,也不经常使用。

☘️最常用的莫过于上面这两个函数了,empty 用于判断链表是否为空,而 size 用于查看当前有效节点的数量

【STL】list的使用  【STL】list的使用

☘️我们可以写一段代码简单使用一下。

int main()
{
	list<int> l1(3, 6);
	list<int> l2;
	if (!l1.empty())    //l1不为空,打印大小为3
		cout << "l1 size: " << l1.size() << endl;
	if (!l2.empty())    //l2为空,不打印
		cout << "l2 size: " << l2.size() << endl;
	return 0;
}

 【STL】list的使用

数据访问

☘️在 list 中,我们可以使用 front 和 back 访问链表的首尾元素

【STL】list的使用  【STL】list的使用

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9 };
	list<int> l(arr, arr + 9);
	cout << "front is: " << l.front() << endl;
	cout << "back is: " << l.back() << endl;
	return 0;
}

 【STL】list的使用

数据修改

assign

☘️就像我们前面构造函数中的那样,可以讲内容转换为 n 个 val 或是一个迭代器区间。

☘️但是,以前的内容会被清除

【STL】list的使用

int main()
{
	list<int> l(2, 3);    // 33
	l.assign(5, 6);       // 66666
	for (auto it : l) cout << it << " ";
	cout << endl;
	vector<int> v = { 2,3,5,4,2 };
	l.assign(v.begin(), v.end());   // 23542
	for (auto it : l) cout << it << " ";
	cout << endl;
	return 0;
}

【STL】list的使用 

头插头删尾插尾删

☘️通过 push_front push_back 可以做到头插和尾插,而 pop_frontpop_back 可以做到头删和尾删。

☘️只要记住 front 是前 back 是后push 代表插入 pop 代表删除,之后怎么排列组合都不会记混了。

int main()
{
	vector<int> v = { 2,3,5,4,2 };
	list<int> l(v.begin(), v.end());
	for (auto it : l) cout << it << " ";  //原链表
	cout << endl;
	l.push_front(10);
	l.push_back(20);
	for (auto it : l) cout << it << " ";  //插入后的链表
	cout << endl;
	l.pop_front();
	l.pop_back();
	for (auto it : l) cout << it << " ";  //删除后的链表
	cout << endl;
	return 0;
}

 【STL】list的使用

指定位置插入删除

☘️我们使用 insert 和 erase 可以做到指定位置插入和删除,但是我们发现 list 中并没有 find 这个函数。若是每次从 begin 获取迭代器还要++,那未免过于麻烦了。

【STL】list的使用

【STL】list的使用  

☘️没关系,算法库里有一个find函数能够满足我们的使用需求,使用前记得带上头文件<algorithm>。

【STL】list的使用

☘️这样我们就能任意地插入删除了。

int main()
{
	vector<int> v = { 2,3,5,4,2 };
	list<int> l(v.begin(), v.end());
	list<int>::iterator pos = find(l.begin(), l.end(), 3);
	l.insert(pos, 20);     //在3前插入20
	for (auto it : l) cout << it << " "; 
	cout << endl;
	pos = find(l.begin(), l.end(), 5); 
	l.erase(pos);          //删除5
	for (auto it : l) cout << it << " "; 
	cout << endl;
	return 0;
}

迭代器失效

☘️当我们使用 erase 进行删除后,此时指向删除位置的迭代器就失效了,再次使用就会令程序崩溃。

【STL】list的使用 

☘️因此若要多次删除,则需要在使用后利用 erase 的返回值更新迭代器,这样使用才不会出现错误。

int main()
{
	vector<int> v = { 2,3,5,4,6 };
	list<int> l(v.begin(), v.end());
	list<int>::iterator pos = find(l.begin(), l.end(), 3);
	for (int i = 0; i < 3; i++)
	{
		pos = l.erase(pos);   //利用erase的返回值更新迭代器
	}
	for (auto it : l) cout << it << " ";
	cout << endl;
	return 0;
}

【STL】list的使用  

交换

☘️跟 vector 和 string 里面的一样都是直接交换内部的指向,而不需要拷贝节点。

【STL】list的使用

int main()
{
	vector<int> v1 = { 2,3,5,4,6 };
	vector<int> v2 = { 1,2,3,4,5 };
	list<int> l1(v1.begin(), v1.end());
	list<int> l2(v2.begin(), v2.end());
	for (auto it : l1) cout << it << " ";   //交换前
	cout << endl;
	for (auto it : l2) cout << it << " ";
	cout << endl;
	cout << endl;
	l1.swap(l2);
	for (auto it : l1) cout << it << " ";   //交换后
	cout << endl;
	for (auto it : l2) cout << it << " ";
	cout << endl;
	return 0;
}

其他操作

☘️除了上面那些基础了操作,库中还有一些其他函数,挑了几个讲讲。

splice

☘️这个函数可以帮助我们将一个链表的节点转移到另外一个链表中。

【STL】list的使用

☘️函数结束后,l2 就变成了空链表,而其节点被转移到 l1 之中。 

int main()
{
	int arr[] = { 1,2,3,4,5,6 };
	int arr2[] = { 10,20,30 };
	list<int> l1(arr, arr + 6);
	list<int> l2(arr2, arr2 + 3);
	for (auto i : l1) cout << i << " ";  //转移前
	cout << endl;
	for (auto i : l2) cout << i << " ";
	cout << endl;
	cout << endl;
	list<int>::iterator it = l1.begin();
	++it;
	l1.splice(it, l2);
	for (auto i : l1) cout << i << " ";  //转移后
	cout << endl;
	for (auto i : l2) cout << i << " ";
	cout << endl;
	return 0;
}

 【STL】list的使用

remove

☘️remove 本质上就是 find + erase,可以方便我们的实际的使用。

【STL】list的使用

int main()
{
	int arr[] = { 1,2,3,4,5,6 };
	list<int> l1(arr, arr + 6);
	for (auto i : l1) cout << i << " ";  //删除前
	cout << endl;
	l1.remove(5);
	for (auto i : l1) cout << i << " ";  //删除后
	cout << endl;
	return 0;
}

合并

☘️使用 merge,我们可以合并两个有序的链表,实际上的操作就类似于归并排序。

【STL】list的使用

int main()
{
	int arr[] = { 1,2,3,4,5,6 };
	int arr2[] = { 10,20,30 };
	list<int> l1(arr, arr + 6);
	list<int> l2(arr2, arr2 + 3);
	for (auto i : l1) cout << i << " ";  //合并前
	cout << endl;
	for (auto i : l2) cout << i << " ";
	cout << endl;
	cout << endl;
	l1.merge(l2);
	for (auto i : l1) cout << i << " ";  //合并后
	cout << endl;
	for (auto i : l2) cout << i << " ";
	cout << endl;
	return 0;
}

【STL】list的使用  

去重

☘️使用这个函数可以去除掉链表中的重复元素,但前提是这个链表必须是有序的。

【STL】list的使用

int main()
{
	vector<int> v = { 1,1,1,2,3,3,4,4,5 };
	list<int> l1(v.begin(),v.end());
	for (auto i : l1) cout << i << " ";  //去重前
	cout << endl;
	cout << endl;
	l1.unique();
	for (auto i : l1) cout << i << " ";  //去重后
	cout << endl;
	return 0;
}

【STL】list的使用  

逆置

☘️可以通过这个函数可以将链表逆置。

【STL】list的使用

int main()
{
	vector<int> v = { 1,2,3,4,5,6 };
	list<int> l1(v.begin(), v.end());
	for (auto i : l1) cout << i << " ";  //逆置前
	cout << endl;
	cout << endl;
	l1.reverse();
	for (auto i : l1) cout << i << " ";  //逆置后
	cout << endl;
	return 0;
}

 【STL】list的使用

排序

☘️list 并不能使用算法库里面的 sort,因为它的迭代器并不是随机访问的迭代器,所以库中重载了一个 sort。

【STL】list的使用 

☘️但其实,这个函数的效率并不高,我们可以写个代码来测试一下。

int main()
{
	srand((size_t)time(NULL));
	int n = 1000000;
	vector<int> v;
	list<int> l;
	for (int i = 0; i < n; i++)
	{
		int val = rand() % 100;
		v.push_back(val);
		l.push_back(val);
	}

	int begin1 = clock();     //vector开始排序
	sort(v.begin(), v.end());
	int end1 = clock();       //vector结束排序

	int begin2 = clock();    //list开始排序
	l.sort();
	int end2 = clock();      //list结束排序

	cout << "vector: " << end1 - begin1 << endl;
	cout << "list: " << end2 - begin2 << endl;
	return 0;
}

☘️当 n 为1000000时,差距就非常的明显了。

【STL】list的使用

☘️之后我们将一个 list 拷贝进 vector 后再进行排序,看看最后结果如何。

int main()
{
	srand((size_t)time(NULL));
	int n = 1000000;
	list<int> l1;
	list<int> l2;

	for (int i = 0; i < n; i++)
	{
		int val = rand() % 100;
		l1.push_back(val);
		l2.push_back(val);
	}

	int begin1 = clock();
	vector<int> v;
	for (auto it : l1) v.push_back(it); //把list拷贝进vector
	sort(v.begin(), v.end());           //再次排序
	int end1 = clock();

	int begin2 = clock();
	l2.sort();    
	int end2 = clock();

	cout << "l1: " << end1 - begin1 << endl;
	cout << "l2: " << end2 - begin2 << endl;
	return 0;
}

【STL】list的使用

☘️可以看出,差距还是很大的,因此未来的使用中,除非数值较小为了图个方便可以直接使用,否则还不如拷贝到 vector 里再进行排序。


☘️好了,今天 list 的相关内容到这里就结束了,如果这篇文章对你有用的话还请留下你的三连加关注。 文章来源地址https://www.toymoban.com/news/detail-459809.html

到了这里,关于【STL】list的使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 面试之快速学习STL-deuqe和list

    deque 容器用数组(数组名假设为 map)存储着各个连续空间的首地址。也就是说, map 数组中存储的都是指针 如果 map 数组满了怎么办?很简单, 再申请一块更大的连续空间供 map 数组使用,将原有数据(很多指针)拷贝到新的 map 数组中,然后释放旧的空间 。 deque容器迭代器

    2024年02月12日
    浏览(24)
  • 【whale-starry-stl】01天 list学习笔记

    std::bidirectional_iterator_tag 是 C++ 标准库中定义的一个迭代器类型标签,用于标识支持双向遍历的迭代器类型。 在 C++ 中,迭代器是一种泛型指针,用于遍历容器中的元素。迭代器类型标签用于标识迭代器的特性,从而在算法中选择合适的迭代器类型。 std::bidirectional_iterator_tag

    2024年02月09日
    浏览(26)
  • C++STL----list的使用

    list是一种可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以 前后双向迭代 。 list的 底层是带头双向链表 ,带头双向链表中每个元素存储在独立结点当中,在结点中通过指针指向其前一个元素和后一个元素。 list与forward_list非常相似,最主要的不同

    2024年02月07日
    浏览(334)
  • STL好难(4):list的使用

    和列表很像 点击这里查看 list 的官方文档 list类似数据结构中的 链表 1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且 该容器可以前后双向迭代。 2. list的底层是双向链表结构 ,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指

    2024年02月13日
    浏览(29)
  • 【C++】STL——list介绍及使用

    🚀 作者简介:一名在后端领域学习,并渴望能够学有所成的追梦人。 🚁 个人主页:不 良 🔥 系列专栏:🛸C++  🛹Linux 📕 学习格言:博观而约取,厚积而薄发 🌹 欢迎进来的小伙伴,如果小伙伴们在学习的过程中,发现有需要纠正的地方,烦请指正,希望能够与诸君一同

    2024年02月12日
    浏览(63)
  • C++ [STL之list的使用]

    本文已收录至《C++语言》专栏! 作者:ARMCSKGT vector是一片连续的空间,在数据访问上性能较好,但是任意位置插入删除性能较低,头插头删性能亦是如此;此时在这种需要频繁插入的场景下,显然链表是一种更好的选择,STL中实现了带头双选循环链表,本次我们来介绍该如何

    2024年02月07日
    浏览(35)
  • C++ STL- list 的使用以及练习

    目录 0.引言 1. list 介绍  2. list 使用 2.1 构造函数 2.2 list iterator 的使用  3 list capacity  4. list element access  5. list modifiers  6. list 迭代器失效  7. list 与vector 对vector 8. OJ 题讲解  删除链表的倒数第 N  个节点: 本篇博客我们将介绍 STL 中 list 的使用,由于list STL 接口函数与之前

    2024年03月26日
    浏览(37)
  • 【C++STL基础入门】list基本使用

    STL(Standard Template Library)是C++标准库的一个重要组成部分,提供了一套丰富的数据结构和算法,可以大大简化C++程序的开发过程。其中,list容器是STL提供的一种双向链表实现的数据结构,具有高效的插入和删除操作,适用于需要频繁插入和删除元素的场景。本文将介绍list容

    2024年02月07日
    浏览(30)
  • 【STL】“list“容器从使用到模拟实现

    🎉博客主页:小智_x0___0x_ 🎉欢迎关注:👍点赞🙌收藏✍️留言 🎉系列专栏:C++初阶 🎉代码仓库:小智的代码仓库 list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。 list的底层是 双向链表结构 ,双向链表中每个元素存储在

    2024年02月16日
    浏览(36)
  • C++ STL学习之【list的使用】

    ✨个人主页: 北 海 🎉所属专栏: C++修行之路 🎊每篇一句: 图片来源 A year from now you may wish you had started today. 明年今日,你会希望此时此刻的自己已经开始行动了。 STL 中的 vector 存在头部及中部操作效率低的缺陷,需要另一种容器来弥补其短板,此时 list 就应运而生,

    2023年04月16日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包