STL:List从0到1
🎉个人名片:
🐼作者简介:一名乐于分享在学习道路上收获的大二在校生
🙈个人主页🎉:GOTXX
🐼个人WeChat:ILXOXVJE
🐼本文由GOTXX原创,首发CSDN🎉🎉🎉
🐵系列专栏:零基础学习C语言----- 数据结构的学习之路----C++的学习之路
🐓每日一句:如果没有特别幸运,那就请特别努力!🎉🎉🎉
————————————————
🎉文章简介:
🎉本篇文章将 介绍如何使用C++编写代码来实现一个类似于STL中的List容器 相关知识进行分享!
💕如果您觉得文章不错,期待你的一键三连哦,你的鼓励是我创作动力的源泉,让我们一起加 油,一起奔跑,让我们顶峰相见!!!🎉🎉🎉
——————————————————
一.前言
这篇文章将介绍如何使用C++编写代码来实现一个类似于STL中的List容器。 list是一个可以在常数范围内在任意位置进行插入和删除的序列式容器。在这篇文章中,你将学习并实现List的常见功能,如添加元素、删除元素等。通过实现自己的List容器,你将更好地熟悉List的使用及相关特性,并提升对C++语言的理解和掌握。
————————————————
二.List的介绍
List文档介绍链接: link
1. list是一个可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代;
2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素;
3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效;
4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好(不需要挪动数据);
5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素);
三.List的结构及模拟实现
一.底层结构
List底层是一个带头双向循环链表
如图:
库里面的begin() 与end() 返回的节点位置:
begin()返回的是头节点的下一个节点;
而end()返回的是头节点;
二.List的模拟实现
重点::迭代器的实现
1. 构造函数
template<class T>
struct ListNode
{ListNode<T>(const T& x=T()) :_next(nullptr),_prev(nullptr),_val(x){}ListNode<T>* _next;ListNode<T>* _prev;T _val;
};
库里面的List类的构造函数是另写一个函数,因为这个函数拷贝构造会使用,然后复用它,所以我们也这样实现;
template<class T>
class List
{
public:void empty_List(){_phead = new node;_phead->_next = _phead;_phead->_prev = _phead;}List(){empty_List();}
}
2. 拷贝构造函数
List(const List<T>& lt)
{empty_List(); //初始化for (const auto& e : lt) {push_back(e); //将lt里面的数据依次尾插}
}
3. 插入函数
思路:记录前一个和后一个节点,然后连接
iterator insert(iterator pos, const T& x)
{node* newnode = new node(x); //构造一个节点node* next = pos._node; node* prev = next->_prev; //记录前一个newnode->_next = next; next->_prev = newnode; //链接newnode->_prev = prev;prev->_next = newnode;return pos;
}
4. 尾插函数
复用insert函数
void push_back(const T& x)
{
/* node* newnode = new node(x);node* tail = _phead->_prev;tail->_next = newnode;newnode->_prev = tail; //不复用的写法newnode->_next = _phead;_phead->_prev = newnode;*/insert(end(), x);
}
5. 头插函数
复用insert函数
void push_front(const T& x){insert(begin(),x);}
6. 删除函数
iterator erase(iterator pos)
{assert(pos!=end());node* prev = pos._node->_prev; //保存前一个节点node* next = pos._node->_next; //保存后一个节点prev->_next = next; //连接next->_prev = prev;delete pos._node; //释放掉该节点return next; //返回删除元素的下一个节点
}
7. 尾删函数
复用删除函数
void pop_back(){//erase(end()._node->_prev);erase(--end()); //头节点的前指针指向的是最后一个节点}
8. 头删函数
复用删除函数
void pop_front(){erase(begin()); }
9. 迭代器的实现
因为链表的底层物理空间不是连续的,所以不能使用原生指针类实现。因为原生指针++,可以找到下一个数据,但是链表的节点与节点之间不是连续的,指针++,不能找到下一个节点,所以我们需要操作符重载,改变 ++, != ,* 等操作符的行为;又因为节点指针是内置类型,不能进行操作符重载,所以我们只能将它进行封装,封装在一个类里面,进行重载;
template<class T>
struct __List_iterator
{typedef ListNode<T> node;typedef __List_iterator<T> self;__List_iterator(node* node) //构造函数:_node(node){}self& operator++() //运算符的重载{_node = _node->_next; //前置++,返回++后的值return *this;}self& operator++(int){self tmp(_node); //保存++前的值_node = _node->_next;return tmp; //返回++前的值}self& operator--(){_node = _node->_prev;return *this;}self& operator--(int){self tmp(_node);_node = _node->_prev;return tmp;}T& operator*(){return _node->_val;}bool operator!=(const self& s){return s._node!= this->_node;}bool operator==(const self& s){return s._node == this->_node;}node* _node;
};
10. 赋值运算符重载
传统写法:
void clear(){iterator lt = begin();while (lt != end()){lt = erase(lt);}}
//lt1=lt2
List<T>& operator=(const List<T>& lt)
{clear(); //清空函数,将链表中的有效数据删除掉,保留头节点for (const auto& e : lt){push_back(e); //依次尾插}return *this;
}
现代写法:
void swap(List<T>& lt)
{std::swap(_phead, lt->_phead);
}
//lt1=lt2
List<T>& operator=(List<T> lt) //lt是lt2的拷贝构造
{swap(lt); //交换lt与lt1return *this; //返回
}
补充知识:
typedef 放在类里面与外面的区别:
如果是放在公有里面,则类外面也可以使用,但是要指定类域;
如果是私有的话,则类外面不能使用;
三.总代码:
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;namespace L
{template<class T>struct ListNode{ListNode<T>(const T& x=T()):_next(nullptr),_prev(nullptr),_val(x){}ListNode<T>* _next;ListNode<T>* _prev;T _val;};template<class T>struct __List_iterator{typedef ListNode<T> node;typedef __List_iterator<T> self;__List_iterator(node* node):_node(node){}self& operator++(){_node = _node->_next;return *this;}self& operator++(int){self tmp(_node);_node = _node->_next;return tmp;}self& operator--(){_node = _node->_prev;return *this;}self& operator--(int){self tmp(_node);_node = _node->_prev;return tmp;}T& operator*(){return _node->_val;}bool operator!=(const self& s){return s._node!= this->_node;}bool operator==(const self& s){return s._node == this->_node;}node* _node;};template<class T>class List{public:typedef ListNode<T> node;typedef __List_iterator<T> iterator;iterator begin(){return iterator(_phead->_next);}iterator end(){return iterator(_phead);}void empty_List(){_phead = new node;_phead->_next = _phead;_phead->_prev = _phead;}List(){empty_List();}List(const List<T>& lt){empty_List();for (const auto& e : lt) //引用更好,如果T类型是自定义类型的话{push_back(e);}}//lt1=lt2List<T>& operator=(const List<T>& lt){clear();for (const auto& e : lt){push_back(e);}return *this;}void swap(List<T>& lt){std::swap(_phead, lt->_phead);}//lt1=lt2List<T>& operator=(List<T> lt){swap(lt);return *this;}void clear(){iterator lt = begin();while (lt != end()){lt = erase(lt);}}~List(){clear();delete _phead;_phead = nullptr;}void push_back(const T& x){/* node* newnode = new node(x);node* tail = _phead->_prev;tail->_next = newnode;newnode->_prev = tail;newnode->_next = _phead;_phead->_prev = newnode;*/insert(end(), x);}void push_front(const T& x){insert(begin(),x);}iterator insert(iterator pos, const T& x){node* newnode = new node(x);node* next = pos._node;node* prev = next->_prev;newnode->_next = next;next->_prev = newnode;newnode->_prev = prev;prev->_next = newnode;return pos;}iterator erase(iterator pos){assert(pos!=end());node* prev = pos._node->_prev;node* next = pos._node->_next;prev->_next = next;next->_prev = prev;delete pos._node;return next;}void pop_back(){//erase(end()._node->_prev);erase(--end());}void pop_front(){erase(begin());}private:node* _phead;};
0
相关文章:

STL:List从0到1
🎉个人名片: 🐼作者简介:一名乐于分享在学习道路上收获的大二在校生 🙈个人主页🎉:GOTXX 🐼个人WeChat:ILXOXVJE 🐼本文由GOTXX原创,首发CSDN&…...

利用高分五号02星高光谱数据进行地物识别
高分五号02星搭载了一台60公里幅宽、330谱段、30米分辨率的可见短波红外高光谱相机(AHSI),可见近红外(400~1000nm)和短波红外光谱(1000~2500nm)分辨率分别达到5纳米和10纳米。单看参数性能优越&…...
前端如何识别上传的二维码---jsQR
npm npm i -d jsqrhtml <el-button click"$refs.input.click()">识别</el-button> <input type"file" style"display: none" id"input" input"upload">js import jsQR from "jsqr";decodeQR…...

flink1.18.0 自定义函数 接收row类型的参数
比如sql中某字段类型 array<row<f1 string,f2 string,f3 string,f4 bigint>> 现在需要编写 tableFunction 需要接受的参数如上 解决方案 用户定义函数|阿帕奇弗林克 --- User-defined Functions | Apache Flink...

JDK8和JDK11在Ubuntu18上切换(解决nvvp启动报错)
本文主要介绍JDK8和JDK11在Ubuntu18上切换,以供读者能够理解该技术的定义、原理、应用。 🎬个人简介:一个全栈工程师的升级之路! 📋个人专栏:计算机杂记 🎀CSDN主页 发狂的小花 🌄人…...

基于eleiment-plus的表格select控件
控件不是我写的,来源于scui,但在使用中遇到了一些问题,希望能把过程记录下来,同时把这个问题修复掉。 在使用的时候对控件进行二级封装,比如我的一个商品组件,再很多地方可以用到,于是 <template>&l…...

「❤️万文总结 时光回忆录❤️」那年,我在北京邮电大学计算机学院求学的日子
文章目录 关于我 | About Me梦绕西土城,邮情涌流 | Dreams and Connections in Haidian 北邮求学记 | My Days at BUPT岁月如歌,追忆往昔 | Reminiscing the Fleeting Years新篇章:班级与环境 | New Class, New Surroundings高压与挑战&#…...

【四 (1)数据可视化之如何选用正确的图表】
目录 文章导航一、数据分析中可视化的作用1、揭示数据关联和模式2、支持数据分析和决策3、提升沟通和共享效果4、强调关键信息和发现5、增强故事叙述和记忆效果6、有效增强数据交互性数据7、复杂信息易理解8、数据多维度显示 二、如何选用合适的图表1、简洁性避免使用过于复杂或…...

PHP<=7.4.21 Development Server源码泄露漏洞 例题
打开题目 dirsearch扫描发现存在shell.php 非预期解 访问shell.php,往下翻直接就看到了flag.. 正常解法 访问shell.php 看见php的版本是7.3.33 我们知道 PHP<7.4.21时通过php -S开起的WEB服务器存在源码泄露漏洞,可以将PHP文件作为静态文件直接输…...

大语言模型RAG-技术概览 (一)
大语言模型RAG-技术概览 (一) 一 RAG概览 检索增强生成(Retrieval-AugmentedGeneration, RAG)。即大模型在回答问题或生成问题时会先从大量的文档中检索相关的信息,然后基于这些信息进行回答。RAG很好的弥补了传统搜索方法和大模型两类技术…...
【嵌入式DIY实例】-DIY锂电池电压检测表
DIY锂电池电压检测表 文章目录 DIY锂电池电压检测表1、直流电压检测传感器介绍2、硬件准备3、代码实现4、OLED显示在电子应用中,通常需要使用到电池,电源管理是必不可少的部分。本文将详细介绍如何使用一个0-25V的直流电压传感器来检测锂电池的电压。 1、直流电压检测传感器介…...
生成baidu.com域名的私有证书:Linux系统命令示例
在Linux系统上生成一个针对xzyxdev.prec-tech.com域名的私有证书(通常指的是自签名证书),你可以使用openssl工具。以下是一个简单的步骤和命令示例来生成这样的证书: 生成私钥 首先,你需要生成一个私钥。这通常是一个…...
小程序学习4 mock
services/home.js import { config, cdnBase } from ../../config/index;/** 获取首页数据 */ function mockFetchHome() {const { delay } require(../_utils/delay);const { genSwiperImageList } require(../../model/swiper);return delay().then(() > {return {swip…...
Unity3D MMORPG角色的UI血条管理详解
前言 在Unity3D游戏开发中,MMORPG(Massively Multiplayer Online Role-Playing Game)游戏是一种非常流行的游戏类型。在这种类型的游戏中,玩家通常可以选择不同的角色来进行游戏,而角色的血条管理是游戏中非常重要的一…...

【python】爬取杭州市二手房销售数据做数据分析【附源码】
一、背景 在数据分析和市场调研中,获取房地产数据是至关重要的一环。本文介绍了如何利用 Python 中的 requests、lxml 库以及 pandas 库,结合 XPath 解析网页信息,实现对链家网二手房销售数据的爬取,并将数据导出为 Excel 文件的过…...

Day34:安全开发-JavaEE应用反射机制攻击链类对象成员变量方法构造方法
目录 Java-反射-Class对象类获取 Java-反射-Field成员变量类获取 Java-反射-Method成员方法类获取 Java-反射-Constructor构造方法类获取 Java-反射-不安全命令执行&反序列化链构造 思维导图 Java知识点 功能:数据库操作,文件操作,…...

Transformer代码从零解读【Pytorch官方版本】
文章目录 1、Transformer大致有3大应用2、Transformer的整体结构图3、如何处理batch-size句子长度不一致问题4、MultiHeadAttention(多头注意力机制)5、前馈神经网络6、Encoder中的输入masked7、完整代码补充知识: 1、Transformer大致有3大应…...
安卓性能优化面试题 31-35
31. 简述Handler导致的内存泄露的原因以及如何解决 ?在Android开发中,Handler对象可能导致内存泄漏的主要原因是由于Handler持有对外部类对象的隐式引用,从而导致外部类无法被垃圾回收,进而引发内存泄漏。下面是导致Handler内存泄漏的几种常见情况及相应的解决方法: 1. 长…...
QML与C++通信
一、QML中如何使用C的类和对象 前提条件: 1.从 QObject 或 QObject 的派生类继承 2.使用 Q_OBJECT 宏 这两个条件是为了让一个类能够进入 Qt 强大的元对象系统(meta-object system)中,只有使用元对象系统,一个类的某些…...

Explain详解与索引优化最佳实践
Explain工具介绍 使用EXPLAIN关键字可以模拟优化器执行SQL语句,分析你的查询语句或是结构的性能瓶颈 在select语句之前增加explain关键字,MySQL会在查询前设置一个标记,执行查询会返回执行计划的信息,而不是执行这条SQL 注意: 如果from中包含子查询,仍会执行该子查询,将结果…...

RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...

微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...

MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...

渗透实战PortSwigger靶场:lab13存储型DOM XSS详解
进来是需要留言的,先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码,输入的<>当成字符串处理回显到页面中,看来只是把用户输…...
前端高频面试题2:浏览器/计算机网络
本专栏相关链接 前端高频面试题1:HTML/CSS 前端高频面试题2:浏览器/计算机网络 前端高频面试题3:JavaScript 1.什么是强缓存、协商缓存? 强缓存: 当浏览器请求资源时,首先检查本地缓存是否命中。如果命…...
Oracle实用参考(13)——Oracle for Linux物理DG环境搭建(2)
13.2. Oracle for Linux物理DG环境搭建 Oracle 数据库的DataGuard技术方案,业界也称为DG,其在数据库高可用、容灾及负载分离等方面,都有着非常广泛的应用,对此,前面相关章节已做过较为详尽的讲解,此处不再赘述。 需要说明的是, DG方案又分为物理DG和逻辑DG,两者的搭建…...