C++ [STL之list的使用]

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

C++ [STL之list的使用]

本文已收录至《C++语言》专栏!
作者:ARMCSKGT

C++ [STL之list的使用]

前言

vector是一片连续的空间,在数据访问上性能较好,但是任意位置插入删除性能较低,头插头删性能亦是如此;此时在这种需要频繁插入的场景下,显然链表是一种更好的选择,STL中实现了带头双选循环链表,本次我们来介绍该如何使用STL中的链表list!
C++ [STL之list的使用]


正文

本文理论依据来自于官方文档:STL容器list文档!
首先在使用list前,需要声明头文件 < list > 且声明命名空间std!
list是通过模板实例的泛型容器,需要指定类型进行实例化

默认成员函数


构造函数类

C++ [STL之list的使用]

  • 默认构造
    –构造一个空对象,里面没有任何数据(底层上只有一个头节点)
  • 构造n个值为val的链表对象
    –插入n个值实例类型的val值,构造一个对象
  • 迭代器区间构造
    – 通过其他容器迭代器或指针构造一个list对象
  • 列表初始化(C++11)
    – 对于支持C++11的编译器支持像数组一样使用列表{ }初始化
  • 拷贝构造
    – 拷贝另一个list,从而构造一个一模一样的list对象
#include <iostream>
#include <list>
using namespace std;

void Print(const list<int>& obj) //打印函数
{
	for (const auto& x : obj)
	{
		cout << x << " ";
	}
	cout << endl;
}

int main()
{
	list<int> l1; //默认构造

	list<int> l2(5, 1); //构造5个值为1(int类型)的链表对象
	Print(l2);

	int arr[] = { 5,4,3,2,1 };
	list<int> l3(arr, arr + 5); //指针作为迭代器区间构造
	Print(l3);

	vector<int> v = { 6,7,8,9,10 };
	list<int> l4(v.begin(),v.end()); //容器迭代器区间构造
	Print(l4);

	list<int> l5 = {11,12,13,14,15}; //列表初始化构造(C++11)
	Print(l5);

	list<int> l6(l5); //拷贝构造
	Print(l6);

	return 0;
}

C++ [STL之list的使用]



赋值重载

赋值重载与拷贝构造类似,只不过赋值的前提是对象已存在,拷贝构造是初始化对象!

void Print(const list<int>& obj)
{
	for (const auto& x : obj)
	{
		cout << x << " ";
	}
	cout << endl;
}

int main()
{
	vector<int> v = { 5,4,3,2,1 };
	list<int> l1(v.begin(),v.end()); //容器迭代器构造

	list<int> l2(5, 1);
	Print(l2);
	l2 = l1;
	Print(l2);
	return 0;
}

C++ [STL之list的使用]



析构函数

析构函数在list对象声明周期结束时调用,释放所有节点(包括头节点)!
C++ [STL之list的使用]


迭代器


list迭代器比较特殊,知道链表的小伙伴应该都懂链表不是一片连续的空间,所以不能像顺序表那样支持迭代器随机访问!(即it+1,it+2…)

同样的,链表的迭代器也分三种(理论上四种):

  • 正向迭代器
  • 反向迭代器
  • const迭代器(正向和反向)
    C++ [STL之list的使用]
    const迭代器不支持修改当前指向的值,每一种迭代器不能混用,各自使用各自的迭代器类型!
int main()
{
	vector<int> v = { 5,4,3,2,1 };
	list<int> l1(v.begin(),v.end());
	list<int>::iterator it = l1.begin();
	list<int>::reverse_iterator rit = l1.rbegin();
	while (it != l1.end())
	{
		cout << *it << endl;
		++it;		
	}
	cout << endl;
	while (rit != l1.rend())
	{
		cout << *rit << endl;
		++rit;		
	}
	return 0;
}

C++ [STL之list的使用]
虽然list迭代器不支持随机访问,但是也支持通过++和- -遍历链表,甚至可以反向遍历,可见迭代器的强大之处!

迭代器在链表中的位置:
C++ [STL之list的使用]


容量操作类


数据查询类

链表没有提前开空间这样的说法,对于数据只能查询以及对现有数据操作!
C++ [STL之list的使用]

  • empty:查询链表是否为空,为空返回真
  • size:返回当前节点的个数
  • max_size:返回当前实例对象可容纳最大节点数(这个函数常用来检查申请空间的合法性即配合resize使用,查看申请的空间是否合法)
int main()
{
	list<int> obj(10,5); //初始化10个值为5的节点
	cout << "empty:" << obj.empty() << endl;
	cout << "size:" << obj.size() << endl;
	cout << "max_size:" << obj.max_size() << endl;

	return 0;
}

C++ [STL之list的使用]



节点数量修改

list支持resize控制节点个数,resize支持增多节点和减少节点,对于增多的节点设置为val值,val有缺省参数,根据需要可进行设置初始化!
C++ [STL之list的使用]

int main()
{
	list<int> obj = { 1,2,3,4,5 };
	cout << "size:" << obj.size() << endl;
	obj.resize(10);
	cout << "size:" << obj.size() << endl;
	obj.resize(3);
	cout << "size:" << obj.size() << endl;
	return 0;
}

C++ [STL之list的使用]


数据访问


链表不支持下标访问,提供了额外对头尾节点访问的函数front和back,并且拥有引用和const引用重载函数应对不同的场景!
C++ [STL之list的使用]

int main()
{
	list<int> obj = { 668,778,888,998,1080 };
	cout << "front:" << obj.front() << endl;
	cout << "back:" << obj.back() << endl;
	return 0;
}

C++ [STL之list的使用]

不过list常用的遍历方式还是通过迭代器遍历,而且list因为迭代器所以支持范围for遍历!


增删类


因为链表插入删除效率非常高,所以增删类接口比较多,在这里介绍几种常用的接口!
C++ [STL之list的使用]



头插头删

头插push_front()头删pop_front() 只需要对头节点next(后继节点)指针操作即可!

int main()
{
	list<int> obj = { 1,2,3,4,5 };
	Print(obj);
	obj.pop_front();
	Print(obj);
	obj.push_front(668);
	Print(obj);

	return 0;
}

C++ [STL之list的使用]



尾插尾删

头插push_back()头删pop_back() 也只需要对头节点prev(前驱节点)指针操作即可!

int main()
{
	list<int> obj = { 1,2,3,4,5 };
	Print(obj);
	obj.pop_back();
	Print(obj);
	obj.push_back(668);
	Print(obj);

	return 0;
}

C++ [STL之list的使用]



重新分配

assign函数有点类似于 = 赋值重载,区别于赋值的是该函数可以跨容器达到赋值的效果(利用迭代器区间),assign函数会先将list对象清空再重新插入值!

assign函数有两个版本:

  • 分配n个值为val的节点(类似于带参构造函数)
  • 迭代器区间分配
int main()
{
	list<int> obj = { 1,2,3,4,5 };
	Print(obj);
	obj.assign(10, 5); //分配5个值为10的节点
	Print(obj);

	int arr[] = { 668 ,778,888,998,1080 };
	obj.assign(arr,arr+5); //分配arr数组区间
	Print(obj);

	return 0;
}

C++ [STL之list的使用]



任意位置插入

insert函数支持在迭代器位置插入节点,有三个版本:
C++ [STL之list的使用]

  • 在position迭代器位置下插入值为val的节点(返回新插入节点的迭代器)
  • 在position迭代器位置下插入n个值为val的节点
  • 在position迭代器位置下插入一段区间
int main()
{
	list<int> obj = { 1,2,3,4,5 };
	auto it = ++obj.begin(); //记录2位置迭代器
	Print(obj);

	it = obj.insert(it, 668); //在2位置插入668更新迭代器为668节点
	Print(obj);

	obj.insert(it, 888); //在668位置插入888
	Print(obj);

	int arr[] = { 6,7,8,9,10 };
	obj.insert(obj.begin(), arr, arr + 5); //在头节点1位置插入arr数组区间
	Print(obj);

	return 0;
}

C++ [STL之list的使用]
虽然insert函数不会导致迭代器失效,但是insert在插入单节点的函数上支持更新迭代器,必要时建议及时更新迭代器!



任意位置删除

erase函数有两种版本,删除函数需要注意迭代器失效问题,因为删除当前迭代器位置的节点后该位置节点内存已经被释放,所以两个版本的删除函数都会返回删除后的下一个节点迭代器
C++ [STL之list的使用]

int main()
{
	list<int> l1 = { 1,2,3 };
	list<int> l2 = { 1,2,3,4,5,6,7,8,9,10 };
	Print(l1);
	Print(l2);

	auto it1 = l1.begin();
	auto it2 = l2.begin();
	++it2; ++it2; ++it2;//迭代器挪动到4位置下

	it1 = l1.erase(it1); //删除手节点 - 更新迭代器
	it2 = l2.erase(l2.begin(),it2); //删除1-3区间节点(左闭右开) - 更新迭代器

	Print(l1);
	Print(l2);

	return 0;
}

C++ [STL之list的使用]
迭代器失效情况:
C++ [STL之list的使用]
对于单节点删除,如果不更新迭代器,肯定会造成野指针访问,但对于区间删除,因为是左避右开,所以删不到闭区间的迭代器,所以不更新迭代器影响不大,但是如果删除的区间是全部,则还是需要更新迭代器!
这里可以发现it1如果更新迭代器则指向下一个节点迭代器!


其他操作


清空函数

clear可以将链表清空,也就是逐个释放节点,只留下头节点(区别于析构函数,clear不会释放对象)!

int main()
{
	list<int> obj = { 1,2,3 };
	cout << "size:" << obj.size() << endl;
	obj.clear();
	cout << "size:" << obj.size() << endl;
	return 0;
}

C++ [STL之list的使用]



交换函数

list有属于自己的交换函数,同时库中的函数也支持对list进行交换!

int main()
{
	list<int> l1 = { 1,2,3 };
	list<int> l2 = { 4,5,6 };
	cout << "l1:"; Print(l1);
	cout << "l2:"; Print(l2);
	cout << endl;

	l1.swap(l2); //list对象swap函数交换
	cout << "l1:"; Print(l1);
	cout << "l2:"; Print(l2);
	cout << endl;

	std::swap(l1, l2); //库函数交换
	cout << "l1:"; Print(l1);
	cout << "l2:"; Print(l2);
	cout << endl;

	return 0;
}

C++ [STL之list的使用]
list自带的swap和库中的swap都支持容器的交换!



拼接

splice支持将一个list对象中的一个节点或者一段节点从原list对象上截取下来链接在调用对象上!
splice有三个版本:

  • 一个list截取插入到另一个list中的position迭代器位置
  • 将一个list的一个迭代器节点截取插入另一个list中的position迭代器位置
  • 将一个list的迭代器区间截取插入到另一个list中的position迭代器位置
    C++ [STL之list的使用]
int main()
{
	list<int> l1 = { 1,2,3 };
	list<int> l2 = { 4,5,6 };

	l1.splice(l1.begin(), l2); //将l2插入l1中
	cout << "l1:"; Print(l1);
	cout << "l2:"; Print(l2);
	cout << endl;

	list<int> l3 = { 1,2,3 };
	list<int> l4 = { 7,8,9 };
	l3.splice(l3.begin(),l4,l4.begin()); //将l4中的头节点插入l3中
	cout << "l3:"; Print(l3);
	cout << "l4:"; Print(l4);
	cout << endl;

	list<int> l5 = { 1,2,3 };
	list<int> l6 = { 448,558,668,778,888 };
	l5.splice(l5.begin(), l6, ++l6.begin(), l6.end()); //将l5的558-888插入l6中
	cout << "l5:"; Print(l5);
	cout << "l6:"; Print(l6);
	cout << endl;

	return 0;
}

C++ [STL之list的使用]
splice函数每次执行都需要另一个list对象的引用,哪怕只截取一个节点,说明在函数内部需要对节点进行剥离和缝合!

splice函数原理:
C++ [STL之list的使用]



移除指定值元素

remove类似于find+erase,find先找到这个值返回其迭代器供erase删除,该函数会根据给定的val值找到此值并删除,如果该值不存在,则什么也不做!
C++ [STL之list的使用]

int main()
{
	list<int> l1 = { 1,2,3 };
	l1.remove(2); //删除2
	l1.remove(5); //删除非list中的元素则执行无效
	Print(l1);

	return 0;
}

C++ [STL之list的使用]



排序

list自带排序算法,但效率非常低!

一般如果要对list排序,可以先将数据拷贝到vector,然后使用库函数sort快速排序后再拷贝会list,虽然有两次拷贝,但是排序性能仍然超过直接对list排序!

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

int main()
{
	list<int> l1;
	list<int> l2;
	for (int i = 1; i < 10000000; ++i) //一千万降序数
	{
		l1.push_back(i);
		l2.push_back(i);
	}
	//排升序	
	time_t t = clock(); //记录开始时间
	l1.sort(); //list排序
	cout << "list sort:" << clock() - t << endl;

	t = clock();
	vector<int> v(l2.begin(),l2.end());
	std::sort(v.begin(), v.end()); //库函数排序
	l2.assign(v.begin(), v.end()); //将排完序的元素分配回list
	cout << "lib sort:" << clock() - t << endl;

	return 0;
}

C++ [STL之list的使用]
使用库函数sort需要声明头文件algorithm和std命名空间,此结果是在release模式下运行!
库函数对容器操作一般都是使用迭代器!



逆置

list对象自带reverse逆置函数,库中也有reverse函数,作用是将当前序列反过来!
list自带的reverse直接调用即可,库中的reverse则是通过容器迭代器作为参数进行逆置!

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

int main()
{
	list<int> obj = { 1,2,3,4,5,6,7,8,9,10 };
	Print(obj);
	obj.reverse(); //list自带逆置
	Print(obj);
	std::reverse(obj.begin(), obj.end()); //库函数逆置
	Print(obj);

	return 0;
}

C++ [STL之list的使用]
同样的,使用库函数reverse需要声明头文件algorithm和命名空间std!



去掉重复元素

unique函数支持对元素,但是在去重之前需要进行排序,因为其底层原理是两个相邻的重复元素会删除一个,而需要将所有相同的元素相邻就需要sort排序!

同样的,如果数据量小我们可以直接使用list的排序和去重,如果数据量较大建议拷贝到vector排序然后拷贝会list再去重!

int main()
{
	list<int> obj = { 1,2,5,3,7,1,6,5,9,8,7,2 };
	Print(obj);

	obj.unique(); //不排序直接去重没有任何效果
	Print(obj);

	obj.sort(); //排序再去重才会有效果
	obj.unique();
	Print(obj);

	return 0;
}

C++ [STL之list的使用]
算法库algorithm中也有unique,同样的适用于所有容器,因为使用迭代器操作!



合并

merge函数支持将一个list合并(移动)到另一个list中(而并非复制),与splice函数类似,只不过merge是将list中的全部节点按升序或降序的方式链入被插入对象中,该函数有两种形式:
C++ [STL之list的使用]

  • 第一种是合并一个list,并按升序插入其中
  • 第二种支持控制升序和降序,我们可以选择将list按升序插入其中,也可以按降序插入其中(使用greater和less控制)
int main()
{
	list<int> l1 = {6,10};
	list<int> l2 = { 1,2,5 };
	Print(l1);

	l1.merge(l2); //将l2按默认规则链入l1中
	Print(l1);

	list<int> l3 = {3,4,9,8,7};
	l1.merge(l3, less<int>()); //将l3按升序规则链入l1中
	Print(l1);

	return 0;
}

C++ [STL之list的使用]



关于查找

  • list没有提供查找函数,因为对于链表的查找只能用迭代遍历的方式进行,所以用库中的find足以!
  • 库中的find也是基于迭代器的遍历查找,也适用于所有支持迭代器的容器,find如果查找到了就会返回该元素的迭代器,如果没找到则返回传入的开区间(end())迭代器,如果有多个重复的值则返回正向序列上第一个符号条件的值!
//find的使用
find(迭代器开区间(起始区间),迭代器闭区间(结束区间),查找元素);

find底层原理:

template<class InputIterator, class T>
InputIterator find (InputIterator first, InputIterator last, const T& val)
{
 	while (first!=last) {
    if (*first==val) return first;
    	++first;
	}
 return last;
}

使用展示:

int main()
{
	list<int> obj = { 1,2,5,3,7,1,6,5,9,8,7,2 };
	list<int>::iterator it = std::find(obj.begin(), obj.end(), 2); //找到了就返回迭代器
	cout << *it << endl;
	it = std::find(obj.begin(), obj.end(), 10);
	cout << (it == obj.end()) << endl; //没找到返回开区间
	return 0;
}

C++ [STL之list的使用]
find属于库函数,需要algorithm算法头文件和是他的命名空间!


最后

list的使用介绍到这里就结束了,相信学会了list在需要频繁增删数据的场景下我们那个轻松应对,对于vector和list两者各有优劣,应对不同的场景我们需要合理应用甚至结合使用;list的底层实现非常值得我们去研究,C++下一节将为大家介绍list模拟实现,揭开迭代器的背后秘密!

本次 <C++ list的使用> 就先介绍到这里啦,希望能够尽可能帮助到大家。

如果文章中有瑕疵,还请各位大佬细心点评和留言,我将立即修补错误,谢谢!

C++ [STL之list的使用]

🌟其他文章阅读推荐🌟
C++ <STL之vector模拟实现> -CSDN博客
C++ <STL之vector的使用> -CSDN博客
C++ <STL之string的使用> -CSDN博客
C++ <STL之string模拟实现> -CSDN博客
🌹欢迎读者多多浏览多多支持!🌹

​​

​​


文章来源地址https://www.toymoban.com/news/detail-471330.html

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

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

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

相关文章

  • C++ STL之list的使用及模拟实现

    英文解释: 也就是说: list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。 list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。 list与forward_list非常

    2024年01月24日
    浏览(80)
  • C++ STL之list接口的详细使用指南

    本文详细介绍了C++ STL中list接口的使用,包括list的基本特性、底层结构、与其他容器的比较,以及各种操作方法如插入、删除、迭代、排序等。通过阅读本文,您将对C++中的list有更深入的理解。

    2024年02月13日
    浏览(95)
  • 【C++初阶】STL详解(五)List的介绍与使用

    本专栏内容为:C++学习专栏,分为初阶和进阶两部分。 通过本专栏的深入学习,你可以了解并掌握C++。 💓博主csdn个人主页:小小unicorn ⏩专栏分类:C++ 🚚代码仓库:小小unicorn的代码仓库🚚 🌹🌹🌹关注我带你学习编程知识 1.list是一种可以在常数范围内在任意位置进行插

    2024年02月04日
    浏览(58)
  • 【C++】STL——list的介绍和使用、list增删查改函数的介绍和使用、push_back、pop_back

    list构造函数的介绍和使用      push_front()函数用于将一个新的元素插入到链表的开头位置。 通过调用push_front()函数并将待插入的元素作为参数传递给该函数,即可实现在链表开头插入新元素的操作。   和链表的插入一样,push_front()函数的时间复杂度为O(1),因为在双向链

    2024年02月15日
    浏览(56)
  • C++:STL--List

    STL容器的代码设计中, 模板编程 和 代码复用的思想贯穿始终 ,代码复用可以将各个成员接口统一起来从而 大大增加程序的可读性和可维护性 ,模板编程使得容器类可以 根据需要用于存储各种不同类型的数据 . C++STL标准库中的list使用的数据结构是带头双向循环链表 ; 链表的头

    2024年02月10日
    浏览(44)
  • 【C++】STL---list

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

    2024年02月11日
    浏览(38)
  • C++ STL list

    ✅1主页:我的代码爱吃辣 📃2知识讲解:C++之 STL list介绍和模拟实现 ☂️3开发环境:Visual Studio 2022 💬4前言:上次我们详细的介绍了vector,今天我们继续来介绍一下TSTL中的另外一个容器list。list在基础的功能和结构上就是一个双向带头的循环链表,实现起来基本不难,但是

    2024年02月13日
    浏览(35)
  • 【C++】学习STL中的list

            大家好!,今天为大家带来的一篇博客是关于STL中的list,内容主要包括list的介绍使用、list的模拟实现。以及list与vector的对比。         首先,让我们看看list的文档介绍: list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向

    2024年02月10日
    浏览(40)
  • <C++> STL_list

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

    2024年02月10日
    浏览(42)
  • 【C++学习】STL容器——list

    目录 一、list的介绍及使用 1.1 list的介绍  1.2 list的使用 1.2.1 list的构造  1.2.2  list iterator的使用 1.2.3 list capacity 1.2.4 list element access 1.2.5 list modifiers 1.2.6 list 迭代器失效 二、list的模拟实现 2.1 模拟实现list 三、list和vector的对比 一、list的介绍及使用 1.1 list的介绍 list的文档介绍

    2024年02月14日
    浏览(40)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包