C++语言·list链表(下)

news2024/12/26 13:53:01

        还是之前说的,因为要写模板,为了避免链接出现问题,我们将所有内容都写到一个文件中去。首先就是画出链表的框架

                

        链表本身只需要一个头节点就足以找到整条链表,而需要它拼接的节点我们再写一个模板。而我们知道list是一个带头双向循环链表,所以再写节点模板是时候将要表现出来双向,在写链表模板的时候要表现出带头和循环。

        之所以要把ListNode设定成struct而不是class,因为再list中的成员函数中要反复访问节点中的成员变量,所以ListNode中的成员变量和成员函数都要设置成public公有的,所以干脆就直接写成struct了。

        本节的重点也是难点就是链表的迭代器

1. 迭代器

        构造析构还有那些接口我们已经熟的不能再熟了,之后再说,这里我们直接进入重点。

        之前 string 和 vector 的迭代器我们都是直接 typdef 出来的,那list的迭代器可不可以也这样做。

                        

        答案当然是不行,前两者都是因为它们的存储空间是在物理上连续的,所以在 ++iterator的时候可以直接访问到下一块内存,显然链表是不行的,因此我们要给链表的迭代器特别写一个类出来,为了重载iterator的 ++iterator *iterator等行为

        这里不建议把这个迭代器写成list的内部类,因为写成内部类之后迭代器不是list的友元啊,之后操作迭代器就会变得异常麻烦。

        ​​​​​​​        ​​​​​​​

        链表的迭代器我们选择用节点的指针。现在我们将迭代器的结构描绘出来,然后记得在list类中typedef一下,达到容器中迭代器的名称一致。

        下面我们实现迭代器类,主要就是重载迭代器的几种操作方式

        ​​​​​​​        ​​​​​​​

        我们的迭代器是不去支持 + 或 - 的因为这个效率很低,就是在O(N)时间复杂度的遍历链表,拷贝构造,析构和赋值也都不需要写,让编译器自动生成浅拷贝就行,因为迭代器的目的是去访问节点,而不是去修改,也不要释放。当然,解引用访问到某个节点之后进行的修改不是说迭代器在修改节点数据,而是利用迭代器访问到了节点之后,在节点中修改它的数据。

        其实迭代器这个东西就是一种封装,Node*指针无法直接完成下一块空间的访问,因此我们就将Node*封装到迭代器中,并且在迭代器中重载实现访问下一块空间的方法。

1.1 begin() end()

                

        end()取出的迭代器就是那个头节点或者说哨兵位,因为迭代器都是左闭右开的嘛

1.2 const_iterator

        因为迭代器跟指针的效果是类似的,const迭代器起到一个迭代器本身可以修改但是其指向内容不能修改的效果,所以const迭代器我们不能在iterator前面简单加一个const,而是要再写一个专门的ListConstIterator类。

        这个类其实和普通的内容都基本一样,我们只需要在*运算符重载的函数前面加const限制返回值就好了。

        ​​​​​​​        ​​​​​​​        

        不过我更推荐的是将const和非const的迭代器类用模板写在一起

        

        在list类中实例迭代器的时候将它们区分开

        ​​​​​​​

        那个Ptr模板是重载->操作符用的,具体内容可以到最后完整代码中查看

2. 构造 析构 赋值操作符重载

2.1 默认构造

        ​​​​​​​        

        默认构造就是建立出那个头节点,或者说哨兵位。

2.2 析构

                 

        我们先写一个清空容器的函数clear,析构就是把容器清空之后再把头节点哨兵位释放掉。

2.3 拷贝构造

        ​​​​​​​        ​​​​​​​        

2.4 赋值操作符重载

                

2.5 initializer_list构造

                

3. 插入与删除

3.1 随机位置插入

        ​​​​​​​        

        双向链表的插入逻辑我就不讲了,详见数据结构章节的链表讲解

数据结构·双向链表-CSDN博客文章浏览阅读1k次,点赞18次,收藏16次。本节讲解了双向链表的结构,以及实现了一个双向链表及功能,不得不说双向链表比单链表写起来简单多了,没有那么多繁琐的条件判断https://blog.csdn.net/atlanteep/article/details/135880386        这里要说的问题就是链表插入时的迭代器会不会失效,事实上是不会的,因为链表不涉及扩容和数据位置的挪动,所以在插入数据的时候迭代器是不会失效的。

        但是在STL标准中,为了各个容器的一致,链表insert也会返回第一个新插入元素的迭代器

3.2 随机位置的删除

        ​​​​​​​        

        随机位置的删除我们要先断言一下,不能说把哨兵位都给删了。

        删除节点是会造成迭代器失效的,STL标准中要返回被删除的最后一个元素之后那个元素的迭代器。

        

3.3 头插头删、尾插尾删

                

        这个纯白给的,只不过注意一下尾删时,要删除哨兵位的前一个节点,而不是删end()位置的节点。

4. 完整代码

#include<assert.h>

namespace atl
{
	template<class T>
	struct ListNode
	{
		ListNode<T>* _next;
		ListNode<T>* _prev;
		T _data;

		ListNode(const T& data = T())
			:_next(nullptr)
			, _prev(nullptr)
			, _data(data)
		{}
	};

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

		ListIterator() = default;
		ListIterator(Node* node)
			:_node(node)
		{}

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

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

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

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

		//*iterator
		Ref operator*()
		{
			return _node->_data;
		}

		//iterator->
		Ptr operator->()
		{
			return &_node->_data;
		}

		//iterator!=iterator
		bool operator!=(const Self& it)
		{
			return _node != it._node;
		}

		//iterator==iterator
		bool operator==(const Self& it)
		{
			return _node == it._node;
		}
	};



	template<class T>
	class list
	{
		typedef ListNode<T> Node;
	public:
		//迭代器
		typedef ListIterator<T, T&, T*> iterator;
		typedef ListIterator<T, const T&, const T*> const_iterator;

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

		//默认构造
		list()
		{
			_head = new Node();
			_head->_next = _head;
			_head->_prev = _head;
		}



		//析构
		void clear()
		{
			auto it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}

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



		//拷贝构造
		list(const list<T>& lt)
		{
			//创建头节点
			_head = new Node();
			_head->_next = _head;
			_head->_prev = _head;

			for (const auto& e : lt)
			{
				push_back(e);
			}
		}


		//赋值操作符重载
		list<T>& operator=(list<T> lt)
		{
			std::swap(_head, lt._head);
			return *this;
		}


		//initializer_list构造
		list(initializer_list<T> il)
		{
			//创建头节点
			_head = new Node();
			_head->_next = _head;
			_head->_prev = _head;

			for (const auto& e : il)
			{
				push_back(e);
			}
		}


		//没有迭代器失效
		//随机位置插入
		iterator insert(iterator pos,const T& x)
		{
			Node* cur = pos._node;
			Node* newnode = new Node(x);

			newnode->_prev = cur->_prev;
			newnode->_next = cur;
			cur->_prev->_next = newnode;
			cur->_prev = newnode;

			return iterator(newnode);
		}

		//有迭代器失效
		//随机位置删除
		iterator erase(iterator pos)
		{
			assert(pos != end());
			Node* cur = pos._node;

			//记录将要返回的迭代器
			iterator it((cur->_next));

			cur->_next->_prev = cur->_prev;
			cur->_prev->_next = cur->_next;
			delete cur;

			return it;
		}

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

	private:
		Node* _head;
	};
}

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

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

相关文章

JAVA云HIS医院管理系统源码 云HIS系统源码 云HIS的优势 云HIS的发展

JAVA云HIS医院管理系统源码 云HIS系统源码 云HIS的优势 云HIS的发展 HIS系统&#xff0c;即医院信息系统&#xff08;Hospital Information System&#xff09;&#xff0c;在医院的运营和管理中扮演着至关重要的角色。关于HIS系统的溯源&#xff0c;简单从以下几个方面进行讲…

618精选编程书单:学好代码是用好大模型的基础

大家好&#xff0c;我是爱编程的喵喵。双985硕士毕业&#xff0c;现担任全栈工程师一职&#xff0c;热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。…

QT6.2.4 MSVC2019 连接MySql5.7数据库,无驱动问题

1.下载 查询一下数据库驱动 qDebug()<<QSqlDatabase::drivers(); 结果显示&#xff0c;没有QMYSQL的驱动。 QList("QSQLITE", "QMARIADB", "QODBC", "QPSQL") MySql6.2.4驱动下载地址&#xff0c;如果是别的版本&#xff0c;…

【Python Cookbook】S01E03 找到最大最小的N个元素

目录 问题解决方案讨论 问题 如何在一个集合中找到最大或最小的 N 个元素&#xff1f; 解决方案 使用 heapq 模块。 pip install heapqheapq 模块中&#xff0c;有 nlargest() 以及 nsmallest() 两个函数&#xff1a; import heapqnums [1, 8, 23, 2, 7, -4, 8, 18, 42, …

Matlab中% note that Wilkinson notation (‘L1~L4~1‘) is used to specify the model

fitrm 函数的输入参数不正确&#xff0c;似乎出错的地方是在定义 fitrm 对象时使用了不正确的参数。 fitrm 函数的语法是这样的&#xff1a; rm fitrm(tbl, model, WithinDesign, withinDesign) 其中&#xff1a; - tbl 是一个表格&#xff0c;包含了待分析的数据。 - mod…

WPF使用Prism实现简单订餐系统

新建wpf项目&#xff0c;nuget引入Prism.DryIoc&#xff0c;MaterialDesignThemes 引入后&#xff0c;修改App.xaml 前台引入 xmlns:prism"http://prismlibrary.com/"和prism:PrismApplication App.xaml.cs App.xaml.cs继承PrismApplication&#xff0c;重写CreateS…

这款AI绘画软件,带你快速生成高质量产品效果图!

前言 随着人工智能技术的飞速发展&#xff0c;AI在设计领域的应用越来越广泛&#xff0c;。今天&#xff0c;介绍的一款能够自动生成高质量产品效果图的AI绘画软件——STARTAI。这款软件以其强大的功能和便捷的操作&#xff0c;正在重新定义电商产品效果图的制作流程。 AI局部…

nginx学习记录-防盗链

1. 防盗链的概念 防盗链&#xff0c;顾名思义就是防止盗取链接&#xff0c;这里的链接一般是资源链接。 如图所示&#xff0c;我们访问一个网站时&#xff08;比如百度&#xff09;&#xff0c;我们第一个请求会获得一个html页面&#xff0c;页面中包含各种资源链接&#xff0…

LeetCode题练习与总结:平衡二叉树--110

一、题目描述 给定一个二叉树&#xff0c;判断它是否是平衡二叉树。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;true示例 2&#xff1a; 输入&#xff1a;root [1,2,2,3,3,null,null,4,4] 输出&#xff1a;false示例 3&#xff1a…

第十三章 进程与线程

第十三章 进程与线程 程序与进程的概念 程序&#xff1a; 英文单词为Program&#xff0c;是指一系列有序指令的集合&#xff0c;使用编程语言所编写&#xff0c;用于实现一定的功能。 进程&#xff1a; 进程则是指启动后的程序&#xff0c;系统会为进程分配内存空间。 函数式…

汇编小习题

汇编你还想及格&#xff1f; 未完待续 1. 注释下面代码 Title Hello Word .386 ;使用80386指令集 .model flat, stdcall;内存模型为平坦模型&#xff0c;子程序采用stdcall约定 .stack 4096;为运行时堆栈分配4096空间 ExitProcess proto , dwExitCode:DWORD;为过程创建过程原…

超好用的加密工具

超好用的加密工具 背景 介于行业原因经常要对相关文件进行加密传输&#xff0c;尽可能避免文件的泄漏&#xff0c;保护群众的隐私。于是我就开发了一个非常好用的加密工具。 环境 本工具目前只适用 Windows 操作系统,最好是Windows8以上&#xff0c;否则需要下载额外的依赖…

门限签名技术

私钥永远不存在 (t, n) 门限签名方案 (TSS) 是一种密码协议&#xff0c;允许一组 n 个参与者共同签署文档或交易&#xff0c;其中 t 1 个或更多参与者的任何子集都可以产生有效签名&#xff0c;但 t 的子集 或更少的人不能。 该方案旨在通过确保没有任何一方可以单方面生成签名…

Paddle使用问题No module named ‘paddle.fluid’

这是Paddle版本的问题&#xff0c;从飞桨框架 2.5 版本开始&#xff0c;已经废弃了 paddle.fluid 。 ​解决方案&#xff1a;修改paddle版本 pip install paddlepaddle2.4.0

Prompt工程与实践

Prompt工程与实践 一、Prompt与大模型 1.1 大模型的定义 大模型本质上就是一个概率生成模型&#xff0c;该模型的模型参数足够大&#xff0c;并且在训练过程中阅读了非常多的各个领域的语料。这个时候&#xff0c;如果通过一个正确的、有效的指令去引导这个模型&#xff0c;…

算法-从归并排序到归并分治

文章目录 前言介绍1 . 简单的归并排序2 . 数组的最小和问题3 . 逆序数对问题4 . 翻转对数量的计算 前言介绍 归并排序是Merge sort&#xff09;是一种有效、稳定的排序算法&#xff0c;它采用了分治法&#xff08;Divide and Conquer&#xff09;的典型应用,何为分治 ? 即把多…

【Linux】Linux基本指令2

我们接着上一篇&#xff1a;http://t.csdnimg.cn/bSJx8 我们接着完善ls指令 我们可以直接匹配对应格式的文件匹配出来 1.man指令&#xff08;重要&#xff09;&#xff1a; Linux的命令有很多参数&#xff0c;我们不可能全记住&#xff0c;我们可以通过查看联机手册获取帮助…

【贪心算法】C++解决回文串、增减字符串匹配、分发饼干、跳跃游戏、加油站问题

1. 前言 贪心算法&#xff08;Greedy Algorithm&#xff09;是一种在每一步选择中都采取当前状态下最优决策的算法。贪心算法通常用来解决最优化问题&#xff0c;其核心思想是通过局部最优解逐步推导出全局最优解。 在贪心算法中&#xff0c;我们并不总是考虑到未来可能发生的…

【PingPong_注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞 …

如何下载b站(哔哩哔哩bilibili)的学习视频教程

方法1&#xff1a; 打开粘贴视频链接下载即可哔哩哔哩(bilibili)视频解析下载 - 保存B站视频到手机、电脑哔哩哔哩高清视频解析下载工具是一个免费的B站视频在线解析提取工具,支持提取B站APP和bilibili网站上的任何视频,提取出来的视频无水印.我们可以借助此下载器方便地将视频…