【STL容器】详解list的使用和模拟实现

🍎 博客主页:🌙@披星戴月的贾维斯
🍎 欢迎关注:👍点赞🍃收藏🔥留言
🍇系列专栏:🌙 STL函数专栏
🌙请不要相信胜利就像山坡上的蒲公英一样唾手可得,但是请相信,世界上总有一些美好值得我们全力以赴,哪怕粉身碎骨!🌙
🍉一起加油,去追寻、去成为更好的自己!

🍎1、list的介绍

1.1.文档中的定义
在这里插入图片描述
中文意思是列表是序列容器,允许在序列中的任何位置进行恒定时间O(1)的插入和删除操作,以及双向迭代。

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

1.2.list的使用
  list中的接口比较多,此处类似,只需要掌握如何正确的使用,然后再去深入研究背后的原理,已达到可扩展的能力。以下为list中一些常见的重要接口。

以下为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

构造List的代码示例

int main ()
{
    list<int> l1; //构造空的list,并且数据类型为int
    list<int> l2(10, 20); //第一个10是数量,20是值, 相当于是构造时有10个元素,每个元素是20
    list<int> l3(l2);//用另一个list对象拷贝构造l3
    list<int> l4(l3.begin(), l3.end());//用l3的[begin(), end())左闭右开的区间构造l4
    // 以数组为迭代器区间构造l5
    int array[] = { 16,2,77,29 };
    list<int> l5(array, array + sizeof(array) / sizeof(int));

    // 列表格式初始化C++11
    list<int> l6{ 1,2,3,4,5 };

    return 0;
}

1.2.2 list iterator的使用
此处,大家可暂时将迭代器理解成一个指针,该指针指向list中的某个节点。

iterator的使用接口说明
begin + end(重点)返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
rbegin + rend返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的reverse_iterator,即begin位置

【注意】
1. begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
2. rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动


1.2.3 list capacity

函数声明接口说明
empty检测list是否为空,是返回true,否则返回false
size返回list中有效节点的个数

1.2.4 list element access

函数声明接口说明
front返回list的第一个节点中值的引用
back返回list的最后一个节点中值的引用

1.2.5 list modifiers

函数声明接口说明
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中的有效元素

sort是随机访问迭代器
在这里插入图片描述

链表sort的用法:不需要传入链表的首和尾

L.sort();

STL中各种容器的迭代器分类
在这里插入图片描述
1.2.6 list的迭代器失效
前面说过,此处大家可将迭代器暂时理解成类似于指针,迭代器失效即迭代器所指向的节点的无效,即该节
点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代
器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。

void TestListIterator1()
{
	 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;
	 }
}
// 改正
void TestListIterator()
{
	 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())
	 {
		 l.erase(it++); // it = l.erase(it);
	 }
}

🍎2、list的模拟实现

分析list的迭代器
在这里插入图片描述

  • 数组有原生指针,可以当迭代器
  • 但是链表没有,链表是随机的物理地址,原生指针不行,所以要封装一个迭代器,通过自己的努力,重载运算符
  • list的++和 *解引用都是函数调用,不是自带的。vector的iterator 是原生指针,而list的iterator是自定义类型

list源代码里面的迭代器是有三个参数
在这里插入图片描述
实现一个const迭代器,所以要传三个模板参数

template<class T, class Ref, class Ptr>
typedef _list_iterator<T, T&, T*> iterator;
typedef _list_iterator<T, const T&, const T*> const_iterator;

再实现一个const begin()和 const end()

  const_iterator begin() const
        {
            //return _head->_next;
            return iterator(_head->_next);//第一个节点的指针就是head->_next;
        }
        const_iterator end() const
        {
            return iterator(_head);//end就是哨兵位的头结点
        }

完善list的插入和删除
插入的逻辑
在这里插入图片描述
提前把前一个的结点找到
在这里插入图片描述

iterator insert(iterator pos, const T& x)
        {
            Node* newNode = new Node(x);//新生成一个空间并且初始化成x
            Node* cur = pos._node;//取迭代器的结点
            //迭代器的实现是对上层透明的
            Node* prev = cur->_prev;//prev是前一个结点的指针
            //prev    newnode   cur
            prev->_next = newNode;
            newNode->_prev = prev;
            newNode->_next = cur;
            cur->_prev = newNode;

            return iterator(newNode);
        }

erase的逻辑

iterator erase(iterator pos)
        {
            assert(pos != end());//不能把哨兵位的头结点删掉
            Node* cur = pos._node;//取到pos位的结点
            Node* prev = cur->_prev;
            Node* next = cur->_next;
            //prev   next
            prev->_next = next;
            next->_prev = prev;
            delete cur;
            return iterator(next);
        }

🍎LIst的模拟实现

#pragma once

#include<assert.h>
#include"Reit.h"
namespace jiang
{
	template<class T>
	struct list_node
	{
		list_node<T>* _next;
		list_node<T>* _prev;
		T _data;

		list_node(const T& val = T())
			:_next(nullptr)
			,_prev(nullptr)
			,_data(val)
		{}
	};
	//T是类型,比如是int, Ref是T&, Ptr是T*
	template<class T, class Ref, class Ptr>
	struct _list_iterator
	{
		typedef list_node<T> Node;
		typedef _list_iterator<T, Ref, Ptr> self;
		Node* _node;
		_list_iterator(Node* node)
			:_node(node)
		{}
		//析构函数	-- 节点不属于迭代器,不需要迭代器释放
		//拷贝构造和赋值重载 -- 默认生成的浅拷贝就可以
		Ref operator*()
		{
			return _node->_data;
		}
		Ptr operator->()
		{
			return &_node->_data;
		}
		self& operator++()//迭代器++返回的还是迭代器
		{
			_node = _node->_next;
			return *this;
		}
		self& operator--()//迭代器++返回的还是迭代器
		{
			_node = _node->_prev;
			return *this;
		}
		self operator++(int)//后置++
		{
			self tmp(*this);
			_node = _node->_next;
			return tmp;
		}
		self operator--(int)//后置++
		{
			self tmp(*this);
			_node = _node->_prev;
			return tmp;
		}
		bool operator != (const self& it)
		{
			return _node != it._node;
		}
		bool operator==(const self& it)
		{
			return _node == it._node;
		}
	};

	template<class T>
	class list
	{
		typedef list_node<T> Node;
	public:
		typedef _list_iterator<T, T&, T*> iterator;
		typedef _list_iterator<T, const T&, const T*> const_iterator;
		//结点的指针支持隐式类型的转换,转换成迭代器所以写成_head->_next也是可以的
		iterator begin()
		{
			//return _head->_next;
			return iterator(_head->_next);//第一个节点的指针就是head->_next;
		}
		iterator end()
		{
			return iterator(_head);//end就是哨兵位的头结点
		}
		const_iterator begin() const
		{
			//return _head->_next;
			return  const_iterator(_head->_next);
			//第一个节点的指针就是head->_next;
		}
		const_iterator end() const
		{
			return const_iterator(_head);//end就是哨兵位的头结点
		}
		list()
		{
			_head = new Node();
			_head->_next = _head;
			_head->_prev = _head;
		}
		void empty_init()
		{
			_head = new Node();
			_head->_next = _head;
			_head->_prev = _head;
		}
		template <class InputIterator>
		list(InputIterator first, InputIterator last)
		{
			empty_init();

			while (first != last) 
			{
				push_back(*first);
				++first;
			}
		}
		// 17:00 继续
		void swap(list<T>& lt)
		{
			std::swap(_head, lt._head);//交换头指针
		}
		//传统写法,实现深拷贝
		//list(const list<T>& lt)
		//{
		//	_head = new Node();
		//	_head->_next = _head;
		//	_head->_prev = _head;//初始化一个头结点
		//	for (auto e : it)
		//	{
		//		push_back(e);
		//	}
		//}
		// lt2(lt1) -- 现代写法拷贝构造
		list(const list<T>& lt)
		{
			empty_init();
			list<T> tmp(lt.begin(), lt.end());
			swap(tmp);
		}

		// lt2 = lt1
		list<T>& operator=(list<T> lt)//为什么要传&返回,减少拷贝构造
		{
			swap(lt);
			return *this;
		}

		~list()
		{
			clear();
			delete _head;//delete掉头结点
			_head = nullptr;//头结点置空
		}
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}
		void push_back(const T& x)
		{
			//Node* tail = _head->_prev;//找尾,so简单
			//Node* newnode = new Node(x);

			_head      tail    newnode
			//newnode->_next = _head;//先右后左
			//_head->_prev = newnode;
			//tail->_next = newnode;
			//newnode->_prev = tail;
			insert(end(), x);
		}

		void push_front(const T& x)
		{
			insert(begin(), x);
		}
		void pop_back()
		{
			erase(--end());
		}
		void pop_front()
		{
			erase(begin());
		}
		//插入在pos位置的前一个
		iterator insert(iterator pos, const T& x)
		{
			Node* newNode = new Node(x);//新生成一个空间并且初始化成x
			Node* cur = pos._node;//取迭代器的结点
			//迭代器的实现是对上层透明的
			Node* prev = cur->_prev;//prev是前一个结点的指针
			//prev    newnode   cur
			prev->_next = newNode;
			newNode->_prev = prev;
			newNode->_next = cur;
			cur->_prev = newNode;

			return iterator(newNode);
		}
		iterator erase(iterator pos)
		{
			assert(pos != end());//不能把哨兵位的头结点删掉
			Node* cur = pos._node;//取到pos位的结点
			Node* prev = cur->_prev;
			Node* next = cur->_next;
			//prev   next
			prev->_next = next;
			next->_prev = prev;
			delete cur;
			return iterator(next);
		}
	private:
		Node* _head;
	};

🍎总结

    这次和大家分享了基本的list的用法以及基础的模拟实现,希望对大家快速上手list有帮助。

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

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

相关文章

Axure--中继器(增删改查)

&#x1f4da;&#x1f4da; &#x1f3c5;我是bing人&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;在这里&#xff0c;我要推荐给大家我的专栏《Axure》。&#x1f3af;&#x1f3af; &#x1f680;无论你是编程小白&#xff0c;还是有一…

龙迅LT86102UXE HDMI一分二HDMI,支持音频剥离,支持4K60HZ

描述&#xff1a; 龙迅 LT86102UXE HDMI2.0 分路器具有符合 HDMI2.0/1.4 规范的 1&#xff1a;2 分路器、最大 6Gbps 高速数据速率、自适应均衡 RX 输入和预加重 TX 输出&#xff08;用于支持长电缆应用&#xff09;、内部 TX 通道交换以实现灵活的 PCB 布线。 LT86102UXE HDM…

Python importlib模块详细教程

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com importlib模块是Python标准库中用于动态导入模块的工具。它提供了一系列函数&#xff0c;允许以编程方式加载、检查和操作模块。本文将深入探讨importlib的各种用法&#xff0c;并通过丰富的示例代码帮助你更好地…

Python 时间日期处理库函数

标准库 datetime >>> import datetime >>> date datetime.date(2023, 12, 20) >>> print(date) 2023-12-20 >>> date datetime.datetime(2023, 12, 20) >>> print(date) 2023-12-20 00:00:00 >>> print(date.strfti…

fv悬浮球恢复备份配置主界面闪退问题解决方法

错误环境&#xff1a; 闪退版本120.0.6099.43 正常版本104.0.5112.97 当fv悬浮球恢复过往的备份配置后打开出现主界面闪退&#xff0c;但是其他功能仍然一切正常&#xff0c;例如应用启动器&#xff0c;分享保存 问题原因&#xff1a;因为安卓系统以往的Android System Web…

Iview Tooltip显示不换行,被遮挡

部分使用slot 方式无法解决 <Tooltip placement"top"> <Button>多行</Button> <div slot"content"> <p>显示多行信息</p> <p><i>可以自定义样式</i></p> </div> </Tooltip> 所以…

完整的 nuxt3 + vue + ts 服务端渲染项目搭建教程,克隆就能用,新手必学,建议收藏

目录 前言 一、新建仓库 1.1 新建 gitee 仓库 1.2 克隆到本地 二、初始化 nuxt 项目 2.1 初始化 nuxt 2.1.1 使用什么包管理工具 2.1.2 是否初始化 git仓库 2.1.3 整理项目结构 2.1.4 提交代码 2.2 运行项目 2.2.1 运行 npm run dev 2.2.2 增加 .nvmrc 文件 2.2.…

孩子还是有一颗网安梦——Bandit通关教程:Level 16 → Level 17

&#x1f575;️‍♂️ 专栏《解密游戏-Bandit》 &#x1f310; 游戏官网&#xff1a; Bandit游戏 &#x1f3ae; 游戏简介&#xff1a; Bandit游戏专为网络安全初学者设计&#xff0c;通过一系列级别挑战玩家&#xff0c;从Level0开始&#xff0c;逐步学习基础命令行和安全概念…

对大学生创新创业某赛事目前存在的烂尾楼现象的一些研究的分享(1)

经过对”某某网”大学生创新创业大赛国赛第五届-第八届部分金奖项目的研究&#xff0c;进行较为充分的信息溯源、穿透调查&#xff0c;我发现不少项目存在赛事材料画大饼&#xff0c;严重不切合实际&#xff0c;参赛人员并非真正创新创业&#xff0c;赛后迅速销声匿迹、烂尾切割…

MFC 消息映射机制

目录 消息映射机制概述 宏展开 宏展开的作用 消息映射机制的执行流程 消息处理 消息映射机制概述 MFC的消息映射映射机制是可以在不重写WindowProc虚函数的大前提下&#xff0c;仍然可以处理消息。 类必须具备的要件 类内必须添加声明宏 DECLARE_MESSAGE_MAP() 类外…

刷题记录第五十一天-去除重复字母

题目要求的是字典序最小的结果。只需要理解一点就是按大小顺序排列的字符串的字典序就是最小的&#xff0c;如“abcd”这种。 解题思路如下&#xff1a; 首先明确要使用栈结构&#xff0c;并且是从栈底到栈顶递增&#xff0c;要尽可能保证递增&#xff0c;这样就能保证字典序最…

exsi 6.5 添加RTL8111/8168/8411 网卡驱动重新打包

参考安装esxi时候的No Network Adapters报错 解决办法-CSDN博客 lspci 查看网卡型号 RTL8111/8168/8411 PCI Express 驱动下载地址 List of currently available ESXi packages - V-Front VIBSDepot Wiki 注入驱动程序 https://vibsdepot.v-front.de/tools/ESXi-Customi…

mysql 23-2day 数据库查询(DQL)

目录 数据库查询(DQL)环境&#xff1a;准备一个表格作为查询环境查看数据根据要求查看数据运算查询as 可以修改字段名字 进行查询查询所有部门拼接两个字段查询 2017年入职的员工一个是空null 一个是空白查询 NULL集合排序查询查看有那些组通配符正则查询函数 数据库查询(DQL) …

传输层协议分析--第4关:UDP 包分析

任务描述 本关任务&#xff1a;能够掌握简单的 UDP 包分析。 相关知识 为了更好掌握本章内容&#xff0c;你需要了解的有&#xff1a; UDP 报文的简介&#xff1b;UDP 报文格式&#xff1b;Wireshark 软件中的 UDP 抓包分析。 UDP 简介 UDP&#xff08;User Datagram Pro…

Python与Flink的完美融合:合流基本操作解析

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com Apache Flink 是一个流式处理框架&#xff0c;支持复杂事件处理和大规模数据分析。在 Flink 中&#xff0c;合流&#xff08;Join&#xff09;是一种常见的操作&#xff0c;用于将两个或多个流中的数据按照指定条…

12.21 汇编点亮STM32MP157小灯

.text .global _start _start: 时钟使能pb6 pf6 pe9LDR r0,0x50000A28LDR r1,[r0]ORR r1,r1,#(0x1<<4)ORR r1,r1,#(0x1<<5)ORR r1,r1,#(0x1<<1)STR r1,[r0]配置GPIO模式LDR r0,0x50006000LDR r1,[r0]BIC r1,r1,#(0x2<<20)ORR r1,r1,#(0x1<<20)B…

【UE】阅读和理解距离剔除源码

距离剔除 官方文档&#xff1a;虚幻引擎中的剔除距离体积 | 虚幻引擎5.2文档 (unrealengine.com) 距离剔除&#xff0c;顾名思义&#xff0c;是根据距离来将场景对象的渲染进行加卸载的一种管理方式。 用距离剔除&#xff0c;可以减轻场景同时渲染大量物品的情况&#xff0c;…

ACM32F42x/4x3优势有那些?可应用在那些场景上?

优势 • 最大4MB Flash&#xff0c;可用于同时存储程序代码静态图片 • 128KB/196KB SRAM用于程序堆栈部分图片缓存 • 叠封最大8MB PSRAM&#xff0c;用于大容量图片缓存 • 180MHz M33内核&#xff0c;处理性能极佳 • 可选QFN32(4x4)、QFN48(5x5)小封装&#xff0…

动画渲染需要什么配置电脑?动画云渲染有什么优惠?

​在电影制作、游戏开发、广告设计以及其他设计领域&#xff0c;CG&#xff08;计算机图形学&#xff09;这一发展迅速、并融合了艺术创作与科技应用的领域发挥了重大作用。对于追求在 CG 创作中达到卓越表现的人来说&#xff0c;拥有一台高性能电脑设备至关重要。为此&#xf…

利用prometheus+grafana进行Linux主机监控

文章目录 一.架构说明与资源准备二.部署prometheus1.上传软件包2.解压软件包并移动到指定位置3.修改配置文件4.编写启动脚本5.启动prometheus服务 三.部署node-exporter1.上传和解压软件包2.设置systemctl启动3.启动服务 四.部署grafana1.安装和启动grafana2.设置prometheus数据…