list 的实现

news2024/12/28 11:07:15

目录

list

结点类

结点类的构造函数

list的尾插尾删

list的头插头删

迭代器

++运算符重载

--运算符重载

==和!= 运算符重载

* 和 -> 运算符重载

 list 的insert

list的erase


list

list实际上是一个带头双向循环链表,要实现list,则首先需要实现一个结点类,而一个结点需要存储的信息为:数据、前驱指针、后继指针

而对于该结点类的成员函数来说,我们只需实现一个构造函数即可,因为该结点类只需要根据数据来构造一个结点即可,而结点的释放则由list的析构函数来完成,

结点类

结点类的基本结构:

	template<class T>
	struct ListNode
	{
		ListNode<T>* _next;
		ListNode<T>* _prev;
		T _date;

		ListNode(const T& pos = T())
		{
			_next = nullptr;
			_prev = nullptr;
			_date = pos;
		}
	};

这里用struct 的原因是因为ListNode 的 每个成员变量都会被频繁调用。

用struct则不需要封装了。

结点类的构造函数

构造函数直接根据所给数据构造一个结点即可,构造出来的结点的数据域存储的就是所给数据,而前驱指针和后继指针均初始化为空指针即可

		ListNode(const T& pos = T())
		{
			_next = nullptr;
			_prev = nullptr;
			_date = pos;
		}

list的尾插尾删

	template<class T>
	class list
	{
	public:
		typedef ListNode<T> node;	
		list()
			:_head(new node)
		{
			_head->_next = _head;
			_head->_prev = _head;
		}
	    void push_back(const T& x)
		{
			node* head = _head;
			node* tail = _head->_prev;
			node* p = new node(x);
			tail->_next = p;
			p->_prev = tail;
			p->_next = head;
			head->_prev = p;
		}

		void pop_back()
		{
			assert(_head != _head->_next);
			node* head = _head;
			node* tail = head->_prev;
			node* newtail = tail->_prev;
			newtail->_next = head;
			head->_prev = newtail;
			delete[] tail;
		}
	private:
		node* _head;
	};

list的头插头删

	template<class T>
	class list
	{

	public:	
		typedef ListNode<T> node;
		list()
			:_head(new node)
		{
			_head->_next = _head;
			_head->_prev = _head;
		}
		void push_front(const T& x)
		{
			node* newnode = new node(x);
			node* head = _head;
			node* tail = _head->_next;
			head->_next = newnode;
			newnode->_prev = head;
			newnode->_next = tail;
			tail->_prev = newnode;
		}

		void pop_front()
		{
			assert(_head != _head->_next);
			node* head = _head;
			node* tail = _head->_next;
			head->_next = tail->_next;
			tail->_next->_prev = head;
			delete[]tail;
		}
	private:
		node* _head;
	};

迭代器

迭代器有两种实现方式,具体应根据容器底层数据结构实现:

  1. 原生态指针,比如:vector和string ->物理空间是连续的,因为string和vector对象都将其数据存储在了一块连续的内存空间,我们通过指针进行自增、自减以及解引用等操作,就可以对相应位置的数据进行一系列操作,因此string和vector当中的迭代器就是原生指针。
  2. .将原生态指针进行封装,因迭代器使用形式与指针完全相同,因此在自定义的类中必须实现以下方法:

指针可以解引用,迭代器的类中必须重载operator*()
指针可以通过->访问其所指空间成员,迭代器类中必须重载oprator->()
指针可以++向后移动,迭代器类中必须重载operator++()与operator++(int)
至于operator--()/operator--(int) 是否需要重载,根据具体的结构来抉择,双向链表可
以向前 移动,所以需要重载,如果是forward_list就不需要重载–
迭代器需要进行是否相等的比较,因此还需要重载operator==()与operator!=()
但是对于list来说,其各个结点在内存当中的位置是随机的,并不是连续的,我们不能仅通过结点指针的自增、自减以及解引用等操作对相应结点的数据进行操作,

总结:list的迭代器 实际上就是对结点指针进行了封装,对其各种运算符进行了重载,使得结点指针的各种行为看起来和普通指针一样,(例如,对结点指针自增就能指向下一个结点 p = p->next)

	template<class T1, class T2>
	struct Reverse_iterator
	{
		typedef Reverse_iterator<T1, T2> self;
		typedef ListNode<T1> node;

		node* _it;

		Reverse_iterator(node* pos);
		self& operator++();
		self operator++(int);
		self& operator--();
		self operator--(int);
		T2& operator*();
		T2* operator->();
		bool operator!=(const self& pos);
		bool operator==(const self& pos);

    };

迭代器模板参数说明:

构造函数
迭代器类实际上就是对结点指针进行了封装

其成员变量就是结点指针,所以其构造函数直接根据所给结点指针构造一个迭代器对象即可,

		Reverse_iterator(node* pos)
		{
			_it = pos;
		}

拷贝构造,operator,析构函数我们都不需要写,因为成员变量是内置类型(指针), 用编译器默认生成的就可以。

++运算符重载

		self& operator++()//前置
		{
			_it =_it->_prev;
			return *this;
		}

		self operator++(int)//后置
		{
			self tmp(_it);
			_it = _it->_prev;
			return tmp;
		}

前置++原本的作用是将数据自增,然后返回自增后的数据,

而对于结点迭代器的前置++:应该先让结点指针指向后一个结点.然后再返回“自增”后的结点迭代器即可

后置++,先拷贝构造当前迭代器结点, 然后让当前迭代器结点的指针自增指向下一个结点,最后返回“自增”前的结点迭代器即可,

--运算符重载

		self& operator--()//前置
		{
			_it = _it->_next;
			return *this;
		}

		self operator--(int)//后置
		{
			self tmp(_it);
			_it = _it->_next;
			return tmp;
		}

前置- -:当前迭代器结点中的指针指向前一个结点,然后再返回“自减”后的结点迭代器即可,

后置--:拷贝构造当前迭代器对象 -> 当前迭代器结点中的指针自减指向前一个结点 ->返回自减前的迭代器。

==和!= 运算符重载

		bool operator!=(const self& pos)
		{
			return _it != pos._it;
		}

		bool operator==(const self& pos)
		{
			return _it == pos._it;
		}

这里注意形参别写错就好了。

* 和 -> 运算符重载

使用解引用操作符时,是想得到该指针指向的数据内容

因此,我们直接返回当前结点指针所指结点的数据即可,这里需要使用引用返回,因为解引用后可能需要对数据进行修改,

		T2& operator*()
		{
			return _it->_date;
		}

->返回当前迭代器结点的指针所指结点的数据的地址

		T2* operator->()
		{
			return &_it->_date;
		}

使用场景:

 list 的insert

insert函数可以在所给迭代器pos之前插入一个新结点,

1.先根据所给迭代器pos得到该位置处的结点指针

2.然后通过该指针找到前一个位置的结点指针last

根据所给数据x构造一个新结点

		iterator insert(iterator pos,const T& x)
		{
			node* newnode = new node(x);
			node* next = pos._node;
			node* last = next->_prev;
			last->_next = newnode;
			newnode->_prev = last;
			newnode->_next = next;
			next->_prev = newnode;
			return iterator(newnode);
		}

list的erase

erase函数可以删除所给迭代器位置的结点,

注意**:pos不可以是哨兵位的迭代器,即不能删除哨兵位 pos迭代器结点中的指针不能为空**

1.根据所给迭代器得到该位置处的结点指针self

2.通过self指针找到前一个位置的结点指针last,以及后一个位置的结点指针next

3.紧接着释放cur结点,最后prev和next结点进行链接

		iterator erase(iterator pos)
		{
			assert(pos._node);
			assert(_head != _head->_next);
			node* self = pos._node;
			node* next = self->_next;
			node* last = self->_prev;
			last->_next = next;
			next->_prev = last;
			delete[] self;
			return iterator(next);
		}

关于insert 和 erase 迭代器失效的问题:

insert不会导致迭代器失效,因为pos迭代器中的节点指针仍然指向原来的节点。

问:erase之后, pos迭代器是否失效:
一定失效,因为此时pos迭代器中的节点指针指向的节点已经被释放了,该指针相当于是野指针。

 最后所有代码如下:
 

namespace bit
{
	template<class T>
	struct ListNode
	{
		ListNode<T>* _next;
		ListNode<T>* _prev;
		T _date;

		ListNode(const T& pos = T())
		{
			_next = nullptr;
			_prev = nullptr;
			_date = pos;
		}
	};

	template<class T1,class T2 = T1>
	struct ListIterator
	{
		typedef ListIterator<T1,T2> iterator;
		typedef ListNode<T1> node;
		node* _node;

		ListIterator(node* pos)
		{
			_node = pos;
		}

		T2& operator*()
		{
			return _node->_date;
		}

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

		iterator operator++(int)
		{
			iterator tmp(_node);
			_node = _node->_next;
			return tmp;
		}

		iterator& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		iterator operator--(int)
		{
			iterator tmp(_node);
			_node = _node->_prev;
			return tmp;
		}

		T2* operator->()
		{
			return &_node->_date;
		}

		bool operator!=(const iterator& pos)
		{
			return _node != pos._node;
		}

		bool operator==(const iterator& pos)
		{
			return _node == pos._node;
		}
	};

	template<class T1, class T2>
	struct Reverse_iterator
	{
		typedef Reverse_iterator<T1, T2> self;
		typedef ListNode<T1> node;

		node* _it;

		Reverse_iterator(node* pos)
		{
			_it = pos;
		}

		self& operator++()
		{
			_it =_it->_prev;
			return *this;
		}

		self operator++(int)
		{
			self tmp(_it);
			_it = _it->_prev;
			return tmp;
		}

		self& operator--()
		{
			_it = _it->_next;
			return *this;
		}

		self operator--(int)
		{
			self tmp(_it);
			_it = _it->_next;
			return tmp;
		}

		T2& operator*()
		{
			return _it->_date;
		}

		T2* operator->()
		{
			return &_it->_date;
		}

		bool operator!=(const self& pos)
		{
			return _it != pos._it;
		}

		bool operator==(const self& pos)
		{
			return _it == pos._it;
		}
	};

	template<class T>
	class list
	{

	public:
		typedef Reverse_iterator<T, T> reverse_iterator;
		typedef Reverse_iterator<T, const T> const_reverse_iterator;
		typedef ListNode<T> node;
		typedef ListIterator<T> iterator;
		typedef ListIterator<T,const T> const_iterator;
		list()
			:_head(new node)
		{
			_head->_next = _head;
			_head->_prev = _head;
		}

		list(const list& pos)
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
			for (auto e : pos)
			{
				push_back(e);
			}
		}

		list(initializer_list<T> il)
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
			for (auto e : il)
			{
				push_back(e);
			}

		}

		void push_back(const T& x)
		{
			node* head = _head;
			node* tail = _head->_prev;
			node* p = new node(x);
			tail->_next = p;
			p->_prev = tail;
			p->_next = head;
			head->_prev = p;
		}

		void pop_back()
		{
			assert(_head != _head->_next);
			node* head = _head;
			node* tail = head->_prev;
			node* newtail = tail->_prev;
			newtail->_next = head;
			head->_prev = newtail;
			delete[] tail;
		}

		reverse_iterator rbegin()
		{
			return reverse_iterator(_head->_prev);
		}

		reverse_iterator rend()
		{
			return reverse_iterator(_head);
		}

		const_reverse_iterator crbegin()const
		{
			return const_reverse_iterator(_head->_prev);
		}

		const_reverse_iterator crend()const
		{
			return const_reverse_iterator(_head);
		}


		iterator begin()
		{
			return iterator(_head->_next);
		}

		const_iterator begin()const
		{
			return const_iterator(_head->_next);
		}

		iterator end()
		{
			return iterator(_head);
		}

		const_iterator end()const
		{
			return const_iterator(_head);
		}

		void push_front(const T& x)
		{
			node* newnode = new node(x);
			node* head = _head;
			node* tail = _head->_next;
			head->_next = newnode;
			newnode->_prev = head;
			newnode->_next = tail;
			tail->_prev = newnode;
		}

		void pop_front()
		{
			assert(_head != _head->_next);
			node* head = _head;
			node* tail = _head->_next;
			head->_next = tail->_next;
			tail->_next->_prev = head;
			delete[]tail;
		}

		iterator insert(iterator pos,const T& x)
		{
			node* newnode = new node(x);
			node* next = pos._node;
			node* last = next->_prev;
			last->_next = newnode;
			newnode->_prev = last;
			newnode->_next = next;
			next->_prev = newnode;
			return iterator(newnode);
		}

		iterator erase(iterator pos)
		{
			assert(pos._node);
			assert(_head != _head->_next);
			node* self = pos._node;
			node* next = self->_next;
			node* last = self->_prev;
			last->_next = next;
			next->_prev = last;
			delete[] self;
			return iterator(next);
		}

		~list()
		{
			iterator it1 = begin();
			while (it1 != end())
			{
				it1 = erase(it1);
			}
			delete _head;
			_head = nullptr;
		}

	private:
		node* _head;
	};

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

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

相关文章

【Python】解决Python错误报错:IndexError: tuple index out of range

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

宝塔 nginx 配置负载均衡 upstream

nginx 主配置文件加入 upstream myapp1 {server 192.168.124.101:5051;server 192.168.124.102:5052;server 192.168.124.111:5050;}站点配置文件中加入 location / {proxy_pass http://myapp1;}80端口映射到外网域名配置方法 加入红框中的代码 upstream myapp3 {server 192.16…

物联边缘网关有哪些功能?物联边缘网关在工业方向的应用-天拓四方

随着物联网技术的快速发展&#xff0c;越来越多的设备和系统正在接入到网络中&#xff0c;形成了一个庞大的智能生态系统。在这个系统中&#xff0c;物联边缘网关扮演着至关重要的角色&#xff0c;它不仅是连接设备和云端的桥梁&#xff0c;更是推动智能应用落地的关键。在当今…

强烈推荐十款数据防泄密软件,高人气的数据防泄密软件

100G的文件不见了&#xff1f;客户的电话信息被拷贝走了&#xff1f;源代码被竞争对手搞到手了&#xff1f;这些都是严重的数据泄密事件&#xff0c;为此&#xff0c;我们需要数据防泄密软件来全方位保护数据安全。根据当前市场上的热门推荐和综合评价&#xff0c;以下几款数据…

Arm发布Cortex X925、A725、A520,Armv9.2架构

随着半导体行业的不断发展&#xff0c;Arm 通过突破技术界限&#xff0c;为终端用户提供尖端解决方案&#xff0c;在核心和 IP 架构创新方面处于领先地位&#xff0c;尤其是在移动领域。2024 年&#xff0c;Arm 的年度战略进步重点是增强去年的 Armv9.2 架构&#xff0c;并带来…

Vue3-Vite-ts 前端生成拓扑图,复制即用

完整代码&#xff0c;复制即可用&#xff0c;样式自调 试过 jointjs dagre-d3 vis&#xff0c;好用一点 方法1&#xff1a;Vis.js npm install vis-network <template><div id"mynetwork" class"myChart" :style"{width: 100%, height: 9…

你是否正确地编写了 Git 提交信息?

介绍 在版本控制方面&#xff0c;Git 是一个非常有效的工具。然而&#xff0c;像任何其他工具一样&#xff0c;你必须正确使用它才能充分发挥其作用。你需要考虑不同的方面。本文着重介绍如何按照传统提交规范&#xff08;Conventional Commits specification&#xff09;编写…

在Unity中配置Android项目以允许HTTP流量,解决AVPro在Android平台中无法播放http视频

解决方法快速通道&#xff1a;拉到底&#xff0c;看倒数第二张图 好记性不如烂笔头 最近在使用AVpro插件播放http视频&#xff0c;在Editor中一切正常&#xff0c;然而打包在Android平台下就播放不了 AVPro在Unity中的警告&#xff1a; 感觉只是个警告&#xff0c;没引起注意…

3d渲染的常用概念和技术,渲染100邀请码1a12

之前我们介绍了3D渲染的基本原理和流程&#xff0c;这次说下几个常用概念和技术。 3D渲染中涉及到很多专业的概念和技术&#xff0c;它们决定了渲染质量和效果&#xff0c;常用的有以下几个。1、光线追踪 光线追踪是一些专业渲染器&#xff08;如V-Ray和Corona等&#xff09;…

EXSI虚拟机新增磁盘并将空间扩充到已有分区

这里写自定义目录标题 1、在EXSI虚拟机中新增一块磁盘配置大小2、确认新磁盘3、格式化新分区4、添加新分区到LVM5、将新增分区添加到已有分区里 1、在EXSI虚拟机中新增一块磁盘配置大小 注意事项&#xff1a; (1)需确保虚拟机已关闭活处于维护模式&#xff0c;避免数据丢失 (2…

多输入多输出非线性对象的模型预测控制—Matlab实现

本示例展示了如何在 Simulink 中设计多输入多输出对象的闭环模型预测控制。该对象有三个操纵变量和两个测量输出。 一、非线性对象的线性化 运行该示例需要同时安装 Simulink 和 Simulink Control Design。 % 检查是否同时安装了 Simulink 和 Simulink Control Design if ~m…

探索第三方美颜SDK:美颜插件的技术原理

本篇文章&#xff0c;我们将深入了解第三方美颜SDK&#xff0c;主要探讨关于美颜插件的工作机制与算法。 一、第三方美颜SDK的概述 第三方美颜SDK是由专业团队开发的一套用于实现美颜功能的软件开发工具包。它通常包括了各种美颜算法、滤镜效果、人脸识别等核心技术&#xff…

面试题vue+uniapp(个人理解-面试口头答述)未编辑完整....

1.vue2和vue3的区别&#xff08;vue3与vue2的区别&#xff08;你不知道细节全在这&#xff09;_vue2和vue3区别-CSDN博客&#xff09;参考 Vue3 在组合式&#xff08;Composition &#xff09;API&#xff0c;中使用生命周期钩子时需要先引入&#xff0c;而 Vue2 在选项API&am…

数字化转型推动生物技术企业增长—纷享销客与集萃药康共探新动力

上周&#xff0c;在南京锦创书城&#xff0c;一场主题为“生物技术企业增长新动力&#xff1a;以客户为中心的数字化转型与创新”的研讨会圆满落幕。此次活动由纷享销客江苏分公司联合江苏集萃药康生物科技股份有限公司共同举办&#xff0c;吸引了众多生物技术领域企业的负责人…

新零售收银解决方案:传统门店超市的数字化-亿发

在数字化浪潮的推动下&#xff0c;零售行业正经历着前所未有的变革。阿里巴巴提出的“新零售”概念&#xff0c;不仅仅是一个商业口号&#xff0c;它代表了一种全新的商业模式和运营理念。随着时代的进步和消费需求的不断升级&#xff0c;新零售的兴起已成为行业发展的必然趋势…

GUI 02:布局管理器相关知识,AWT 的 3 种布局管理器应用,以及嵌套布局的使用

一、前言 记录时间 [2024-05-31] 前置文章 GUI 01&#xff1a;GUI 编程概述&#xff0c;AWT 相关知识&#xff0c;Frame 窗口&#xff0c;Panel 面板&#xff0c;及监听事件的应用 本文讲述了 GUI 编程种布局管理器的相关知识&#xff0c;以及 AWT 的 3 种布局管理器——流式布…

STL中list的模拟实现

目录 list模拟实现 list节点 list的push_back()函数 list的迭代器操作&#xff08;非const&#xff09; list的迭代器操作&#xff08;const&#xff09; list迭代器const 非const优化 list的insert()函数 list的erase()函数 list的pop_back() push_front() pop_front(…

【Python】解决Python报错:AttributeError: ‘int‘ object has no attribute ‘xxx‘

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

使用element的提示框并修改css

使用el-tooltip来做提示框&#xff1a; <el-tooltip popper-class"popper" content"敬请期待" placement"right"><div><i class"iconfont icon-lianjie-01"></i><span>输入链接</span></div&…

从头开始构建GPT标记器

从头开始构建GPT标记器 对于GPT Tokenizer&#xff0c;论文《Language Models are Unsupervised Multitask Learners》中介绍了一种字节级编码作为LLM的标记化机制&#xff1a; The vocabulary is expanded to 50,257. We also increase the context size from 512 to 1024 to…