【STL】:list的模拟实现

朋友们、伙计们,我们又见面了,本期来给大家解读一下有关list的模拟实现,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!

C 语 言 专 栏:C语言:从入门到精通

数据结构专栏:数据结构

个  人  主  页 :stackY、

C + + 专 栏   :C++

Linux 专 栏  :Linux

目录

1. 基本构造

2. 正向迭代器

2.1 非const迭代器

2.2 const迭代器

2.3 正向迭代器的改进优化

3. 修改相关接口

3.1 insert、erase

3.2 尾插、头插、尾删、头删、清理

4. 拷贝构造、赋值重载、析构

5. 完整代码


1. 基本构造

list的底层其实是一个带头双向循环的链表,所以在模拟实现之前可以看一下库里面怎么实现的:

namespace ywh
{//链表结构template<class T>struct list_node{T _data;                 //节点中的数据list_node<T>* _prev;    //指向前一个节点的指针list_node<T>* _next;    //指向后一个节点的指针//构造list_node(const T& x = T()):_data(x), _prev(nullptr), _next(nullptr){}};//list结构template<class T>class list{public:typedef list_node<T> Node;public://空初始化void empty_init(){_head = new Node;_head->_prev = _head;_head->_next = _next;}//构造list(){empty_init();}private:Node* _head;  //链表的头节点size_t _size; //节点个数};
}

2. 正向迭代器

在使用list的阶段的迭代器使用方法和之前的string、vector一样,但是了解过链表的结构都知道,链表要访问下一个节点就是使用节点中的next指针指向下一个节点,需要访问里面的数据需要找到里面的data,++和解引用并不能访问链表的节点,那么迭代器的使用在底层肯定是封装加运算符重载。

在实现迭代器的时候也需要采用封装和运算符重载,运算符重载需要用到++、--、!=、==、*、->等运算符。迭代器分为const迭代器和非const迭代器,首先先来看看非const迭代器:

2.1 非const迭代器

迭代器的实现一般使用struct来进行封装:

namespace ljm
{//链表结构template<class T>struct list_node{T _data;                 //节点中的数据list_node<T>* _prev;    //指向前一个节点的指针list_node<T>* _next;    //指向后一个节点的指针//构造list_node(const T& x = T()):_data(x), _prev(nullptr), _next(nullptr){}};//非const正向迭代器template<class T>struct __list_iterator{typedef list_node<T> Node;typedef __list_iterator<T> self;Node* _node;//迭代器构造__list_iterator(Node* node):_node(node){}//前置//operator++self& operator++(){return _node->_next;}//operator--self& operator--(){return _node->_prev;}//后置self operator++(int){self* tmp(_node);_node = _node->_next;return tmp;}//operator--self operator--(int){self* tmp(_node);_node = _node->_prev;return tmp;}//operator*T& operator*(){return _node->_data;}//operator->T* operator->(){return &_node->_data;}//operator!=bool operator!=(const self& s){return _node != s._node;}//operator==bool operator==(const self& s){return _node == s._node;}};//list结构template<class T>class list{public:typedef list_node<T> Node;public://空初始化void empty_init(){_head = new Node;_head->_prev = _head;_head->_next = _head;}//构造list(){empty_init();}///正向迭代器iterator begin(){return iterator(_head->_next); //使用匿名对象进行构造}iterator end(){return iterator(_head);}private:Node* _head;  //链表的头节点size_t _size; //节点个数};
}

2.2 const迭代器

迭代器中涉及到修改的就是*和->,const迭代器的作用是指向的内容不能修改,本身不能修改,所以需要重新进行封装:

namespace ljm
{//链表结构template<class T>struct list_node{T _data;                 //节点中的数据list_node<T>* _prev;    //指向前一个节点的指针list_node<T>* _next;    //指向后一个节点的指针//构造list_node(const T& x = T()):_data(x), _prev(nullptr), _next(nullptr){}};//非const正向迭代器//...//const正向迭代器template<class T>struct __list_const_iterator{typedef list_node<T> Node;typedef __list_const_iterator<T> self;Node* _node;//迭代器构造__list_const_iterator(Node* node):_node(node){}//前置//operator++self& operator++(){_node = _node->_next;return *this;}//operator--self& operator--(){_node = _node->_prev;return *this;}//后置self operator++(int){self* tmp(_node);_node = _node->_next;return tmp;}//operator--self operator--(int){self* tmp(_node);_node = _node->_prev;return tmp;}//operator*const T& operator*(){return _node->_data;}//operator->const T* operator->(){return &_node->_data;}//operator!=bool operator!=(const self& s){return _node != s._node;}//operator==bool operator==(const self& s){return _node == s._node;}};//list结构template<class T>class list{public:typedef list_node<T> Node;typedef __list_iterator<T> iterator;typedef __list_const_iterator<T> const_iterator;public:基本构造/////空初始化void empty_init(){_head = new Node;_head->_prev = _head;_head->_next = _head;}//构造list(){empty_init();}///正向迭代器iterator begin(){return iterator(_head->_next); //使用匿名对象进行构造}iterator end(){return iterator(_head);}const_iterator begin() const{return const_iterator(_head->_next);}const_iterator end() const{return const_iterator(_head);}private:Node* _head;  //链表的头节点size_t _size; //节点个数};
}

2.3 正向迭代器的改进优化

上面的这两种写法不难看出有很多代码都是重复出现的,使得代码非常冗余,那么怎么将这些冗余的代码进行合并呢?

我们可以看一下库里面如何实现:

采用泛型编程,使用模板,将这一转化的工作交给编译器,而我们只需传递const参数和非const参数即可:

namespace ywh
{//链表结构template<class T>struct list_node{T _data;                 //节点中的数据list_node<T>* _prev;    //指向前一个节点的指针list_node<T>* _next;    //指向后一个节点的指针//构造list_node(const T& x = T()):_data(x), _prev(nullptr), _next(nullptr){}};//非const正向迭代器//   类型模板参数   传递引用      传递指针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){}//前置//operator++self& operator++(){_node = _node->_next;return *this;}//operator--self& operator--(){_node = _node->_prev;return *this;}//后置self operator++(int){self* tmp(_node);_node = _node->_next;return tmp;}//operator--self operator--(int){self* tmp(_node);_node = _node->_prev;return tmp;}//operator*Ref operator*(){return _node->_data;}//operator->Ptr operator->(){return &_node->_data;}//operator!=bool operator!=(const self& s){return _node != s._node;}//operator==bool operator==(const self& s){return _node == s._node;}};//list结构template<class T>class list{public:typedef list_node<T> Node;typedef __list_iterator<T, T&, T*> iterator;   //非const迭代器typedef __list_iterator<T, const T&, const T*> const_iterator;  //const迭代器public:基本构造/////空初始化void empty_init(){_head = new Node;_head->_prev = _head;_head->_next = _head;}//构造list(){empty_init();}///正向迭代器iterator begin(){return iterator(_head->_next); //使用匿名对象进行构造}iterator end(){return iterator(_head);}const_iterator begin() const{return const_iterator(_head->_next);}const_iterator end() const{return const_iterator(_head);}private:Node* _head;  //链表的头节点size_t _size; //节点个数};
}

3. 修改相关接口

3.1 insert、erase

在pos位置进行插入删除比较简单,需要进行链接新节点,改变节点中指针的指向即可,库里面对于插入和删除都带有返回值,那么我们在实现的时候也加上返回值:

///修改相关接口//在pos位置插入节点iterator insert(iterator pos, const T& x){//创建新新节点Node* newnode = new Node(x);//链接节点Node* cur = pos._node;Node* prev = cur->_prev;prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;//更新节点个数++_size;//返回新节点的位置return iterator(newnode);}//删掉pos位置的节点iterator erase(iterator pos){//保存相对位置Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;//链接节点delete cur;prev->_next = next;next->_prev = prev;//更新节点个数--_size;//返回pos的下一个位置return iterator(next);}

在进行insert之后可以看到迭代器是不会失效的,但是在erase之后pos位置会被释放,因此erase之后迭代器会失效,在使用前先更新迭代器。

3.2 尾插、头插、尾删、头删、清理

当实现了insert和erase之后,实现这些接口直接复用即可:

        //尾插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());}//清理void clear(){iterator it = begin();while (it != end()){it = erase(it);}}

4. 拷贝构造、赋值重载、析构

在这里我们都是采用现代写法:

 拷贝构造直接使用迭代器依次遍历进行尾插。

//拷贝构造list(const list<T>& lt){//先创建空节点empty_init();//依次尾插即可for (auto e : lt){push_back(e);}}//operator=void swap(list<T>& lt){std::swap(_head, lt._head);std::swap(_size, lt._size);}list<T>& operator=(list<T> lt){swap(lt);return *this;}//析构~list(){clear();delete _head;_head = nullptr;_size = 0;}

5. 完整代码

 头文件:List.h

#pragma oncenamespace ywh
{//链表结构template<class T>struct list_node{T _data;                 //节点中的数据list_node<T>* _prev;    //指向前一个节点的指针list_node<T>* _next;    //指向后一个节点的指针//构造list_node(const T& x = T()):_data(x), _prev(nullptr), _next(nullptr){}};//非const正向迭代器//   类型模板参数   传递引用      传递指针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){}//前置//operator++self& operator++(){_node = _node->_next;return *this;}//operator--self& operator--(){_node = _node->_prev;return *this;}//后置self operator++(int){self* tmp(_node);_node = _node->_next;return tmp;}//operator--self operator--(int){self* tmp(_node);_node = _node->_prev;return tmp;}//operator*Ref operator*(){return _node->_data;}//operator->Ptr operator->(){return &_node->_data;}//operator!=bool operator!=(const self& s){return _node != s._node;}//operator==bool operator==(const self& s){return _node == s._node;}};//list结构template<class T>class list{public:typedef list_node<T> Node;typedef __list_iterator<T, T&, T*> iterator;   //非const迭代器typedef __list_iterator<T, const T&, const T*> const_iterator;  //const迭代器public:基本构造/////空初始化void empty_init(){_head = new Node;_head->_prev = _head;_head->_next = _head;}//构造list(){empty_init();}//拷贝构造list(const list<T>& lt){//先创建空节点empty_init();//依次尾插即可for (auto e : lt){push_back(e);}}//operator=void swap(list<T>& lt){std::swap(_head, lt._head);std::swap(_size, lt._size);}list<T>& operator=(list<T> lt){swap(lt);return *this;}//析构~list(){clear();delete _head;_head = nullptr;_size = 0;}///正向迭代器iterator begin(){return iterator(_head->_next); //使用匿名对象进行构造}iterator end(){return iterator(_head);}const_iterator begin() const{return const_iterator(_head->_next);}const_iterator end() const{return const_iterator(_head);}///修改相关接口//在pos位置插入节点iterator insert(iterator pos, const T& x){//创建新新节点Node* newnode = new Node(x);//链接节点Node* cur = pos._node;Node* prev = cur->_prev;prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;//更新节点个数++_size;//返回新节点的位置return iterator(newnode);}//删掉pos位置的节点iterator erase(iterator pos){//保存相对位置Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;//链接节点delete cur;prev->_next = next;next->_prev = prev;//更新节点个数--_size;//返回pos的下一个位置return iterator(next);}//尾插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());}//清理void clear(){iterator it = begin();while (it != end()){it = erase(it);}}//节点个数size_t size(){return _size;}private:Node* _head;  //链表的头节点size_t _size; //节点个数};
}

朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持!  

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

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

相关文章

Activiti监听器

文章目录 学习链接任务监听器 TaskListener监听的事件TaskListener 接口监听器委托类DelegateTask 任务监听实现方式 — 类class绘制流程图自定义任务监听器SiteReportUserTaskListener 测试 监听实现方式 — 表达式expression绘制流程图自定义 TaskListenerExpression测试spri…

【入门Flink】- 02Flink经典案例-WordCount

WordCount 需求&#xff1a;统计一段文字中&#xff0c;每个单词出现的频次 添加依赖 <properties><flink.version>1.17.0</flink.version></properties><dependencies><dependency><groupId>org.apache.flink</groupId><…

基于前馈神经网络完成鸢尾花分类

目录 1 小批量梯度下降法 1.0 展开聊一聊~ 1.1 数据分组 1.2 用DataLoader进行封装 1.3 模型构建 1.4 完善Runner类 1.5 模型训练 1.6 模型评价 1.7 模型预测 思考 总结 参考文献 首先基础知识铺垫~ 继续使用第三章中的鸢尾花分类任务&#xff0c;将Softm…

uinapp微信小程序隐私政策授权

&#x1f680; 隐私弹窗效果图&#xff1a; 1、启用隐私相关功能在manifest.json文件中配置 usePrivacyCheck: true "mp-weixin" : {"__usePrivacyCheck__" : true, },2、创建组件 <template><view><!-- 隐私政策弹窗 --><uni-popu…

阿里云服务器怎么购买更省钱?优惠入口分享

阿里云服务器怎么购买更省钱&#xff1f;不要直接在云服务器页面购买&#xff0c;不划算&#xff0c;在阿里云特价活动上购买更优惠&#xff0c;阿腾云atengyun.com分享阿里云服务器省钱购买方法&#xff0c;节省90%&#xff0c;可以先在阿里云CLUB中心领券 aliyun.club 专用满…

Java-Hbase介绍

1.1. 概念 base 是分布式、面向列的开源数据库&#xff08;其实准确的说是面向列族&#xff09;。HDFS 为 Hbase 提供可靠的 底层数据存储服务&#xff0c;MapReduce 为 Hbase 提供高性能的计算能力&#xff0c;Zookeeper 为 Hbase 提供 稳定服务和 Failover 机制&#xff0c…

【优选算法系列】【专题五位运算】第一节.常见的位运算(面试题 01.01. 判定字符是否唯一和268. 丢失的数字)

文章目录 前言常见的位运算一、判定字符是否唯一 1.1 题目描述 1.2 题目解析 1.2.1 算法原理 1.2.2 代码编写二、丢失的数字 2.1 题目描述 2.2 题目解析 2.2.1 算法原理 2.2.2 代码编写总结 前言 常见的…

k8s之service五种负载均衡byte的区别

1&#xff0c;什么是Service&#xff1f; 1.1 Service的概念​ 在k8s中&#xff0c;service 是一个固定接入层&#xff0c;客户端可以通过访问 service 的 ip 和端口访问到 service 关联的后端pod&#xff0c;这个 service 工作依赖于在 kubernetes 集群之上部署的一个附件&a…

Spring的总结

SpringFramework 认识SpringFramework 最首先&#xff0c;我们先要认识Spring和SpringFramework两者之间有什么联系&#xff1f; 广义上的 Spring 泛指以 Spring Framework 为基础的 Spring 技术栈。 狭义的 Spring 特指 Spring Framework&#xff0c;通常我们将它称为 Spr…

找不到d3dcompiler_47.dll,无法继续执行代码,解决方法

首先&#xff0c;让我们来了解一下d3dcompiler_47.dll文件。d3dcompiler_47.dll是一个动态链接库文件&#xff0c;它是DirectX SDK中的一个重要组件&#xff0c;用于编译DirectX着色器。当我们在使用一些需要DirectX支持的软件或游戏时&#xff0c;如果缺少了这个文件&#xff…

Mysql数据库的备份和恢复及日志管理

一、数据备份概述 1.1 备份的分类 完全备份&#xff1a;整个数据库完整地进行备份 增量备份&#xff1a;在完全备份的基础之上&#xff0c;对后续新增的内容进行备份 冷备份&#xff1a;关机备份&#xff0c;停止mysql服务&#xff0c;然后进行备份 热备份&#xff1a;开机备…

java 版本企业招标投标管理系统源码+多个行业+tbms+及时准确+全程电子化

项目说明 随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大&#xff0c;公司对内部招采管理的提升提出了更高的要求。在企业里建立一个公平、公开、公正的采购环境&#xff0c;最大限度控制采购成本至关重要。符合国家电子招投标法律法规及相关规范&#xff0c;以及审…

go 语言介绍

背景 一直有在零散的时间用go写点代码&#xff0c;正好借着最近比较有时间写东西的契机&#xff0c;给这个看着年轻&#xff0c;实际也已经发展10几年&#xff0c;并在当下众多开发领域都有不可忽视作用的语言做个介绍吧 golang 的起点 golang 的诞生可以说是时代造就了它&a…

Python实现Labelme的Json标注文件与YOLO格式的TXT标注文件相互转换

Python实现Labelme的Json标注文件与YOLO格式的TXT标注文件相互转换 前言前提条件相关介绍实验环境Labelme的Json标注文件与YOLO格式的TXT标注文件相互转换convert_labelme_json_to_txtjsons/000000000009.json代码实现输出结果labels/000000000009.txt convert_txt_to_labelme_…

Docker Compose安装milvus向量数据库单机版-milvus基本操作

目录 安装Ubuntu 22.04 LTS在power shell启动milvus容器安装docker desktop下载yaml文件启动milvus容器Milvus管理软件Attu python连接milvus配置下载wget示例导入必要的模块和类与Milvus数据库建立连接创建名为"hello_milvus"的Milvus数据表插入数据创建索引基于向量…

NLP之Bert多分类实现案例(数据获取与处理)

文章目录 1. 代码解读1.1 代码展示1.2 流程介绍1.3 debug的方式逐行介绍 3. 知识点 1. 代码解读 1.1 代码展示 import json import numpy as np from tqdm import tqdmbert_model "bert-base-chinese"from transformers import AutoTokenizertokenizer AutoToken…

数据结构--前缀树(Trie)

1. 简介 前缀树是一种数据结构&#xff0c;常用来字符搜索。 2. 实现 包含的操作主要是: 加入串搜索串 代码实现&#xff0c;直接用leetcode_208的题解咯。 代码 class Trie { public:Trie():isEnd(false){for ( int i 0; i < 26;i)child[i] nullptr;}~Trie() {fo…

Webpack 中 Plugin 的作用是什么?常用 plugin 有哪些?

说说webpack中常见的Plugin&#xff1f;解决了什么问题&#xff1f;- 题目详情 - 前端面试题宝典 1、plugin 的作用 Plugin 是一种计算机应用程序&#xff0c;它和主应用程序互相交互&#xff0c;以提供特定的功能。 是一种遵循一定规范的应用程序接口编写出来的程序&#…

CSDN每日一题学习训练——Java版(两数相加、二叉树的锯齿形层序遍历、解数独)

版本说明 当前版本号[20231106]。 版本修改说明20231106初版 目录 文章目录 版本说明目录两数相加题目解题思路代码思路补充说明参考代码 二叉树的锯齿形层序遍历题目解题思路代码思路参考代码 解数独题目解题思路代码思路补充说明参考代码 两数相加 题目 给你两个 非空 的…

动态IP和静态IP哪个安全,该怎么选择

随着互联网的普及&#xff0c;越来越多的人开始关注网络安全问题。其中&#xff0c;IP地址作为网络通信中的重要组成部分&#xff0c;也成为了人们关注的焦点。 在IP地址中&#xff0c;动态IP和静态IP是两种不同的分配方式&#xff0c;它们各自具有不同的特点&#xff0c;那么…