当前位置: 首页 > article >正文

*C++: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 - C++ Reference

1.list的构造

2.list迭代器

将迭代器理解成一个指针,该指针指向 list 中的某个节点
list是包含一个哨兵位的
注意:
1. begin end 为正向迭代器,对迭代器执行 ++操作,迭代器向后移动
2. rbegin(end) rend(begin) 为反向迭代器,对迭代器执行 ++操作,迭代器向前移动
list迭代器失效问题

迭代器可以暂时理解成类似于指针,迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响

int main()
{
	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 = l.erase(it);
	}
	return 0;
}

这里l.erase(it++)要理解这个写法,相当于是先锁定了it的位置,这样erase后直接在这个位置上++就行;与l.erase(it);++it;不同,因为编译器里是一行一行执行的,在同一行内,it还不会失效,如果我们在进入下一行之前就给他重新找好位置的话就不会有问题。

3.list capacity

4.list元素访问

5.list元素修改

6.构造函数,插入,删除

int main()
{
	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);

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	// 第5个位置插入数据

	//v.insert(v.begin()+5, 10);
	//list是一个双向链表,不是连续的,begin()+5不是第5个位置

	//不用挪动数据
	auto it = lt.begin();
	for (size_t i = 0; i < 5; i++)
	{
		++it;   //这个++是个运算符重载,不是平时所谓的+1
	}
	lt.insert(it, 100);

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	//list没有find,这个find是std里的
	it = find(lt.begin(), lt.end(), 3);    
	if (it != lt.end())
	{
		lt.insert(it, 30);

		// insert以后,it不失效
		*it *= 100;
	}

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	it = find(lt.begin(), lt.end(), 2);
	if (it != lt.end())
	{
		lt.erase(it);

		//erase以后,it失效了
		// *it *= 100;
	}

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	it = lt.begin();
	while (it != lt.end())
	{
		if (*it % 2 == 0)
		{
			it = lt.erase(it);   //如果持续删除,需要先用迭代器it保存,erase是有一个返回值的
		}
		else
		{
			++it;
		}
	}

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

    return 0;
}

7.reverse,sort

int main()
{
	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);

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	reverse(lt.begin(), lt.end());
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	lt.reverse();
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	//sort是RandomAccessIterator随机迭代器,但是list是双向迭代器,不适用
	//sort(lt.begin(), lt.end());

	lt.sort();
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

    return 0;
}
可以看到,reverse可以使用std的函数,但是sort不可以,因为list是双向迭代器,std::sort不适用。

8.remove splice

int main()
{
	int myints[] = { 17,89,7,14 };
	std::list<int> mylist(myints, myints + 4);

	mylist.remove(89);

	for (auto e : mylist)
	{
		cout << e << " ";
	}
	cout << endl;
}

可以看到,remove可以删除指定val值的位置元素,与erase不同,erase删除的是指定位置的值。

int main()
{
	list<int> mylist1, mylist2;
	list<int>::iterator it;

	// set some initial values:
	for (int i = 1; i <= 4; ++i)
		mylist1.push_back(i);      // mylist1: 1 2 3 4

	for (int i = 1; i <= 3; ++i)
		mylist2.push_back(i * 10);   // mylist2: 10 20 30

	for (auto e : mylist1)
	{
		cout << e << " ";
	}
	cout << endl;

	for (auto e : mylist2)
	{
		cout << e << " ";
	}
	cout << endl << endl;

	it = mylist1.begin();
	++it;                         // points to 2

	// 全部转移
	mylist1.splice(it, mylist2);  //把mylist2里的东西,从it位置开始,转移到mylist1里

	// 部分转移
	//mylist1.splice(it, mylist2, ++mylist2.begin());    //把mylist2里++mylist2.begin()里的东西,从it位置开始,转移到mylist1里
	//mylist1.splice(it, mylist2, ++mylist2.begin(), mylist2.end());  //把mylist2里++mylist2.begin()到mylist2.end()里的东西,从it位置开始,转移到mylist1里

	//mylist1.splice(mylist1.begin(), mylist1, ++mylist1.begin(), mylist1.end());   //支持把自己的数据转移给自己

	cout << "mylist1: ";
	for (auto e : mylist1)
	{
		cout << e << " ";
	}
	cout << endl;

	cout << "mylist2: ";
	for (auto e : mylist2)
	{
		cout << e << " ";
	}
	cout << endl;

}

三.list和vector对比

void test_op()
{
	srand(time(0));
	const int N = 1000000;
	vector<int> v;
	v.reserve(N);
	list<int> lt1;
	list<int> lt2;

	for (int i = 0; i < N; ++i)
	{
		auto e = rand();
		lt2.push_back(e);
		lt1.push_back(e);
	}

	// 拷贝到vector排序,排完以后再拷贝回来
	int begin1 = clock();
	// 先拷贝到vector
	for (auto e : lt1)
	{
		v.push_back(e);
	}

	// 排序
	sort(v.begin(), v.end());

	// 拷贝回去
	size_t i = 0;
	for (auto& e : lt1)
	{
		e = v[i++];
	}

	int end1 = clock();

	int begin2 = clock();
	//list的sort效率太慢
	//相对便捷,数据量小的时候可以用
	lt2.sort();
	int end2 = clock();

	printf("vector sort:%d\n", end1 - begin1);
	printf("list sort:%d\n", end2 - begin2);
}

int main()
{
    test_op();
}

可以看到,即使是多进行了两次人为拷贝,vector效率依旧比list高,可以证明,在处理大量数据的时候,vector效率明显优于list,list相对便捷,在处理少量数据的时候可以使用。

四.list类模拟实现

课件代码/C++课件V6/List的模拟实现/List.h · will/C++上课 - Gitee.com

五.list反向迭代器


http://www.kler.cn/news/324007.html

相关文章:

  • 【C语言零基础入门篇 - 17】:排序算法
  • ubuntu系统下,c++图形库Matplot++配置
  • 深度学习(3):Tensor和Optimizer
  • 求职Leetcode题目(11)
  • 如何使用C语言接入Doris数据库
  • 线性表二——栈stack
  • 微信小程序开发系列之-在微信小程序中使用云开发
  • How to install JetBrains ToolBox in Ubuntu 22.04 LTS?
  • ELK-03-skywalking监控linux系统
  • JAVA JDK华为云镜像下载,速度很快
  • AIGC入门:Comfyui整合包,解压即用!
  • Goweb---Gorm操作数据库(二)
  • project_object_model_3d
  • ES6中迭代器与生成器知识浅析
  • Python知识点:如何使用Python与.NET进行互操作(IronPython)
  • ubuntu 安装harbor
  • 解锁MySQL高可用新境界:深入探索MHA架构的无限魅力与实战部署
  • HI3520DV510 22AP80/SS522V100 芯片及开发板
  • 认识 Linux操作系统
  • 新疆交投路桥桥梁公司:向“新”求“质”,积蓄发展新势能
  • Tkinter制作登录界面以及登陆后页面切换(一)
  • Linux 基本指令的学习
  • 【深度学习】03-神经网络 3-3 梯度下降的优化方法-动量算法Momentum
  • mysql数据库sql语句总结
  • 综合业务区的数字化创新与智能化蓝图
  • GitLab CI/CD脚本入门
  • 04_OpenCV图片缩放
  • 先进制造aps专题二十六 基于强化学习的人工智能ai生产排程aps模型简介
  • Oracle 数据库安装和配置指南(新)
  • 进阶SpringBoot之分布式系统与 RPC 原理