C++基础——List详解

list的介绍

list实际上就是一个带头双向循环链表

文档介绍

  1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
  2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
  3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,让其更简单高效。
  4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
  5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)

list的使用

list的接口比较多,我们只需要掌握一些常用的接口即可。

list的构造函数

list的构造函数常用的是无参构造和拷贝构造

构造函数函数说明
list()无参构造,得到空的list
list(const list& x)拷贝构造函数
list(size_type n, const value_type& val = value_type())构造n个元素且数值为val的list
list (InputIterator first, InputIterator last)使用迭代器构造list
//常用无参构造和拷贝构造
void test2()
{
	//1.默认构造,list() 得到空list
	list<int> lt1;
	lt1.push_back(1);
	lt1.push_back(2);
	lt1.push_back(3);
	lt1.push_back(4);
	//2.拷贝构造	list(const list& x)
	list<int> lt2(lt1);
	for (auto it : lt2)
	{
		cout << it << " ";
	}
	cout << endl;

	//3.list(size_type n, const value_type& val = value_type())
	list<int> lt3(10, 1);
	for (auto it : lt3)
	{
		cout << it << " ";
	}
	cout << endl;

	//4.list (InputIterator first, InputIterator last)
	list<int> lt4(lt1.begin(), lt1.end());
	for (auto it : lt4)
	{
		cout << it << " ";
	}
	cout << endl;
}
int main()
{
	test2();
	return 0;
}

上述构造方法,基本上和vector是一致的,只需要掌握住常用的两种构造方法即可

list iteraotr的使用

list支持正向和反向迭代器

begin+end:正向迭代器iteraotr,分别获取list的首元素地址和最后一个元素的下一个地址

rbegin+rend:反向迭代器reverse_iterator,分别获取list的最后一个元素的地址和首元素地址的前一个位置

void test3()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);

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

	//rbegin+rend 反向迭代器
	list<int>::reverse_iterator rit = lt.rbegin();
	while (rit != lt.rend())
	{
		cout << *rit << " ";
		++rit;
	}
	cout << endl;
}
int main()
{
	test3();
	return 0;
}

注意:无论正向还是反向迭代器,都是++迭代器,且list不支持迭代器+n的操作(物理空间不连续)

list的常用函数

函数名函数说明
push_front头插
pop_front头删
push_back尾插
pop_back尾删
insert在pos位置插入数据val
erase删除pos位置/迭代器区间的数据
swap交换链表(实际上就是交换两个list的成员变量的地址)
clear清空链表(清空数据)
empty判断list是否为空
size得到list元素个数
front得到list首元素
back得到list最后一个元素
resize更改size大小

熟悉掌握这些函数即可,其他函数可以在文档中查看

push和pop的函数

使用规则很简单,如下所示

void test4()
{
	list<int> lt;
	//1.push_back 尾插
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	for (auto it : lt)
	{
		cout << it << " ";
	}
	cout << endl;
	//2.pop_back  尾删
	lt.pop_back();
	lt.pop_back();
	for (auto it : lt)
	{
		cout << it << " ";
	}
	cout << endl;

	//3.push_front  头插
	lt.push_front(10);
	lt.push_front(20);
	for (auto it : lt)
	{
		cout << it << " ";
	}
	cout << endl;

	//4.pop_front()  头删
	lt.pop_front();
	lt.pop_front();

	for (auto it : lt)
	{
		cout << it << " ";
	}
	cout << endl;
}
int main()
{
	test4();
	return 0;
}

insert

list的insert不存在迭代器失效,因为不移动数据,erase之后pos位置的迭代器失效,因为节点没了

总结:在pos位置上插入一个或多个数据val,多个数据表示方式:迭代器区间表示插入的多个数据、n*val

void test5()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	//1.iterator insert (iterator position, const value_type& val);
	//返回迭代器
	lt.insert(lt.begin(), 10);
	for (auto it : lt)
	{
		cout << it << " ";
	}
	cout << endl;

	//2.void insert (iterator position, size_type n, const value_type& val);
	lt.insert(lt.begin(), 5, 100);
	lt.insert(lt.begin(), 10);
	for (auto it : lt)
	{
		cout << it << " ";
	}
	cout << endl;

	//3. void insert (iterator position, InputIterator first, InputIterator last);
	int a[] = { 10,20,30,40 };
	lt.insert(lt.begin(), a, a + 3);
	for (auto it : lt)
	{
		cout << it << " ";
	}
	cout << endl;
}
int main()
{
	test5();
	return 0;
}

erase

void test6()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);
	//1.iterator erase (iterator position);
	lt.erase(lt.begin());
	for (auto it : lt)
	{
		cout << it << " ";
	}
	cout << endl;
	//由于不能使用lt.begin()+n的操作随意访问,所以当我们要删除指定位置pos的数据的时候,先要遍历得到pos的位置
	//比如想要删除第三个数据
	int num = 0;
	auto it = lt.begin();
	while (it != lt.end())
	{
		num++;
		if (num == 3)
		{
			lt.erase(it);
			break;
		}
		it++;
	}
	for (auto it : lt)
	{
		cout << it << " ";
	}
	cout << endl;

	//2.iterator erase (iterator first, iterator last);
	//同上,所以list删除指定位置数据的时候,麻烦一点,主要是得先遍历一遍得到我们想要得到的迭代器pos地址
	
	//全部删除
	lt.erase(lt.begin(), lt.end());
	if (lt.empty())
	{
		cout << "当前list为空" << endl;
	}
}
int main()
{
	test6();
	return 0;
}

总结:erase对于list来讲,在取的pos位置上,需要遍历得到(不支持begin()+n的操作)

其他函数

void test7()
{
	list<int> lt1;
	lt1.push_back(1);
	lt1.push_back(2);
	lt1.push_back(3);
	lt1.push_back(4);
	lt1.push_back(5);
	list<int> lt2(lt1);
	lt2.pop_back();
	lt2.pop_back();
	cout << "lt1:";
	for (auto it : lt1)
	{
		cout << it << " ";
	}
	cout << endl;
	cout << "lt2:";
	for (auto it : lt2)
	{
		cout << it << " ";
	}
	cout << endl;
	//1.swap 交换链表
	//在<algorithm>中template <class T> void swap (T& a, T& b);
	//在list中void swap (list& x);

	lt1.swap(lt2);
	cout << "lt1:";
	for (auto it : lt1)
	{
		cout << it << " ";
	}
	cout << endl;
	cout << "lt2:";
	for (auto it : lt2)
	{
		cout << it << " ";
	}
	cout << endl;
	//swap(lt1,lt2)   
	//2.clear 清空
	if (lt1.empty())
	{
		cout << "当前list为空" << endl;
	}
	else
	{
		cout << "list非空" << endl;
	}
	lt1.clear();
	//3.empty  判空
	if (lt1.empty())
	{
		cout << "当前list为空" << endl;
	}
	else
	{
		cout << "list非空" << endl;
	}
	//4.size  得到当前元素的个数
	cout << "lt1的元素个数:" << lt1.size() << endl;
	cout << "lt2的元素个数:" << lt2.size() << endl;
	//5.front 得到第一个元素
	cout << lt2.front() << endl;
	//6.back 得到最后一个元素
	cout << lt2.back() << endl;
	//7.resize 更改size大小
	lt2.resize(2);
	for (auto it : lt2)
	{
		cout << it << " ";
	}
	cout << endl;
}
int main()
{
	test7();
	return 0;
}

还有一些函数是比较好玩的,比如merge、unique、reverse、remove、splice

merge表示合并两个链表,需要将两个链表sort排序之后再使用merge合并,lt1.merge(lt2)将lt2中节点都放在了lt1中,lt2成为空链表

unique表示删除重复项,只保留list中唯一项,需要sort排序list,再使用unique

reverse就是将list反转

remove 删除val,如果list没有val,不做处理,如果有,那就删除

splice 转移,将a链表的节点取下来放在b的链表中,可以a作用与于a,但是要保证节点不要重叠

void test9()
{
	//测试merge
	list<int> lt1;
	lt1.push_back(1);
	lt1.push_back(3);
	lt1.push_back(2);
	lt1.push_back(20);
	lt1.push_back(2);

	list<int> lt2;
	lt2.push_back(1);
	lt2.push_back(22);
	lt2.push_back(34);
	lt2.push_back(124);
	lt2.push_back(5);
	cout << "lt1:";
	for (auto it : lt1)
	{
		cout << it << " ";
	}
	cout << endl;
	cout << "lt2:";
	for (auto it : lt2)
	{
		cout << it << " ";
	}
	cout << endl;
	//先排序
	lt1.sort();
	lt2.sort();
	lt1.merge(lt2);
	cout << "lt1:";
	for (auto it : lt1)
	{
		cout << it << " ";
	}
	cout << endl;
	cout << "lt2:";
	for (auto it : lt2)
	{
		cout << it << " ";
	}
	cout << endl;
	cout << "lt2元素个数" <<lt2.size() <<endl;
}

void test10()
{
	list<int> lt1;
	//测试merge   空串也是可以使用的
	list<int> lt2;
	lt2.push_back(1);
	lt2.push_back(22);
	lt2.push_back(34);
	lt2.push_back(124);
	lt2.push_back(5);
	lt2.sort();
	lt1.merge(lt2);
	cout << "lt1:";
	for (auto it : lt1)
	{
		cout << it << " ";
	}
	cout << endl;
	cout << "lt2:";
	for (auto it : lt2)
	{
		cout << it << " ";
	}
	cout << endl;
	cout << "lt2元素个数" << lt2.size() << endl;
}
//测试unique
void test11()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);
	cout << "lt:";
	for (auto it : lt)
	{
		cout << it << " ";
	}
	cout << endl;
	lt.sort();
	lt.unique();
	cout << "lt:";  //lt:1 2 3 4 5 1 2 3 4 5
	for (auto it : lt)
	{
		cout << it << " ";
	}
	cout << endl;	//lt:1 2 3 4 5
}
//测试remove和reverse
void test12()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);

	lt.remove(2);
	cout << "lt:";
	for (auto it : lt)
	{
		cout << it << " ";
	}
	cout << endl;	//lt:1 3 4 5
	lt.remove(20);
	cout << "lt:";
	for (auto it : lt)
	{
		cout << it << " ";
	}
	cout << endl;
	//reverse逆序lt
	lt.reverse();
	cout << "lt:";
	for (auto it : lt)
	{
		cout << it << " ";
	}
	cout << endl;
}
//splice测试
void test13()
{
	list<int> lt1;
	lt1.push_back(1);
	lt1.push_back(2);
	lt1.push_back(3);
	lt1.push_back(4);
	lt1.push_back(5);
	list<int> lt2(lt1);
	lt2.pop_back();
	lt2.pop_back();

	cout << "lt1:";
	for (auto it : lt1)
	{
		cout << it << " ";
	}
	cout << endl;
	cout << "lt2:";
	for (auto it : lt2)
	{
		cout << it << " ";
	}
	cout << endl;
	//创建一个空链表lt3
	list<int> lt3;
	//使用splice,将lt2的第一个节点放在lt3头部
	lt3.splice(lt3.begin(), lt2,lt2.begin());
	cout << "splice移动之后" << endl;
	cout << "lt2:";
	for (auto it : lt2)
	{
		cout << it << " ";
	}
	cout << endl;
	cout << "lt3:";
	for (auto it : lt3)
	{
		cout << it << " ";
	}
	cout << endl;

	//将整个lt2链表放在lt3的头部
	lt3.splice(lt3.begin(), lt2);

	cout << "第一次splice移动之后" << endl;
	cout << "lt2:";
	for (auto it : lt2)
	{
		cout << it << " ";
	}
	cout << endl;
	cout << "lt3:";
	for (auto it : lt3)
	{
		cout << it << " ";
	}
	cout << endl;

	//区间移动,将lt3的所有内容,移动到lt2中
	lt2.splice(lt2.begin(), lt3, lt3.begin(), lt3.end());

	cout << "第二次splice移动之后" << endl;
	cout << "lt2:";
	for (auto it : lt2)
	{
		cout << it << " ";
	}
	cout << endl;
	cout << "lt3:";
	for (auto it : lt3)
	{
		cout << it << " ";
	}
	cout << endl;
}
int main()
{
	test12();
	return 0;
}

迭代器失效

现在对于迭代器简单理解为指针即可,迭代器失效就是其所指向的节点的地址失效了,list中只有该节点被删除了,才会使得该节点迭代器失效

在list中,因为自身数据结构的没有扩容这一事情,所以插入不会导致list迭代器失效,只有在删除的时候才会失效,并且失效的是指向被删除节点的迭代器,其他迭代器不受影响。

迭代器分类

迭代器分为三类:单向、双向、随机

迭代器是连接容器和算法的桥梁,提供统一方式访问容器,可以不用去考虑容器的底层结构,就能访问容器

迭代器介绍
单向(forward iterator)单向迭代器只能++,主要是单链表(forward_list)、哈希
双向(Bidirectional Iterator)双向迭代器,能++ / - -,主要是list和map、set
随机(random_access_iterator)随机迭代器,能++ / +n / - -等操作,主要是vector、string、deque

<algorithm>中,不同的函数接口,有不同的迭代器类型,能使用不同的接口。

总结

list的优点:

1.支持任意位置插入数据,更加效率

2.按需进行释放空间,不会有扩容问题

list的缺点:

1.不能随机访问,即下标访问

2.cpu高速缓存效率低

高速缓存是一个用于存储临时数据的快速存储器,位于CPU和主内存之间。它具有较高的读取和写入速度,能够提供更快的数据访问。

CPU高速缓存是计算机处理器中的一种临时存储器,用于加快对内存中数据的访问速度。当CPU高速缓存效率低时,意味着CPU无法有效利用缓存和内存之间的数据传输,导致程序的执行速度变慢。这可能是由于代码结构不合理、数据访问模式不佳等原因引起的。为了提高CPU高速缓存效率,可以优化代码的布局、调整数据访问方式、避免冲突等措施。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

byg_qlh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值