list的常用接口底层实现与介绍

news2024/10/10 2:19:19

目录

概念:

list的基本结构:

list的迭代器⭐❤: 

自定义类型的完善: 

const的迭代器:

insert 

erase:

size 

empty 

push_back 、push_front 、pop_back、pop_front 

swap 、operator=

析构函数、clear() 


 

概念:
  1. list是序列容器,允许在序列中的任何位置执行固定O(1)时间复杂度的插入和删除操作,并在两个方向进行迭代。
  2. list容器使用双链表实现;双链表将每个元素存储在不同的位置,每个节点通过next,prev指针链接成顺序表。
  3. list与其他标准序列容器(array,vector和deque)相比,list通常可以在容器内的任何位置插入、提取和移动元素。
  4. list与其他标准序列容器(array,vector和deque)相比,list和forward_list(单链表实现)的主要缺点是他们不能通过位置直接访问元素;例如,要访问列表中的第五个元素,必须从已知位置(开始或结束)迭代到该位置,需要哦线性时间开销。
  5. 存储密度低,list要使用一些额外的内容空间(next,prev)来保持与每个元素相关联(前后续的线性)的链接信息,从而导致存储小元素类型(如char,short,int等)的列表的存储密度低。

list的基本结构:
#include<iostream>
#include<assert.h>
using namespace std;
namespace bit {
	template <class T>
	struct ListNode
	{
		ListNode<T>* _next;
		ListNode<T>* _prev;
		T _data;
		ListNode(const T& x = T())
			:_next(nullptr)
			,_prev(nullptr)
			,_data(x)
		{}
	};
	template<class T>
	class list {
		typedef ListNode<T> Node;
	public:
		list()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;
		}
	private:
		Node* _head;
		size_t _size;
	};
}

通过list的概念我们可以得知,list在本质上就是一种带头的双向链表结构,所以在创建list类之前,我们首先要构建一个节点的结构体,其次在list的构造函数中,为了让list遵循它本质上的数据结构,以及通过之后的插入删除等等操作,利用带头双向链表结构的特点,我们将成员变量特地设置为头节点以及长度。

list的迭代器⭐❤: 

list的迭代器特殊之处: 

  • 迭代器的本质就是不管是什么类型的数据都可以进行访问!所以该如何使用迭代器遍历链表内的数据呢?是否能使用前驱、后继指针充当迭代器?答案是不能!
  • 如果使用前驱后继指针充当迭代器,那么就需要遇到一个问题,链表的各个节点是不同位置不同地址的!
  • 所以如果使用指针+1的方式并不能正确的遍历链表!
  • 其次,如果使用前驱后继指针当迭代器,那么解引用能得到节点内的数据吗?不能!因为Node * 解引用后上一个Node 是一个strcut结构体

所以对于list的迭代器,正确的解决方案是另外在设置一个类对迭代器进行分装操作! 

	template<class T>
	struct ListIterator
	{
		typedef ListNode<T> Node;
		typedef ListIterator<T> self;
		Node* _node;
		ListIterator(Node*node)
			:_node(node)
		{}
	};

在原先的原身指针 prev和next下,并不能解决迭代器遍历操作,以及解引用操作,因为节点的空间问题并不能使用指针+1的操作,以及解引用prev和next并不能直接获取到节点内部存储的数值元素,所以我们需要在这个封装操作中进行。

至于这个封装为什么是结构体呢?

在C++中,struct和class是非常相似的,唯一的区别是默认的访问权限不同。在struct中,成员默认是公共的(public),而在class中,默认是私有的(private)。因此,在C++中,struct内部是可以定义函数的。 

在这个结构体的内部我们需要解决迭代器的遍历、解引用、遍历修改、不等于 等运算符重载问题! 

		//*
		T& operator*()
		{
			return _node->_data;
		}
		//前置++ 因为这里是链表所以++应该是指向下一个节点!
		self& operator++() {
			_node = _node->_next;
			return *this;
		}
		self& operator++(int)
		{
			self tmp(*this);
			_node = _node->_next;
			return tmp;
		}
		//同理--应该是指向上一个节点!
		self& operator--()
		{
			_node = _node->_prev;
			return*this;
		}
		slef& operator--(int)
		{
			self tmp(*this);
			_node = _node->_prev;
			return tmp;
		}
		bool operator!=(const self& it)
		{
			return _node != it._node;
		}

我们定义这些重载运算符的意义就在于为了之后我们在定义begin时,可以直接使用封装的重载运算符!

		typedef ListIterator<T> iteator;
		iterator begin()
		{
			iterator it(_head->_next);
			return it;
		}
		iterator end()
		{
			iterator it(_head);
			return it;
		}

自定义类型的完善: 

自定义类型的完善 ,因为我们写的是一个自定义类型,所以读取*it后解决完后还是一个自定义类型的数据结构,所以需要在使用数据结构的写法,表明其中内部的数据即可!

 

		T* operator->()
		{
			return &_node->_data;
		}

it->是需要进行拆解的,可以变成it.operator()->,这里其实是编译器的一种隐藏操作,之前的it++也是一种隐藏操作,可以变化为it.operator++()

而it->_a1这里隐藏的是另一个-> ,也就是图中第二个->,第一个->是属于it的重载运算符标识!

const的迭代器:

首先要记住const不能调用非const的成员函数!但是非const可以调用const的成员函数

为了让const修饰的类型能够使用迭代器,我们需要根据const不能修改内容的特点进行另外设置一共专门让const使用的迭代器封装,但是由于只是需要变动operator*()和operator->所以我们可以使用模板类解决这类问题! 

template<class T, class Ref, class Ptr>
	struct ListIterator
	{
		typedef ListNode<T> Node;
		typedef ListIterator<T, Ref, Ptr> Self;

		Node* _node;

		ListIterator(Node* node)
			:_node(node)
		{}

		// *it
		//T& operator*()
		Ref operator*()
		{
			return _node->_data;
		}

		// it->
		//T* operator->()
		Ptr operator->()
		{
			return &_node->_data;
		}

		// ++it
		Self& operator++()
		{
			_node = _node->_next;
			return *this;
		}

		Self operator++(int)
		{
			Self tmp(*this);
			_node = _node->_next;

			return tmp;
		}

		Self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		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;
		}
	};
insert 

 有了迭代器之后了,可以利用迭代器的操作进行insert的底层实现

void insert(iterator pos, const T& val)
		{
			Node* cur = pos._node;
			Node* newnode = new Node(val);
			Node* prev = cur->_prev;

			// prev newnode cur;
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
			_size++;
		}

erase:

操作和insert一样都是对指针的方向的修改!但是pos会在后面失效,也就是迭代器会失效,所以需要改变类型!然后进行返回!这里的返回变成了的pos节点的后面一个节点的迭代器。

iterator erase(iterator pos)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;
			
			prev->_next = next;
			next->_prev = prev;
			delete cur;
			_size--;

			return iterator(next);
		}
size 
        size_t size() const
		{
			return _size;
		}
empty 
        bool empty()
		{
			return _size == 0;
		}
push_back 、push_front 、pop_back、pop_front 
        void push_back(const T& x)//尾插
		{
			insert(end(), x);
		}

		void push_front(const T& x)//头插
		{
			insert(begin(), x);
		}

		void pop_back()//尾删
		{
			erase(--end());
		}

		void pop_front()//头删
		{
			erase(begin());
		}
swap 、operator=
        void swap(list<T>& lt)
		{
			std::swap(_head, lt._head);
			std::swap(_size, lt._size);
		}

		// lt1 = lt3
		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}
析构函数、clear() 
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

记录Http访问服务接口出现 301 Moved Permanently

记录Http访问服务接口出现 301 Moved Permanently 一、项目背景 ​ 在A服务中 需要通过远程调用 B服务接口 获取数据。A项目与B项目部署在不同的服务接口中。 请求接口响应界面 ​ 在调用B项目的接口时&#xff0c;响应的数据一直为 301 Moved Permanently Html代码&#x…

【星计划★C语言】c语言初相识:探索编程之路

&#x1f308;个人主页&#xff1a;聆风吟_ &#x1f525;系列专栏&#xff1a;星计划★C语言、Linux实践室 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. ⛳️第一个c语言程序二. ⛳️数据类型2.1 &#x1f514;数据单位2.2 &…

2024福建三支一扶报名流程,超全超详细!

2024年福建三支一扶报名已经开始&#xff0c;请注意时间&#xff01; ⚠2024年福建省省级“三支一扶”计划招募岗位1070个 报名时间&#xff1a;4月1日8:00至4月17日17:00 审查考核&#xff1a;4月18日至5月10日 确定派遣人员&#xff1a;5月11日至5月31日 报名入口&#xff1…

Day66-企业级防火墙iptables精讲2

Day66-企业级防火墙iptables精讲2 1. iptables项目案例2&#xff1a;局域网共享上网&#xff1a;2. iptables项目案例3&#xff1a;外网IP的端口映射到内网IP的端口3. 老男孩教育iptables项目案例4&#xff1a;IP一对一映射&#xff08;DMZ&#xff09;4. 老男孩教育iptables项…

jdbc连SQL server,显示1433端口连接失败解决方法

Exception in thread "main" com.microsoft.sqlserver.jdbc.SQLServerException: 通过端口 1433 连接到主机 localhost 的 TCP/IP 连接失败。错误:“connect timed out。请验证连接属性。确保 SQL Server 的实例正在主机上运行&#xff0c;且在此端口接受 TCP/IP 连接…

在project模式下自定义Implementation Strategies

Implementation Settings定义了默认选项&#xff0c;当要定义新的implementation runs时会使用这些选项&#xff0c;选项的值可以在Vivado IDE中进行配置。 图1展示了“Settings”对话框中的“implementation runs”对话框。要从Vivado IDE打开此对话框&#xff0c;请从主菜单中…

文件管理--fscanf,fread,fwrite和fprintf

fprintf函数&#xff1a;对于fprintf函数&#xff0c;它和printf一样&#xff0c;但是它的表达式为&#xff1a;int fprintf ( FILE * stream, const char * format, ... );和printf的很相似&#xff0c;但有不一样。它是格式化输出函数&#xff0c;代码为&#xff1a; #includ…

2024年清明节安装matlab 2024a

下载安装离线支持包SupportSoftwareDownloader_R2024a_win64&#xff0c;地址https://ww2.mathworks.cn/support/install/support-software-downloader.html&#xff0c;运行软件&#xff08;自解压运行&#xff09;&#xff0c;登录账号&#xff08;需要提前在官网注册&#x…

Ubuntu部署BOA服务器

BOA服务器概述 BOA是一款非常小巧的Web服务器&#xff0c;源代码开放、性能优秀、支持CGI通用网关接口技术&#xff0c;特别适合用在嵌入式系统中。 BOA服务器主要功能是在互联嵌入式设备之间进行信息交互&#xff0c;达到通用网络对嵌入式设备进行监控&#xff0c;并将反馈信…

MPLS-基础、LSR、LSP、标签、体系结构

MPLS技术 MPLS基础 MPLS&#xff1a;转发数据时&#xff0c;只在网络边缘分析IP报文头&#xff0c;不在每一跳都分析&#xff0c;节约了转发时间。 MPLS&#xff1a;Multiprotocol Label Switching&#xff0c;多协议标签交换骨干网技术。主要应用&#xff1a;VPN、流量工程…

基于wordcloud、matplotlib等对某东评论数据情感分析-Python数据分析项目

基于wordcloud、matplotlib等对某东评论数据情感分析 文章目录 基于wordcloud、matplotlib等对某东评论数据情感分析1 数据导入及预处理1.1 数据导入1.2 数据描述1.3 数据预处理 2 情感分析2.1 情感分析2.2 情感分直方图2.3 词云图2.4 关键词提取 3 积极评论与消极评论3.1 积极…

【番外篇1】统计学+spss-t检验

目录 t检验 独立样本 t 检验 p值 spss如何分析独立样本t检验 配对样本t检验 单样本t检验 t检验 当我们想要比较两组数据&#xff08;比如两组学生的成绩&#xff09;是否真的有显著差异时&#xff0c;就可以使用 t 检验。 t 检验告诉我们&#xff0c;这种差异是不是真的…

深入解析Hadoop生态核心组件:HDFS、MapReduce和YARN

这里写目录标题 01HDFS02Yarn03Hive04HBase1&#xff0e;特点2&#xff0e;存储 05Spark及Spark Streaming关于作者&#xff1a;推荐理由&#xff1a;作者直播推荐&#xff1a; 一篇讲明白 Hadoop 生态的三大部件 进入大数据阶段就意味着进入NoSQL阶段&#xff0c;更多的是面向…

@Order和@DependsOn的区别

这里写自定义目录标题 一、区别二、demo演示1、Order2、DependsOn 一、区别 Order&#xff1a;改变Bean注入的顺序DependsOn&#xff1a;改变Bean创建的顺序 二、demo演示 1、Order 类 A B 都实现了接口 I &#xff0c;且 A B都由Spring容器创建并且管理 public class A im…

.NET CORE 分布式事务(四) CAP实现最终一致性

目录 引言&#xff1a; 1.0 最终一致性介绍 2.0 CAP 2.0 架构预览 3.0 .NET CORE 结合CAP实现最终一致性分布式事务 3.1 准备工作(数据库&#xff0c;本文使用的是MySql) 3.1.1 数据模型 3.1.2 DbContext 3.1.3 数据库最终生成 3.2 Nuget引入 3.3 appsettings.json …

FressRTOS_day4:2024/4/4

1.总结二进制信号量和计数型信号量的区别&#xff0c;以及他们的使用场景。 二进制信号量的数值只有0和1。&#xff08;用于共享资源的访问&#xff09;&#xff1b;而计数型信号量的值一般是大于或者等于2&#xff08;用于生产者和消费者模型&#xff09; 2.使用计数型信号量…

安装Schedule库的方法最终解答!_Python第三方库

安装Python第三方库Schedule 我的环境&#xff1a;Window10&#xff0c;Python3.7&#xff0c;Anaconda3&#xff0c;Pycharm2023.1.3 Schedule库 Schedule 是一个轻量级、功能强大而灵活的任务调度工具库&#xff0c;用于在指定的时间间隔内执行任务。为用户提供了简单易用的…

树(Tree) - 概念与基础

树的基本概念 树(Tree)是一种重要的数据结构&#xff0c;它在计算机科学中被广泛应用于各种算法和程序中。树是由节点(node)组成的层次结构&#xff0c;其中每个节点都有一个父节点&#xff0c;除了根节点外&#xff0c;每个节点都有零个或多个子节点。树的一个关键特点是没有…

Java云联his系统,支持电子病历四级,医院信息管理系统源码

SaaS模式Java语言开发的云HIS系统源码&#xff0c;支持电子病历四级&#xff0c;系统充分考虑了模板化、配置化、智能化、扩展化等设计方法&#xff0c;覆盖了基层医疗机构的主要工作流程&#xff0c;能够与监管系统有序对接&#xff0c;并能满足未来系统扩展的需要。 云HIS系统…

matlab使用教程(33)—求解时滞微分方程(1)

1.时滞微分方程(DDE)的分类 时滞微分方程 (DDE) 是当前时间的解与过去时间的解相关的常微分方程。该时滞可以固定不变、与时间相关、与状态相关或与导数相关。要开始积分&#xff0c;通常必须提供历史解&#xff0c;以便求解器可以获取初始积分点之前的时间的解。 1.1常时滞 D…