C++ list 类的使用

一、list的介绍及使用

1.1 list的介绍

  1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。

  2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向

其前一个元素和后一个元素。

  1. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。

  2. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。

  3. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)

1.2 list的使用

list中的接口比较多,此处类似,根据前面所学的string和vector,我们可以进行初步的使用list

cpp 复制代码
void test_list1()
{
	//创建一个int的list对象,并尾插数据
	list<int> l1;
	l1.push_back(34);
	l1.push_back(31);
	l1.push_back(32);
	l1.push_back(35);
	l1.push_back(38);
	//遍历(注意:list不能使用[ ]进行访问了,list使用迭代器和范围for遍历)
	list<int>::iterator it1 = l1.begin();
	while (it1 != l1.end())
	{
		cout << *it1 << endl;
		++it1;
	}
	cout << endl;
	//范围for遍历
	for (auto element1 : l1)
	{
		cout << element1 << " ";
	}
	cout << endl;
}

以下为list中一些常见的重要接口:

1.2.1 list的构造

构造函数( (constructor)) 接口说明

list(size_type n, const value_type& val = value_type()) 构造的list中包含n个值为val的元素

list() 构造空的list

list(const list& x) 拷贝构造函数

list(InputIterator first, InputIterator last) 用[first, last)区间中的元素构造list

1.2.2 list iterator的使用

函数声明 接口说明

begin +end 返回第一个元素的迭代器 + 返回最后一个元素下一个位置的迭代器

rbegin +rend 返回第一个元素的reverse_iterator, 即end位置,返回最后一个元素下一个 位置的reverse_iterator, 即begin位置

cpp 复制代码
void test_list2()
{
	list<int> lt;
	//尾插数据
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);

	//头插数据
	lt.push_front(10);
	lt.push_front(20);
	lt.push_front(30);
	lt.push_front(40);

	//迭代器打印
	list<int>::iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

【注意】

  1. begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动

  2. rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动

1.2.3 list容器

函数声明 接口说明

empty 检测list是否为空,是返回true,否则返回false

size 返回list中有效节点的个数

1.2.4 list 元素接口

函数声明 接口说明

front 返回list的第一个节点中值的引用

back 返回list的最后一个节点中值的引用

1.2.5 list增删查改

函数声明 接口说明

push_front 在list首元素前插入值为val的元素

pop_front 删除list中第一个元素

push_back 在list尾部插入值为val的元素

pop_back 删除list中最后一个元素

insert 在list position 位置中插入值为val的元素

erase 删除list position位置的元素

swap 交换两个list中的元素

clear 清空list中的有效元素

注意:list和vector都没有提供find了,因为find被直接放到了算法里,通过迭代器实现

cpp 复制代码
void test_list3()
{
	list<int> lt1;
	lt1.push_back(3);
	lt1.push_back(5);
	lt1.push_back(7);
	lt1.push_back(9);
	for (auto element : lt1)
	{
		cout << element << " ";
	}
	cout << endl;

	//在第三个位置插入一个数字100(insert)
	//不能像以前vector一样使用迭代器直接插入了
	list<int>::iterator it = lt1.begin();
	for (size_t i = 0; i < 3; ++i)
	{
		++it;
	}
	lt1.insert(it, 100);
	for (auto element : lt1)
	{
		cout << element << " ";
	}
	cout << endl;

	//用find查找9的位置
	list<int>::iterator init = lt1.begin();
	auto pos = find(init, lt1.end(), 9);
	if (pos != lt1.end())
	{
		cout << *pos;
		//9之前插入30
		lt1.insert(pos, 30);
	}
	for (auto element : lt1)
	{
		cout << element << " ";
	}
	cout << endl;
}

1.2.6 list 操作

函数声明 接口说明

reverse 逆置list

sort 对list进行排序

merge 对两个list进行归并

unique 对链表去重(前提是有序)

remove 就是找到这个位置,并删除(相当于find+erase)

splice 把一个list的内容直接转移到另外一个

cpp 复制代码
void test_list4()
{
	list<int> lt1;
	lt1.push_back(3);
	lt1.push_back(67);
	lt1.push_back(7);
	lt1.push_back(9);
	for (auto element : lt1)
	{
		cout << element << " ";
	}
	cout << endl << "逆置后:";
	reverse(lt1.begin(), lt1.end());
	for (auto element : lt1)
	{
		cout << element << " ";
	}
	cout << endl;
	//对list排序只能使用list的sort
	lt1.sort();
	for (auto element : lt1)
	{
		cout << element << " ";
	}
	cout << endl;

	int begin = clock();
	//由于list的sort效率较低,所以可以把list的数据拷贝到vector排完序在拷贝回去
	list<int> lt2;
	vector<int> v;
	srand((size_t)time(0));
	int n = 100000;
	for (size_t i = 0; i < n; i++)
	{
		lt2.push_back(rand());
	}
	//把list产生的数拷贝到vector
	for (auto element : lt2)
	{
		v.push_back(element);
	}
	//排序
	sort(v.begin(), v.end());
	//把vector的数据拷贝回list
	size_t i = 0;
	for (auto& element : lt2)
	{
		element = v[i++];
	}
	int end = clock();
	cout << "sort:" << end - begin << "ms" << endl;

	int arr[] = { 34,12,34,67 };
	list<int> lt3(arr, arr + sizeof(arr) / sizeof(arr[0]));
	lt3.remove(34);
	lt3.remove(2300);
	for (auto e : lt3)
	{
		cout << e << " ";
	}
	cout << endl;
}

注意:remove如果没找到也不会报错,只会上面都不做

1.2.7 list的迭代器失效

迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。

因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响,也就是说list下list的insert不会造成迭代器失效,而erase会造成迭代器失效

cpp 复制代码
void test_list5()
{
	int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
	list<int> l(array, array + sizeof(array) / sizeof(array[0]));

	auto it = l.begin();
	while (it != l.end())
	{
		// erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给其赋值
		l.erase(it);
		++it;//迭代器已经失效
	}
}

1.2.8 迭代器的划分(按照性质划分)

1.单向迭代器(++):forward_list/unordered_xxx

2.双向(++/--): list/map/set

3.随机(++/--/+/-): vector/string/deque

注意:三种迭代器划分由容器的底层结构决定

相关推荐
景彡先生3 小时前
Python函数定义与调用全解析:从基础语法到实战技巧
linux·开发语言·python
l1t3 小时前
利用DuckDB SQL求解集合数学题
数据库·sql·算法·集合·duckdb
yuyanjingtao3 小时前
CCF-GESP 等级考试 2024年9月认证C++四级真题解析
c++·算法·青少年编程·gesp·csp-j/s
光头闪亮亮3 小时前
curl库应用-c++客户端示例及golang服务端应用示例
c++·go
微笑尅乐3 小时前
洗牌算法讲解——力扣384.打乱数组
算法·leetcode·职场和发展
Lei_3359673 小时前
[算法]背包DP(01背包、完全背包问题、多重背包、分组背包、混合背包问题、有依赖的背包问题等)
c++·算法
uesowys3 小时前
华为OD算法开发指导-比赛的冠亚季军
算法·华为od
天选之女wow3 小时前
【代码随想录算法训练营——Day48】单调栈——42.接雨水、84.柱状图中最大的矩形
算法·leetcode
不知名。。。。。。。。3 小时前
算法之动态规划
算法·动态规划