【C++】list

list

  • 1. 简单了解list
  • 2. list的常见接口
  • 3. 简单实现list
  • 4. vector和list比较


1. 简单了解list

  1. list的底层是带头双向循环列表。
  2. 因此list支持任意位置的插入和删除,且效率较高。但其缺陷也很明显,由于各节点在物理空间是不连续的,所以不支持对任意位置的访问,效率低。
  3. list的迭代器底层不仅仅是指针这么简单,因为其迭代器支持前后双向迭代,而其空间又不连续,所以其底层是对指针的封装。(后面讲)

2. list的常见接口

  1. 构造

在这里插入图片描述
例子
在这里插入图片描述

  1. 普通迭代器

在这里插入图片描述
与其他容器的迭代器一样,只不过list的底层实现更加复杂,现在暂且将其看成指针。
例子

//迭代器
void test3()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	auto it = lt.begin();
	while (it != lt.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}
//运行结果:
//1 2 3 4
  1. 头插头删和尾插尾删

例子

void test2()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;
	lt.pop_back();
	lt.pop_back();
	lt.pop_front();
	lt.pop_front();
	lt.push_front(4);
	lt.push_front(3);
	lt.push_front(2);
	lt.push_front(1);
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;
}
//运行结果:
//1 2 3 4
//1 2 3 4
  1. 插入

在这里插入图片描述
例子

void test3()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(3);
	lt.push_back(4);
	//想在第二个位置插入2,怎么做
	//lt.insert(lt.begin()+1,2);//错误的,list的iterator没有重载+。这个等下讲原因。
	auto it = lt.begin();
	++it;
	lt.insert(it, 2);
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;
}
//运行结果:
//1 2 3 4

这样确定插入位置太麻烦了,可以用find查找。

auto it = find(lt.begin(),lt.end(),3);
if (it != lt.end())
{
	lt.insert(it,2);
}
  1. 删除

在这里插入图片描述
例子

	//删除偶数
	//删除后,迭代器还指向被删除空间,存在迭代器失效问题
	//所以要重新赋值
	it = lt.begin();
	while (it != lt.end())
	{
		if (*it % 2 == 0)
		{
			it = lt.erase(it);
		}
		else
		{
			++it;
		}
	}
  1. front和back

在这里插入图片描述
例子
在这里插入图片描述

  1. 逆置和排序

在这里插入图片描述
在这里插入图片描述
例子

void test5()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.reverse();
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;
	lt.sort();
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;
}
//运行结果
//4 3 2 1
//1 2 3 4

拓展
(1)库里也有sort,为什么还要在list再写一个?C++库的sort不能用。
(2)这涉及到迭代器的分类。从功能上,由容器底层结构决定,迭代器有单项迭代器、双向迭代器和随机迭代器。单项迭代器只能++,向后迭代,例如forward_list/unordered_xxx的迭代器;双向迭代器能++/–,例如list/map/set;随机迭代器能+/-/++/–,例如string/vector/deque。
(3)有随机迭代器的容器能用随机的、双向的、单向的迭代器的库函数,有双向迭代器的容器能用双向的、单向的迭代器的库函数。
(4)list的迭代器类型是双向的,库函数sort的迭代器类型是随机的,所以list的数据排序不能用库函数sort。
在这里插入图片描述
在这里插入图片描述

  1. 归并

在这里插入图片描述
例子

void test6()
{
	list<int> lt1;
	lt1.push_back(1);
	lt1.push_back(3);
	lt1.push_back(5);
	lt1.push_back(7);
	list<int>lt2;
	lt2.push_back(2);
	lt2.push_back(4);
	lt2.push_back(6);
	lt2.push_back(8);
	lt1.merge(lt2);
	for (auto e : lt1)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << "lt1的大小为:" << lt1.size() << endl;
	cout << "lt2是否变为空:" << lt2.empty() << endl;
}

在这里插入图片描述

  1. unique

在这里插入图片描述
例子

	list<int> lt1;
	lt1.push_back(1);
	lt1.push_back(1);
	lt1.push_back(2);
	lt1.push_back(2);
	lt1.unique();
	cout << "lt1的大小:" << lt1.size() << endl;
	for (auto e : lt1)
	{
		cout << e << " ";
	}
	cout << endl;

在这里插入图片描述

  1. remove

在这里插入图片描述

	list<int> lt1;
	lt1.push_back(1);
	lt1.push_back(1);
	lt1.push_back(2);
	lt1.push_back(2);
	lt1.remove(1);//相当于find+erase
	cout << "lt1的大小:" << lt1.size() << endl;
	for (auto e : lt1)
	{
		cout << e << " ";
	}
	cout << endl;

在这里插入图片描述
remove不像erase,它的参数是值而不是迭代器,且remove如果要移除的值不存在,不会报错。

  1. splice

在这里插入图片描述

	list<int> lt1;
	lt1.push_back(1);
	lt1.push_back(2);
	lt1.push_back(3);
	lt1.push_back(4);
	
	list<int> lt2;
	lt2.push_back(5);
	lt2.push_back(6);
	lt2.push_back(7);
	lt2.push_back(8);

	lt1.splice(lt1.begin(), lt2);//将lt2的所有节点全部转移到lt1之前
	lt1.splice(lt1.begin(), lt2,--lt2.end());//将lt2的最后一个元素转移到lt1之前
	lt1.splice(lt1.begin(), lt2, ++lt2.begin(), lt2.end());//将迭代器区间的节点转移到lt1之前
	lt1.splice(lt1.begin(), lt1, ++lt1.begin(), lt1.end());//将lt1第一个节点后面的节点转移到lt1之前
	for (auto e : lt1)
	{
		cout << e << " ";
	}
	cout << endl;

3. 简单实现list

#include<iostream>
using namespace std;

namespace zn
{
	//节点
	template<class T>
	struct ListNode
	{
		ListNode<T>* _prev;
		ListNode<T>* _next;
		T _val;

		ListNode(const T& val = T())
			:_prev(nullptr)
			, _next(nullptr)
			, _val(val)
		{}
	};

	//迭代器(双向迭代器)
	//迭代器底层都是指针,但有些指针需要封装,例如节点的指针,不然不能满足++/--等操作
	template<class T, class Ref, class Ptr>
	//T == T, Ref == T&/const T&, Ptr == T*/const T*
	struct __list_iterator
	{
		typedef ListNode<T>* iterator;
		typedef __list_iterator<T, Ref, Ptr> This;
		iterator _node;

		//构造
		__list_iterator(iterator node)
			:_node(node)
		{}

		//*重载
		Ref operator*()
		{
			return _node->_val;
		}

		//->重载
		//如果T恰好是结构体类型,而迭代器又被看成指针,那么->就可以派上用场
		Ptr operator->()
		{
			return &(_node->_val);
		}

		//++重载
		This& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		This operator++(int)
		{
			This tmp(_node);
			_node = _node->_next;
			return tmp;
		}

		//--重载
		This& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		This operator--(int)
		{
			This tmp(_node);
			_node = _node->_prev;
			return tmp;
		}

		//==重载和!=重载
		bool operator==(__list_iterator lit)const
		{
			return _node == lit._node;
		}
		bool operator!=(__list_iterator lit)const
		{
			return !(_node == lit._node);
		}

	};
	
	//反向迭代器
	//对正向迭代器的封装,从而得到我们想要的操作
	template<class T,class Ref,class Ptr>
	class ReverseIterator
	{
		typedef __list_iterator<T, Ref, Ptr> iterator;
		typedef	ReverseIterator<T, Ref, Ptr> reverse_iterator;
		iterator _it;

		ReverseIterator(iterator it)
			:_it(it)
		{}

	public:
		Ref operator*()
		{
			iterator tmp = _it;
			return *(--tmp);
		}
		Ptr operator->()
		{
			return &(operator*());
		}
		reverse_iterator& operator++()
		{
			--_it;
			return *this;
		}
		reverse_iterator operator++(int)
		{
			reverse_iterator tmp = *this;
			--_it;
			return tmp;
		}
		reverse_iterator& operator--()
		{
			++_it;
			return *this;
		}
		reverse_iterator operator--(int)
		{
			reverse_iterator tmp = *this;
			++_it;
			return tmp;
		}
		bool operator!=(const reverse_iterator& rit)
		{
			return _it != rit._it;
		}
		bool operator==(const reverse_iterator& rit)
		{
			return _it == rit._it;
		}
	};

	//list
	template<class T>
	class list
	{
		typedef ListNode<T> Node;
		typedef __list_iterator<T, T&, T*> iterator;
		typedef __list_iterator<T, const T&, const T*> const_iterator;
		typedef ReverseIterator<T, T&, T*> reverse_iterator;
		typedef ReverseIterator<T, const T&, const T*> const_reverse_iterator;
	public:
		//构造
		list()
		{
			_head = new Node;
			_head->_prev = _head;
			_head->_next = _head;
			_size = 0;
		}

		//拷贝构造
		list(const list<T>& lt)//list(const list& lt)
		{
			_head = new Node;
			_head->_prev = _head;
			_head->_next = _head;
			_size = 0;

			for (auto& e : lt)
			{
				push_back(e);
			}
			_size = lt._size;
		}
		
		//交换
		void swap(list<T>& lt)
		{
			std::swap(_head, lt._head);
			std::swap(_size, lt._size);
		}

		//=重载
		list<T> operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}

		//析构
		~list()
		{
			Node* cur = _head->_prev;
			while (cur != _head)
			{
				Node* tmp = cur->_next;
				delete cur;
				cur = tmp;
			}
			delete _head;
			_head = nullptr;
		}

		//迭代器
		iterator begin()
		{
			return _head->_next;
		}
		const_iterator begin()const
		{
			return _head->_next;
		}
		iterator end()
		{
			return _head;
		}
		const_iterator end()const
		{
			return _head;
		}
		reverse_iterator rbegin()
		{
			return reverse_iterator(end());
		}
		const_reverse_iterator rbegin()const
		{
			return const_reverse_iterator(end());
		}
		reverse_iterator rend()
		{
			return reverse_iterator(begin());
		}
		const_reverse_iterator rend()const
		{
			return const_reverse_iterator(begin());
		}
		
		//插入(默认在pos前插入)
		iterator insert(iterator pos, const T& val)
		{
			Node* newnode = new Node(val);
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			prev->_next = newnode;
			newnode->_prev = prev;
			cur->_prev = newnode;
			newnode->_next = cur;
			++_size;
			//隐式类型转换
			//返回指针类型,然后用类类型接收,会调用构造
			return newnode;
		}

		//删除
		iterator earse(iterator pos)
		{
			//头节点不能删除,否则析构时会报错
			assert(pos != end());
			Node* cur = pos->_node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;
			prev->_next = next;
			next->_prev = prev;
			delete cur;
			--_size;
			return next;
		}

		//尾插和尾删
		void push_back(const T& val)
		{
			insert(end(), val);
		}
		void pop_back()
		{
			erase(--end());
		}
		
		//头插和头删
		void push_front(const T& val)
		{
			insert(begin(), val);
		}
		void pop_front()
		{
			erase(begin());
		}

		//大小
		size_t size()
		{
			return _size;
		}

		//清理
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
			_size = 0;
		}
	private:
		//指向头节点
		Node* _head;
		size_t _size;
	};

	void test_iterator()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		auto it = lt.begin();
		while (it != lt.end())
		{
			cout << *it << " ";
			it++;
		}
		cout << endl;
	}
}

4. vector和list比较

vectorlist
底层顺序表,可扩容双向循环链表,带头节点
效率具有连续的空间,空间利用率高;访问元素效率高,支持随机访问;插入和删除元素效率低,需要挪动元素,时间复杂度为O(N)底层开辟节点,空间利用率低;访问元素效率低,不支持随机访问; 插入和删除元素效率高,时间复杂度为O(1)
迭代器是原生态指针;是随机迭代器,支持+/-等操作;无论是插入还是删除,都会导致迭代器失效(插入需要扩容,扩容后原来的迭代器失效)是对原生态指针的封装;是双向迭代器,不支持+/-等操作 ;删除会导致迭代器失效
应用适用于插入和删除操作少,访问多的情况适用于插入和删除操作多,访问少的情况

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/90734.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

thinkphp安装workman

需要加版本&#xff0c;版本太高了不行 composer require topthink/think-worker1.0.*

Github的使用指南

首次创建仓库 1.官网创建仓库 打开giuhub官网&#xff0c;右上角点击你的头像&#xff0c;随后点击your repositories 点击New开始创建仓库 如下图为创建仓库的选项解释 出现如下界面就可以进行后续的git指令操作了 2.git上传项目 进入需上传项目的所在目录&#xff0c;打开…

keepalived+haproxy 搭建高可用高负载高性能rabbitmq集群

一、环境准备 1. 我这里准备了三台centos7 虚拟机 主机名主机地址软件node-01192.168.157.133rabbitmq、erlang、haproxy、keepalivednode-02192.168.157.134rabbitmq、erlang、haproxy、keepalivednode-03192.168.157.135rabbitmq、erlang 2. 关闭三台机器的防火墙 # 关闭…

自动控制原理笔记-采样控制系统

目录 采样控制系统的基本概念&#xff1a; 采样过程及采样定理&#xff1a; 一、采样过程 二、采样定理&#xff08;香农采样定理、奈奎斯特采样定律&#xff09; 三、信号复现 四、零阶保持器 z变换与z反变换&#xff1a; z变换的定义 z变换基本定理 z反变换 采样系…

gorm中正确的使用json数据类型

一、说明 1、JSON 数据类型是 MySQL 5.7.8 开始支持的。在此之前&#xff0c;只能通过字符类型&#xff08;CHAR&#xff0c;VARCHAR 或 TEXT &#xff09;来保存 JSON 文档。现实中也很多人不会采用json的存储方式&#xff0c;直接定义一个字符类型,让前端转换传递进来,返回给…

HTTPS协议加密原理

目录 一、什么是HTTPS 二、什么是加密/解密 三、为什么要加密 四、常见的加密方式 1.对称加密 2. 非对称加密 五、HTTPS加密方式探讨 1.只使用对称加密 2.只使用非对称加密 3.非对称加密对称加密 4.非对称加密对称加密CA认证 六、总结 一、什么是HTTPS HTTP 协议&a…

vue3+element下拉多选框组件

<!-- 下拉多选 --> <template><div class"select-checked"><el-select v-model"selected" :class"{ all: optionsAll, hidden: selectedOptions.data.length < 2 }" multipleplaceholder"请选择" :popper-app…

git及GitHub的使用

文章目录 git在本地仓库的使用github使用创建仓库https协议连接(不推荐&#xff0c;现在用起来比较麻烦)ssh连接&#xff08;推荐&#xff09;git分支操作冲突处理忽略文件 git在本地仓库的使用 1.在目标目录下右键打开git bash here 2.创建用户名和邮箱(注&#xff1a; 下载完…

ms-tpm-20-ref 在linux下编译

1、代码地址&#xff0c; GitHub - microsoft/ms-tpm-20-ref: Reference implementation of the TCG Trusted Platform Module 2.0 specification.Reference implementation of the TCG Trusted Platform Module 2.0 specification. - GitHub - microsoft/ms-tpm-20-ref: Refe…

Nodejs-nrm:快速切换npm源 / npm官方源和其他自定义源之间切换

一、理解 Nodejs nrm Nodejs nrm 是一个管理 npm 源的工具。由于 npm 在国内的速度较慢&#xff0c;很多开发者会使用淘宝的 npm 镜像源&#xff0c;但是也会遇到一些问题&#xff0c;例如某些包在淘宝镜像源中不存在&#xff0c;或者淘宝镜像源本身也会有问题。 Nodejs nrm …

CSS scoped 属性的原理

scoped 一、scoped 是什么&#xff1f;二、实现原理 一、scoped 是什么&#xff1f; 在 Vue 组件中&#xff0c;为了使样式私有化&#xff08;模块化&#xff09;&#xff0c;不对全局造成污染&#xff0c;可以在 style 标签上添加 scoped 属性以表示它的只属于当下的模块&am…

【MOS管的作用和工作原理】

数电/模电知识学习与分享001 MOS管的作用和工作原理1、MOS管基本概念2、MOS管基本原理3、MOS管广泛作用4、MOS管特点4、参考文献 MOS管的作用和工作原理 1、MOS管基本概念 MOS管&#xff08;Metal-Oxide-Semiconductor Field-Effect Transistor&#xff09;是一种常用的半导体…

Unity 之利用 localEulerAngle与EulerAngle 控制物体旋转

文章目录 概念讲解localEulerAngle与EulerAngle的区别 概念讲解 欧拉角&#xff08;Euler Angles&#xff09;是一种常用于描述物体在三维空间中旋转的方法。它使用三个角度来表示旋转&#xff0c;分别绕物体的三个坐标轴&#xff08;通常是X、Y和Z轴&#xff09;进行旋转。这…

AI Agent在情景猜谜场景下的AgentBench基准测试

目录 AgentBench评估哪些场景? 近日,来自清华大学、俄亥俄州立大学和加州大学伯克利分校的研究者设计了一个测试工具——AgentBench,用于评估LLM在多维度开放式生成环境中的推理能力和决策能力。研究者对25个LLM进行了全面评估,包括基于API的商业模型和开源模型。 他们发现…

安卓移动应用开发实训室建设方案

一 、系统概述 安卓移动应用开发作为新一代信息技术的重点和促进信息消费的核心产业&#xff0c;已成为我国转变信息服务业的发展新热点&#xff1a;成为信息通信领域发展最快、市场潜力最大的业务领域。互联网尤其是移动互联网&#xff0c;以其巨大的信息交换能力和快速渗透能…

es的索引管理

概念 &#xff08;1&#xff09;集群&#xff08;Cluster&#xff09;&#xff1a; ES可以作为一个独立的单个搜索服务器。不过&#xff0c;为了处理大型数据集&#xff0c;实现容错和高可用性&#xff0c;ES可以运行在许多互相合作的服务器上。这些服务器的集合称为集群。 &…

docker项目实战

目录 1、使用mysql:5.6和 owncloud 镜像&#xff0c;构建一个个人网盘。 1&#xff09;拉取mysql:5.6和owncloud镜像 2&#xff09;后台运行容器 3&#xff09;通过ip:端口的方式访问owncloud 2、安装搭建私有仓库 Harbor 1&#xff09;首先准备所需包 2&#xff09;安装h…

JavaScript——为什么静态方法不能调用非静态方法

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

RabbitMQ---订阅模型-Fanout

1、 订阅模型-Fanout Fanout&#xff0c;也称为广播。 流程图&#xff1a; 在广播模式下&#xff0c;消息发送流程是这样的&#xff1a; 1&#xff09; 可以有多个消费者 2&#xff09; 每个消费者有自己的queue&#xff08;队列&#xff09; 3&#xff09; 每个队列都要绑定…

C++:命名空间,缺省参数,函数重载,引用,内联函数

个人主页 &#xff1a; 个人主页 个人专栏 &#xff1a; 《数据结构》 《C语言》《C》 文章目录 前言一、命名空间命名空间的定义命名空间的使用 二、缺省参数缺省参数概念缺省参数分类 三、函数重载函数重载的概念 四、引用引用的概念引用特性引用的使用场景引用与指针的区别 …