C++ STL list

本文详细介绍了C++标准库中的list容器,包括其双链表实现、O(1)时间复杂度的插入和删除操作,以及与vector等其他容器的区别。通过示例代码展示了list的创建、访问、push_front、erase、unique和remove等方法的使用。此外,还对比了list和vector在空间利用率、迭代器行为以及插入删除效率上的差异,帮助读者理解何时选择使用list。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

list 的介绍

  1. list是序列容器,允许在序列中的任何位置执行固定O(1)时间的插入和删除操作,并在两个方向上进行迭代
  2. list容器使用双链表实现,双链表将每个元素存储在不同的存储位置,每个节点通过next,prev指针链接成顺序表
  3. list与其他基本标准序列容器(array、vector和deque)相比,list通常在容器内的任何位置插入、提取和移动元素,已经获得迭代器的情况下时间渐进复杂度O(1)
  4. list与其他序列容器(vector、array、deque)相比,list和forward_list(单链表实现)的主要缺点是它们不能通过位置直接访问元素,例如,要访问列表中的第六个元素,必须从已知位置(如开始或结束)迭代到该位置,需要线性时间开销
  5. 存储密度低,list要使用一些额外的内存空间(next、prev)来保持与每个元素相关联(前后序的线性)的链接信息,从而导致存储小元素类型(如char、short、int等)的列表存储密度低

在这里插入图片描述

list 的使用

int main()
{
	std::list<int> ilist;
	ilist.push_back(12);
	ilist.push_back(23);
	std::list<int>::iterator it = ilist.begin();

	//it = it + 5;  list 不可以 直接跳跃

	std::vector<int> ivec;
	std::vector<int>::iterator vit = ivec.begin();
	vit = vit + 5;
}

list 中的迭代器属于双向迭代器,既可以 it++,也可以 it--

list 的创建

int main()
{
	int ar[] = { 12,23,34,45,56,67,78,89,90,100 };

	std::vector<int> vec = { 1,2,3,4,5,6,7 };
	std::list<int> ilist1;
	std::list<int> ilist2 = { 12,23,34,45,56 };
	std::list<int> list3(10, 23);//创建10个节点,每个都为23
	std::list<int> ilist4(ar, ar + 10);
	std::list<int> ilist5(ilist2);
	std::list<int> ilist6(std::move(ilist2));
	std::list<int> ilist7(vec.begin(), vec.end());
}
  • list2 的构建方法与vector相似,都是将其放入初始化列表 initializer_list 中,在从初始化列表中一个一个取值进行创建
  • list3 创建10个节点,每个节点都创建为23
  • list4 创建中,ar是数组的首元素地址,ar+10指向最后一个元素后继,将该数组的每个元素放如今list构成双链表
  • list7 属于新版本STL的能力,将vector中的元素创建在list中,但是直接传入vec是不可以的

访问、push_front、erase、unique、remove

int main()
{
	std::list<int> ilist1 = { 1,2,3,4,5,6,7 };
	std::list<int>::iterator it = ilist1.begin();
	for (; it != ilist1.end(); ++it)
	{
		cout << *it << endl; //[] ,at(i)
	}
	for (auto& x : ilist1)
	{
		cout << x << endl;
	}
	return 0;
}

list 中没有opertaor []at(i) 的方法,因为list不属于连续空间不能进行连续访问

list中的大部分方法与vector相似

同时list中即存在头部插入也有尾部插入,因为这两种方法的时间复杂程度都是1,而vector中尾部插入时间复杂度为1,而头部插入需要遍历整个容器

int main()
{
	std::list<int> ilist1 = { 1,2,3,4,5,6,7 };
	std::list<int>::iterator it = ilist1.begin();
	cout << *it << endl;
	ilist1.push_front(10);
	cout << *it << endl;
	return 0;
}

vector 在容量已满的情况下进行push_bcak,那么会创建新的vector 并且进行拷贝构造以及析构旧的容器内的对象,继而会导致原本的迭代器失效

但是list的插入并不会影响原本的迭代器,因为新元素的插入,会购买一个节点进行插入,并不会影响旧的元素

int main()
{
	std::list<int> ilist1 = { 1,2,3,4,5,6,7 };
	std::list<int>::iterator it = ilist1.begin();
	
	it = ilist1.erase(it);
	cout << *it << endl;
	return 0;
}

这里对 it 元素进行删除,并且将后续元素在赋值给 it

int main()
{
	std::list<int> ilist1 = { 2,4,7,4,3,1,5,4,89,8,7,56,4,2,45 };
	
	ilist1.sort(); //排序
	for (auto& x : ilist1)
	{
		cout << x << " ";
	}
	cout << endl;
	
	ilist1.unique(); //唯一性
	for (auto& x : ilist1)
	{
		cout << x << " ";
	}
	cout << endl;
}

首先要进行唯一性操作,首先必须进行排序操作
在这里插入图片描述

int main()
{
	std::list<int> ilista = { 1,2,3,3,3,4,5,6,7,8,9 };

	for (auto& x : ilista)
	{
		cout << x << " ";
	}
	cout << endl;

	ilista.remove(3); //移除满足特定标准的元素
	for (auto& x : ilista)
	{
		cout << x << " ";
	}
	cout << endl;
	return 0;
}

对特定元素进行删除,例如将所有的 3 进行删除
在这里插入图片描述

list 与 vector 的区别

项目vectorlist
底层实现连续存储的容器,动态数组,在堆上分配空间动态双向链表,在堆上分配空间
空间利用率连续空间,不宜造成内存碎片,空间利用率高节点不连续,易造成内存碎片,小元素使节点密度低,空间利用率低
查询元素iterator operator[],find O(n),binary_search O(logn)iterator find O(n)
迭代器随机迭代器,迭代器检查越界,支持++,–,+,+=,<,>,!=,==双向迭代器,迭代器检查越界,支持++,–,==,!=
迭代器失效插入和删除元素都会导致迭代失效插入元素不会导致迭代器失效、删除元素导致当前迭代器失效,不影响其他迭代器

插入和删除 vector

  • 在最后插入(容量够):push_back(val), O(1)
  • 在最后插入(容量不够): 需要内存申请和释放,以及对之前数据进行拷贝
  • 在中间插入(容量够):内存拷贝
  • 在中间插入(容量不够):需要内存申请和释放,以及对之前数据的拷贝 insert(it,va) O(n)
  • 删除:pop_back() 在最后删除 O(1)
  • 在中间删除:内存拷贝,不需要释放内存(将数据往前移动),erase(it) O(n)
  • resize(10):开辟空间,还放入数据
  • reserve(10):只开辟空间,不放入数据,初始化 vector 空间,提高 vector 的使用效率

插入与删除 list

  • 插入:O(1),需要内存申请
  • push_front(val) O(1);push_back(val) O(1); insert(it,val) O(1)
  • 删除:O(1),需要内存释放
  • pop_front() O(1);pop_back() O(1);erase(it) O(n)

总结

  1. vector 底层实现是数组,list 是双向 链表
  2. vector 支持随机访问,list不支持
  3. vector 是顺序内存,list不是
  4. vector 在中间节点进行插入删除会导致内存拷贝,list不会
  5. vector 一次性分配好内存,不够时才进行2倍扩容(或1.5倍),list每次插入新结点都会进行内存申请
  6. vector 随机访问性能高,插入删除性能差,list随机访问性能差,插入删除性能好

使用选择

什么时候该用vector?什么时候该用list?

  1. 如果需要高效的随机存取,而不在乎插入和删除的效率(很少使用插入和删除操作),选用vector
  2. 如果需要大量的插入和删除的操作,随机存取很少使用,选用list
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值