list【1】介绍与使用(超详解哦)

list的介绍与使用

  • 引言
  • list介绍
  • 接口使用
    • 默认成员函数
    • 迭代器
    • 容量
    • 元素访问
    • 数据修改
  • list的算法接口
  • 总结

引言

继vector之后,我们继续来介绍STL容器:list
对于容器的使用其实有着类似的模式,参考之前vector的使用可以让我们更快的上手:
戳我看vector介绍与使用详解哦

list介绍

在之前C语言部分我们就认识了链表,STL中的list是一个双向链表

相对于vector的底层空间是连续的,list的底层空间是通过指针链接的链状的不连续的空间
这样的结构相较连续空间扩容更加方便:不需要重开空间移动数据,只需要在开辟一个新的结点后,将其与前面的结点链接起来即可。其在任意位置插入删除都不需要挪动数据,效率较高:只需要释放或增加对应结点的数据,然后将剩下的结点链接起来即可;
相较于vector,list不能实现高效的任意访问其中的元素,要随机访问元素只能从头或尾遍历访问,效率较低。所以list中也就直接没有实现operator[]

所以,list适用于需要经常在任意位置插入删除大量数据,且不需要经常访问任意位置元素的数据的存储
list是一个类模板,可以支持存储任意类型:
list【1】介绍与使用(超详解哦)_第1张图片

接口使用

与vector类似,list也有默认成员函数、迭代器、容量、元素访问、数据修改等接口(使用库list时需要包含头文件#include

默认成员函数

构造
list【1】介绍与使用(超详解哦)_第2张图片
list的构造函数重载了4个版本,支持无参构造、n个指定元素构造、迭代器区间构造以及拷贝构造。迭代器区间构造是一个函数模板,即可以使用任一InputIterator迭代器区间来构造list。

使用时与vector类似,由于list是一个类模板,所以在使用list来实例化对象是,就需要显式指定模板参数,如list

#include
#include
#include
using namespace std;

int main()
{
	list<int> l1; //无参初始化
	for (auto e : l1)
	{
		cout << e << " ";
	}
	cout << endl;

	list<int> l2(10, 6);    //使用10个6初始化l2
	for (auto e : l2)
	{
		cout << e << " ";
	}
	cout << endl;

	vector<int> v1(10, 5);  //使用10个5初始化v1
	list<int> l3(v1.begin(), v1.end());  //使用迭代器区间初始化
	for (auto e : l3)
	{
		cout << e << " ";
	}
	cout << endl;

	list<int> l4(l3); //拷贝构造
	for (auto e : l4)
	{
		cout << e << " ";
	}
	cout << endl;
}

list【1】介绍与使用(超详解哦)_第3张图片

析构
在这里插入图片描述
析构函数会在list对象生命周期结束时由编译器自己调用,以释放其内部资源。

赋值重载
list【1】介绍与使用(超详解哦)_第4张图片
l2赋值给l1

int main()
{
	list<int> l1;
	for (auto e : l1)
	{
		cout << e << " ";
	}
	cout << endl;

	list<int> l2(10, 6);
	for (auto e : l2)
	{
		cout << e << " ";
	}
	cout << endl;

	l1 = l2;  //将l2赋值给l1
	for (auto e : l1)
	{
		cout << e << " ";
	}
	cout << endl;
}

list【1】介绍与使用(超详解哦)_第5张图片

迭代器

list【1】介绍与使用(超详解哦)_第6张图片
vector的迭代器的底层就是原生指针,与vector不同,list的迭代器是指针的封装(在list实现时会详细介绍),但是在使用list迭代器时是与vector几乎一致的。
但是需要注意的是,list的迭代器由于不是原生指针,在+-数字时的成本太高,所以就不支持迭代器+-,只允许迭代器进行++--的双向移动,即为bidirectional iterator

begin返回首元素位置的迭代器,end返回尾元素下一个位置的迭代器、rbegin返回尾元素的反向迭代器、rend返回首元素前一个位置的反向迭代器。后面的cbegincendcrbegincrend都是返回其对应的const迭代器,但是前面的函数都有重载const版本:

int main()
{
	list<int> l1;
	l1.push_back(1);
	l1.push_back(2);
	l1.push_back(3);
	l1.push_back(4);
	l1.push_back(5);
	l1.push_back(6);
	for (auto e : l1)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << *l1.begin() << endl;
	cout << *(--l1.end()) << endl;
	cout << *l1.rbegin() << endl;
	cout << *(--l1.rend()) << endl;

	return 0;
}

list【1】介绍与使用(超详解哦)_第7张图片

容量

在这里插入图片描述
对于list,没有容量的概念,也就不需要有扩容的操作。
所以在容量部分,list只提供了sizeempty,用于返回list中元素个数与判断list是否为空

int main()
{
	vector<int> l1;
	l1.push_back(1);
	l1.push_back(2);
	l1.push_back(3);
	l1.push_back(4);
	l1.push_back(5);
	l1.push_back(6);
	for (auto e : l1)
	{
		cout << e << " ";
	}
	cout << endl;

	cout << "size:" << l1.size() << " ";
	cout << "is empty:" << l1.empty() << " ";

	return 0;
}

list【1】介绍与使用(超详解哦)_第8张图片

元素访问

在这里插入图片描述
list不是连续的空间,想要通过下标访问任意位置的元素时成本较高,所以list没有重载operator[],而只能通过迭代器访问元素;
同时,list提供了frontback接口来实现对首尾元素的访问

int main()
{
	vector<int> l1;
	l1.push_back(1);
	l1.push_back(2);
	l1.push_back(3);
	l1.push_back(4);
	l1.push_back(5);
	l1.push_back(6);
	for (auto e : l1)
	{
		cout << e << " ";
	}
	cout << endl;

	cout << l1.front() << endl;
	cout << l1.back() << endl;

	return 0;
}

list【1】介绍与使用(超详解哦)_第9张图片

数据修改

list【1】介绍与使用(超详解哦)_第10张图片
与vector类似

push_frontpush_back用于在头、尾插入一个元素;
pop_frontpop_back用于在头、尾删除一个元素;

int main()
{
	list<int> l;
	l.push_back(1);
	l.push_back(2);
	l.push_back(3);
	l.push_front(4);
	l.push_front(5);
	l.push_front(6);
	for (auto e : l)
	{
		cout << e << " ";
	}
	cout << endl;

	l.pop_back();
	l.pop_front();
	for (auto e : l)
	{
		cout << e << " ";
	}
	cout << endl;

	return 0;
}

list【1】介绍与使用(超详解哦)_第11张图片

list【1】介绍与使用(超详解哦)_第12张图片
insert用于在pos位置(迭代器位置)插入一些数据,包括单个元素、多个指定元素以及一个迭代器区间中的元素:

int main()
{
	list<int> l;
	l.push_back(1);
	l.push_back(2);
	l.push_back(3);
	l.push_back(4);
	l.push_back(5);
	l.push_back(6);
	for (auto e : l)
	{
		cout << e << " ";
	}
	cout << endl;

	l.insert(l.begin(), 10);  //相当于头插一个10
	l.insert(l.begin(), 3, 20);  //相当于头插3个20
	list<int> l2(3, 30);  //使用3个30构造l2
	l.insert(l.end(), l2.begin(), l2.end()); //相当于尾插一个l2
	for (auto e : l)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

list【1】介绍与使用(超详解哦)_第13张图片

list【1】介绍与使用(超详解哦)_第14张图片
erase用于删除pos位置的一个元素或一个迭代器区间中的元素

int main()
{
	list<int> l;
	l.push_back(1);
	l.push_back(2);
	l.push_back(3);
	l.push_back(4);
	l.push_back(5);
	l.push_back(6);

	l.erase(++l.begin(), --l.end());
	for (auto e : l)
	{
		cout << e << " ";
	}
	cout << endl;

	l.erase(l.begin());  //相当于头删
	for (auto e : l)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

list【1】介绍与使用(超详解哦)_第15张图片

在这里插入图片描述
resize用于修改list中的容量:
n小于list的元素个数时,就删除至n个元素;
n大于list的元素个数时,就用指定元素value补足:

int main()
{
	list<int> l;
	l.push_back(1);
	l.push_back(2);
	l.push_back(3);
	l.push_back(4);
	l.push_back(5);
	l.push_back(6);
	for (auto e : l)
	{
		cout << e << " ";
	}
	cout << endl;

	l.resize(3);  //删至3个元素
	for (auto e : l)
	{
		cout << e << " ";
	}
	cout << endl;

	l.resize(10, 6); //补到10个元素,不足的用6补
	for (auto e : l)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

list【1】介绍与使用(超详解哦)_第16张图片

swap用于交换两个list中的数据
clear用于清理list中的数据,这里就不再演示了。

list的算法接口

list【1】介绍与使用(超详解哦)_第17张图片
同时,list还提供了一些算法接口,如reversesort等。

  1. 对于reverse算法库中的接口,是通过循环左右交换的方式来实现的:
    list【1】介绍与使用(超详解哦)_第18张图片
    对于list这样,通过逻辑连续的线性结构,我们可以使用更为简单的改变链接顺序的方法来实现反转,list的算法中就是这样实现的(戳我看反转链表OJ 思路与该算法类似)

  2. 对于算法库中sort的接口,使用的是快排的方式来排序的,快排算法是需要三数取中来优化的,即他需要能够+-RandomAccessIterator迭代器作为参数:
    list【1】介绍与使用(超详解哦)_第19张图片
    而list的迭代器却不支持+-的操作,所以不能使用算法库中的sort函数,所以list提供了这个接口。
    但是list中的sort函数效率远没有快排的效率高,所以在进行大量list数据的排序时,可以将其数据转移到vector中排序后再转移回来;也可以干脆不适用list来存储需要大量排序的数据

总结

到此,关于list的简介与接口使用就介绍完了
接下来会进行模拟实现,欢迎持续关注哦

如果大家认为我对某一部分没有介绍清楚或者某一部分出了问题,欢迎大家在评论区提出

如果本文对你有帮助,希望一键三连哦

希望与大家共同进步哦

你可能感兴趣的:(C++初阶,list,数据结构,stl)