C++之List模拟实现

目录

list的逻辑结构

构造函数

拷贝构造函数

赋值运算符重载

返回迭代器的初始位置

返回迭代器的最终位置

 元素的插入

头插

尾插 

删除元素

头删

尾删

清空整个链表

析构函数

正向迭代器 

反向迭代器

整体代码 


 上期我们学写了list的基本操作,本期我们将学习list的模拟实现。

list的逻辑结构

list是一个带头结点的双向循环链表。

构造函数

	list(){_head = new Node;_head->_next = _head;_head->_prev = _head;}

 这是一个无参的构造函数。

        template<class inputiterator>list(inputiterator begin, inputiterator end){_head = new Node;_head->_next = _head;_head->_prev = _head;while (begin != end){push_back(*begin);++begin;}}

这是用迭代器区间进行初始化的构造函数。

		list(int n, const T& data = T()){_head = new Node;_head->_next = _head;_head->_prev = _head;while (n != 0){push_back(data);n--;}}

 初始化链表的n个节点,使它们的每个结点的数据为data的值。

拷贝构造函数

	    list(const list<T>& lt){_head = new Node;_head->_next = _head;_head->_prev = _head;list<T> tmp(lt.begin(), lt.end());std::swap(_head, tmp._head);}

这是拷贝构造函数的现代写法,通过使用迭代器区间构造函数构造了一个list的临时对象,然后交换了临时对象和当前对象的头结点指针,前提是得保证当前结点的头指针不为空,不然到时候调用析构函数时会将同一空间释放两次,导致出错。

赋值运算符重载

	    list<T>& operator= (const list<T>lt){std:swap(_head,lt._head);return *this;}

 先通过拷贝构造函数生成了一个临时对象,然后再交换了临时对象和当前对象的头结点指针。

返回迭代器的初始位置

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

迭代器的初始位置就是头结点的下一位置。

返回迭代器的最终位置

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

迭代器的最终位置为最后一个元素的下一位置,即头结点的位置。

 元素的插入

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

 表示在当前位置之前插入一个元素,但是当前位置必须为迭代器类型。

元素的插入会导致迭代器失效吗?
不会,因为插入元素之后会及时更改迭代器中节点指针的指向。 

头插

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

在头节点之前插入元素,复用了插入函数。 

尾插 

	    void push_back(const T& x){insert(end(), x);}

在最后一个元素的后面插入元素,复用了插入函数。

删除元素

		iterator erase(iterator pos){//前提是不能删除掉头结点assert(pos != end());Node* prev =pos._node->_prev;Node* next = pos._node->_next;delete pos._node;pos._node = nullptr;prev->_next = next;next->_prev = prev;return iterator(next);}

删除某一位置之后,返回的是当前位置的下一位置的迭代器。 

元素的删除会导致迭代器失效吗?
会,因为删除掉当前位置之后,当前迭代器的节点指针会被释放。迭代器的节点都被释放了,所以迭代器自然会失效。

头删

     	void pop_back(){erase(--end());}

尾删

	    void pop_back(){erase(--end());}

清空整个链表

		void clear(){iterator it = begin();while (it != end()){erase(it);it++;}}

删除除头结点之外的所有节点。 

析构函数

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

析构函数会清空整个链表的信息,所以头结点指针也会被释放。

正向迭代器 

正向迭代器,其实就是从前往后进行链表元素的遍历。

	template<class T,class Ref,class Ptr>struct list_iterator{typedef ListNode<T> Node;typedef list_iterator<T, Ref, Ptr> self;//构造函数list_iterator<T,Ref,Ptr>(Node* node):_node(node){}//解引用操作Ref operator*()    {return _node->_data;}//->操作,返回迭代器中的节点指针指向的数据的地址Ptr 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;}//后置--self operator--(int){self tmp(*this);_node = _node->_prev;return tmp;}//赋值运算符重载,浅拷贝self& operator=(const self& iterator){_node = iterator._node;return *this;}//判断是否相等bool operator ==(const self& iterator) const{return _node == iterator._node;}bool operator !=(const self& iterator) const{return _node != iterator._node;}Node* _node;};

list的迭代器和vector的迭代器是不同的,vector的迭代器类型是指针类型,即内置类型。而list的迭代器类型是自定义类型。

反向迭代器

反向迭代器即从后往前进行链表元素的遍历。

namespace yjd {template<class iterator,class Ref,class Ptr>class reverse_iterator{public:typedef reverse_iterator<iterator, Ref, Ptr> self;//构造函数reverse_iterator(iterator it):_it(it){}//解引用操作Ref operator*(){iterator prev = _it;return *(--prev);}//->Ptr operator->(){iterator prev = _it;return &(--prev);}//++self& operator++(){_it--;return *this;}//--self& operator--(){_it++;return *this;}//判断是否相等bool  operator!=(const self & rit) const{return  _it != rit._it;}private:iterator _it;};}

正向迭代器和返现迭代器的开始和结束位置刚好是相对的。反向迭代器的本质仍然是正向迭代器。反向迭代器的++为正向迭代器的--。

虽然说返现迭代器的开始和结束如图如上图所示,但是我们真正在访问的时候并不是访问所示位置的元素,而是访问其前一个位置的元素,比如要访问rbgin位置的元素,则要让正向迭代器进行--访问最后一个位置的元素。 

整体代码 

list.h

#pragma once
#include<assert.h>
#include"reverse_iterator.h"
namespace yjd 
{//节点类型template<class T>struct ListNode {ListNode* _next;ListNode* _prev;T _data;ListNode(const T& data=T()):_next(nullptr),_prev(nullptr),_data(data){}};//链表的迭代器类型template<class T,class Ref,class Ptr>struct list_iterator{typedef ListNode<T> Node;typedef list_iterator<T, Ref, Ptr> self;//构造函数list_iterator<T,Ref,Ptr>(Node* node):_node(node){}//解引用操作Ref operator*()    {return _node->_data;}//->操作,返回迭代器中的节点指针指向的数据的地址Ptr 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;}//后置--self operator--(int){self tmp(*this);_node = _node->_prev;return tmp;}//赋值运算符重载,浅拷贝self& operator=(const self& iterator){_node = iterator._node;return *this;}//判断是否相等bool operator ==(const self& iterator) const{return _node == iterator._node;}bool operator !=(const self& iterator) const{return _node != iterator._node;}Node* _node;};//链表类型template<class T>class list {typedef ListNode<T> Node;public:typedef list_iterator<T, T&, T*> iterator;typedef list_iterator<T, const T&, const T*> const_iterator;typedef reverse_iterator<iterator, T&, T*> reverse_iterator;typedef reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;//构造函数list(){_head = new Node;_head->_next = _head;_head->_prev = _head;}//构造函数,迭代器区间进行构造template<class inputiterator>list(inputiterator begin, inputiterator end){_head = new Node;_head->_next = _head;_head->_prev = _head;while (begin != end){push_back(*begin);++begin;}}//拷贝构造list(const list<T>& lt){_head = new Node;_head->_next = _head;_head->_prev = _head;list<T> tmp(lt.begin(), lt.end());std::swap(_head, tmp._head);}//赋值运算符重载list<T>& operator= (const list<T>lt){std:swap(_head,lt._head);return *this;}//构造函数n个datalist(int n, const T& data = T()){_head = new Node;_head->_next = _head;_head->_prev = _head;while (n != 0){push_back(data);n--;}}//尾插//void push_back(const T& data)//{//	Node* newnode = new Node(data);//	Node* tail = _head->_prev;//	tail->_next = newnode;//	newnode->_prev = tail;//	newnode->_next = _head;//	_head->_prev = newnode;//}// //rbeginreverse_iterator rbegin(){return reverse_iterator(end());}const_reverse_iterator rbegin() const{return const_reverse_iterator(end());}//rendreverse_iterator rend(){return reverse_iterator(begin());}const_reverse_iterator rend() const{return const_reverse_iterator(begin());}//beginiterator begin(){return  iterator(_head->_next);}//enditerator end(){return iterator(_head);}//beginconst_iterator begin() const{return  const_iterator(_head->_next);}//endconst_iterator end() const{return const_iterator(_head);}//元素的插入,在pos位置之前插入元素iterator insert(iterator pos, const T& x){Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(x);prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return iterator(newnode);}//list的头插和尾插,可以复用insert,插入元素之后我们更改的是节点的指针,所以不会涉及到迭代器失效void push_front(const T& x){insert(begin(), x);}void push_back(const T& x){insert(end(), x);}//链表删除某一位置元素,删除某一位置的元素,迭代器会失效吗?会失效,因为删除掉元素之后,会释放节点的指针,节点的指针都被释放了,迭代器自然也就没有意义了iterator erase(iterator pos){//前提是不能删除掉头结点assert(pos != end());Node* prev =pos._node->_prev;Node* next = pos._node->_next;delete pos._node;pos._node = nullptr;prev->_next = next;next->_prev = prev;return iterator(next);}//list的尾删头删void pop_back(){erase(--end());}void pop_front(){erase(begin());}//list整个的删除,这个删除是删除所有的有效的节点void clear(){iterator it = begin();while (it != end()){erase(it);it++;}}//析构函数~list(){clear();delete _head;_head = nullptr;}private:Node* _head;};}

reverse_iterator.h

#pragma oncenamespace yjd {template<class iterator,class Ref,class Ptr>class reverse_iterator{public:typedef reverse_iterator<iterator, Ref, Ptr> self;//构造函数reverse_iterator(iterator it):_it(it){}//解引用操作Ref operator*(){iterator prev = _it;return *(--prev);}//->Ptr operator->(){iterator prev = _it;return &(--prev);}//++self& operator++(){_it--;return *this;}//--self& operator--(){_it++;return *this;}//判断是否相等bool  operator!=(const self & rit) const{return  _it != rit._it;}private:iterator _it;};}

以上便是list模拟实现的所有知识点。

本期内容到此结束^_^

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

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

相关文章

苏东坡传-读书笔记十一

苏东坡对写作与风格所表示的意见最为清楚。他说做文章“大略如行云流水&#xff0c;初无定质&#xff0c;但常行于所当行&#xff0c;常止于所不可不止。文理自然&#xff0c;姿态横生。孔子曰&#xff1a;‘言之不文&#xff0c;行而不远。’又曰&#xff1a;‘辞达而已矣。’…

STC32G/F/8H通用无刷电机驱动板

STC32G/F/8H通用无刷电机驱动板 &#x1f4cc;相关篇《低成本STC32G8K64驱动控制BLDC开源入门学习方案》 ✨该驱动板是在上一版的基础上改版而来。这里的STC32G/F/8H所指的是封装型号为-LQFP48的STC32G8K64、STC32G12K128、STC32F12K54、STC8H8K64U。是一款兼容有感和无感设计的…

【Java算法】二分查找 下

&#x1f525;个人主页&#xff1a; 中草药 &#x1f525;专栏&#xff1a;【算法工作坊】算法实战揭秘 一.山脉数组的峰顶索引 题目链接&#xff1a;852.山脉数组的峰顶 ​ 算法原理 这段代码实现了一个查找山峰数组中峰值索引的算法。山峰数组是一个先递增后递减的数组&…

玩具营销是如何拿捏成年人钱包?

好像现在的成年人逐渐热衷于偏向年轻化&#xff0c;问问题会好奇“尊嘟假嘟”&#xff0c;饭量上的“儿童套餐”&#xff0c;娃娃机前排长队......而最突出的莫过于各类各式的玩具不断收割当代年轻人&#xff0c;除去常给大朋友们小朋友们送去玩具福利的“麦、肯”双门&#xf…

激光干涉仪可以完成哪些测量:全面应用解析

在高端制造领域&#xff0c;精度是衡量产品质量的关键指标之一。激光干涉仪作为一项高精度测量技术&#xff0c;其应用广泛&#xff0c;对于提升产品制造精度具有重要意义。 线性测量&#xff1a;精确定位的基础 激光干涉仪采用迈克尔逊干涉原理&#xff0c;实现线性测量。该…

卷积神经网络之ResNet50迁移学习

数据准备 下载狗与狼分类数据集&#xff0c;数据来自ImageNet&#xff0c;每个分类有大约120张训练图像与30张验证图像。使用download接口下载数据集&#xff0c;并自动解压到当前目录。 全是小狗的图片 另一边全是狼的图片 加载数据集 狼狗数据集提取自ImageNet分类数据集&a…

2-3个月的幼猫能吃主食冻干吗?第一次吃哪款主食冻干比较好

2-3个月的幼猫能吃冻干吗&#xff1f;一般来说&#xff0c;幼猫在2-3个月左右的离乳期就可以吃冻干了。需要注意的&#xff0c;一个是要认准主食冻干&#xff0c;零食冻干会让猫猫从小就挑食&#xff0c;以后就更不好纠正了。而且离乳期的猫猫没有了母乳的保护&#xff0c;免疫…

Open3D 点对面的ICP算法配准(精配准)

目录 一、概述 1.1核心思想 1.2实现步骤 二、代码实现 2.1关键函数 2.2完整代码 三、实现效果 3.1原始点云 3.2配准后点云 3.3计算数据 一、概述 基于点对面的ICP&#xff08;Iterative Closest Point&#xff09;配准算法是ICP的一种变体&#xff0c;它通过最小化源…

【Ty CLI】一个开箱即用的前端脚手架

目录 资源链接基础命令模板创建命令帮助选择模板开始创建开发模板 开发背景npm 发布流程问题记录模板创建超时 更新日志 资源链接 文档&#xff1a;https://ty.cli.vrteam.top/ 源码&#xff1a;https://github.com/bosombaby/ty-cli 基础命令 1. npm 全局安装 npm i ty-cli…

Zabbix Sia Zabbix 逻辑漏洞(CVE-2022-23134)

前言 CVE-2022-23134是一个中等严重度的漏洞&#xff0c;影响Zabbix Web前端。这个漏洞允许未经身份验证的用户访问setup.php文件的某些步骤&#xff0c;这些步骤通常只对超级管理员开放。利用这个漏洞&#xff0c;攻击者可以通过跳过某些步骤来重新配置Zabbix前端&#xff0c…

Redis-Jedis连接池\RedisTemplate\StringRedisTemplate

Redis-Jedis连接池\RedisTemplate\StringRedisTemplate 1. Jedis连接池1.1 通过工具类1.1.1 连接池&#xff1a;JedisConnectionFactory&#xff1a;1.1.2 test&#xff1a;&#xff08;代码其实只有连接池那里改变了&#xff09; 2. SpringDataRedis&#xff08;lettuce&#…

十五、小型电脑没有数字键及insert,怎么解决IDEA快速插入getset构造这些方法

&#x1f33b;&#x1f33b;目录 一、小型电脑没有数字键及insert&#xff0c;怎么解决IDEA快速插入getset构造这些方法 一、小型电脑没有数字键及insert&#xff0c;怎么解决IDEA快速插入getset构造这些方法 解决&#xff1a; 1.winR打开搜索 2.osk回车 屏幕就出现了这样的一…

CC7利用链分析

分析版本 Commons Collections 3.2.1 JDK 8u65 环境配置参考JAVA安全初探(三):CC1链全分析 分析过程 CC7,6,5都是在CC1 LazyMap利用链(引用)的基础上。 只是进入到LazyMap链的入口链不同。 CC7这个链有点绕&#xff0c;下面顺着分析一下利用链。 入口类是Hashtable&…

前端入门知识分享:如何在HTML或CSS文件中引用CSS文件。

阅读提示&#xff1a;本文仅仅仅适用于刚刚接触HTML和CSS的小白从业者&#xff0c;新人爱好者。自觉身份不符的老鸟们&#xff0c;尽快绕行吧&#xff01; 什么是CSS&#xff1f;什么是CSS文件。 CSS&#xff0c;全称为Cascading Style Sheets&#xff08;层叠样式表&#xff…

分布式IO模块软件配置

组态接口模块 1、打开网络视图 2、拖拽出ET200SP 3、双击ET200SP的图片&#xff0c;进入从站配置 总线适配器的组态更换 关于IO地址分配&#xff0c;需要建立好子网通信后&#xff0c;在主机上配置。 可以看到IP 和设备名 设备与控制器的Profinet连接 先找到设备名称再找…

HarmonyOS鸿蒙DevEco Studio无法连接本地模拟器

使用DevEcoStudio 5.0.3.403版本 发现无法选择模拟器 解决方法&#xff1a; 1、打开模拟器 2、关闭DevEco Studio&#xff0c;&#xff08;不要关闭模拟器&#xff09; 3、重新打开DevEco Studio。

四道经典算法JAVA

1.爬楼地 爬20个台阶的爬法:f(19)f(18) 经典斐波拉契数列问题 public class demo4 {//爬楼梯问题public static void main(String[] args) {System.out.println(getSum(20));}public static int getSum(int n) {if (n 1)return 1;if (n 2)return 2;return getSum(n - 1) …

SpringBoot:SpringBoot中如何实现对Http接口进行监控

一、前言 Spring Boot Actuator是Spring Boot提供的一个模块&#xff0c;用于监控和管理Spring Boot应用程序的运行时信息。它提供了一组监控端点&#xff08;endpoints&#xff09;&#xff0c;用于获取应用程序的健康状态、性能指标、配置信息等&#xff0c;并支持通过 HTTP …

关于Python的类的一些理解

才发现python的类对象只能调用类方法 我想使用对类对象a使用系统调用的len方法就会报错 2.类对象a是什么&#xff1f; 答&#xff1a;是所有的带有self的成员变量 举例说明&#xff1a;红色的就是a里面的东西 class A:def __init__(self,data):self.datadataself.b1self.d{a…

解读‘‘不要卷模型,要卷应用‘‘

前言 2024 年 7 月 4 日&#xff0c;世界人工智能大会暨人工智能全球治理高级别会议全体会议在上海世博中心举行。百度创始人李彦宏在产业发展主论坛上发言&#xff0c;呼吁不要卷模型&#xff0c;要卷应用。 目录 四个要点 积极的观点 不合理性 总结 四个要点 李彦宏的呼吁…