C++ list介绍(迭代器失效)
一、常用接口
reverse逆置
sort排序(默认升序)
仿函数greater<int>
merge合并,可以全部合并,也可以一部分合并
unique:去重(先排序,再去重)
remove:删除e值,而不是erase的pos位置删
splice(粘接):其实就是transform(转移)
把某个位置i,转移到当前链表的某个position之前
list的sort效率很低,底层用归并,但是数据访问很低,因为是链表
vectror的sort更好一些,因为是连续的空间
二、迭代器封装问题
原生指针就是天生的迭代器(但是前提是物理空间是连续的)
为什么??
因为解引用就是该数据,++就是下一个数据
但是如果空间是不连续的就会出问题
例如list的原生指针就不能作为迭代器,因为不符合预期
因为list的原生指针式list*,但是list*++是错误的,因为不是连续的空间
解引用list*++,就是在原来地址位置,向后移动一个list类型大小的距离,指向该位置
但是因为不是连续的空间,所以,移动后的list*并不是下一个节点的地址
那怎么办呢?
改造一下
我们用一个类去封装这个原生指针,然后用这个类去控制这个原生指针
重载++list为移动到下一个节点的位置
需要处理的是这个部分
用类封装一个原生指针,这个原生指针也是一个模板
然后重定义这个原生指针为iterator
也就是说,这个itrator就是一个原生指针
这个原生指针也是一个模板
那么,当我们传入任意类型时,原生指针模板就会自动推导出其对应的指针
只是这个指针取了一个别名,叫做iterator,即迭代器
这就充分利用了类型的内涵
也就是说此处的迭代器底层还是一个指针
但是这个指针的行为不符合我们的预期
所以我们需要封装,重载行为
指针是内置类型
前置++和后置++是如何判断的呢?
因为函数重载只看参数列表,返回值不影响
所以,在后置++的重载参数列表加一个占位参数,int
这样就会区别两个函数
迭代器比较:就是比较指针,指针就是地址。地址相等,迭代器相等,否则不等
iterator的特点是不管底层是什么
三、list模拟实现(原码)
insert:
参数为iterator
找到当前的节点
记录前,后,插入即可
erase:参数pos也是iterator指针
删除节点后,当前节点的指针1iterator失效
所以要更新iterator
返回下一个节点的指针
pop_back:删除--end()
end是head,是头节点
resize:尾删和尾插
#pragma once
#include<assert.h>
#include<iostream>
using namespace std;namespace myspace
{//节点template <class T>struct list_node{list_node(const T& val = 0):_date(val), _next(nullptr), _prev(nullptr){}list_node<T>* _next;list_node<T>* _prev;T _date;};//迭代器template <class T, class Ref, class ptr>struct list_iterator{typedef list_node<T> node;typedef list_iterator<T, Ref, ptr> self;//模板推导list_iterator(node* node):_node(node){}//++itself& operator++(){_node = _node->_next;return *this;}//it++self& operator++(int){self tmp = _node;_node = _node->_next;return tmp;}//--itself& operator--(){_node = _node->_prev;return *this;}//it--self& operator--(int){self tmp = _node;_node = _node->_prev;return tmp;}//operatorT& operator*(){return _node->_date;}T* operator->(){return &_node->_date;}bool operator==(const self& it){return _node == it._node;}bool operator!=(const self& it){return _node != it._node;}node* _node;};//反向迭代器template <class T, class Ref, class ptr>struct list_reverse_iterator{typedef list_node<T> node;typedef list_reverse_iterator<T, Ref, ptr> self;//模板推导list_reverse_iterator(node* node):_node(node){}//++itself& operator++(){_node = _node->_prev;return *this;}//it++self& operator++(int){self tmp = _node;_node = _node->_prev;return tmp;}//--itself& operator--(){_node = _node->_next;return *this;}//it--self& operator--(int){self tmp = _node;_node = _node->_next;return tmp;}//operatorT& operator*(){node* tmp = _node->_prev;_node = _node->_prev;return tmp->_date;}T* operator->(){node* tmp = _node->_prev;_node = _node->_prev;return &tmp->_date;}bool operator==(const self& it){return _node == it._node;}bool operator!=(const self& it){return _node != it._node;}node* _node;};//一般对象的iterator和const对象的const_iterator//由于两者对应的实现不同,因此,一般的方式是写两个类//但是,二者的区别只有*引用和->引用两者的不同//所以,如果要书写两个类,显的臃肿//所以,可以使用模板//在需要的地方使用模板推导template <class T>class list{typedef list_node<T> node;public:typedef list_iterator<T, T&, T*> iterator;//一般对象的iteratortypedef list_iterator<T, const T&, const T*> const_iterator;//const对象的iteratortypedef list_reverse_iterator<T, T&, T*> reserve_iterator;//reserve_iteratorvoid empty_init(){node* newnode = new node;newnode->_next = newnode;newnode->_prev = newnode;this->_head = newnode;_size = 0;}void clear(){iterator it = begin();while (it != end()){it = erase(it);}}bool empty(){return size() == 0;}size_t size(){return _size;}list(){empty_init();}//lt1(lt2)//需要重新搞出一个新的list//(this指针就是lt1)list(const list<T>& lt){empty_init();for (auto& e : lt){push_back(e);}}~list(){clear();delete _head;_head = nullptr;}iterator begin(){return _head->_next;}iterator end(){return _head;}const_iterator begin() const{return _head->_next;}const_iterator end() const{return _head;}reserve_iterator rbegin(){return _head;}reserve_iterator rend(){return _head->_next;}//void swap(const list<T>& lt)//{// std::swap(_head,lt._head);// std::swap(lt._size, _size);//}void push_back(const T& val){insert(_head, val);}void push_front(const T& val){insert(begin(), val);}void pop_back(){erase(_head->_prev);}void pop_front(){erase(begin());}void insert(iterator pos, const T& val){node* tmp = new node(val);node* next = pos._node;node* prev = pos._node->_prev;prev->_next = tmp;tmp->_prev = prev;next->_prev = tmp;tmp->_next = next;++_size;}iterator erase(iterator pos){if (_size == 0)return nullptr;node* cur = pos._node;node* next = cur->_next;node* prev = cur->_prev;next->_prev = prev;prev->_next = next;delete cur;pos = nullptr;--_size;return next;}private:node* _head;size_t _size;};//打印const对象void print(const list<int>& clt){list<int>::const_iterator it = clt.begin();while (it != clt.end()){cout << *it << " ";}cout << endl;}//正常的增删改void test1(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);lt.insert(lt.begin(), 10);//lt.erase(lt.begin());lt.pop_back();lt.pop_back();lt.pop_back();lt.pop_back();lt.pop_back();lt.pop_back();list<int>::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it;}cout << endl;}void test2(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);lt.clear();list<int>::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it;}cout << lt.size() << endl;cout << endl;}void test3(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);list<int>::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it;}cout << endl;cout << lt.empty() << endl;}void test4(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);//list<int>::const_iterator it = lt.begin();//while (it != lt.end())//{// cout << *it << " ";// ++it;//}cout << endl;cout << lt.empty() << endl;}void print_list(const list<int>& clt){//const对象的迭代器//const_iterator迭代器是一个单独的对象//为了区别于一般对象,单独搞了一个const_iterator类//这个const_iterator类的目的在于,可以正常进行遍历,但是不能对内部的内容进行修改//因为实现方法不同,一个类无法实现,因此可以考虑使用模板list<int>::const_iterator it = clt.begin();while (it != clt.end()){//*it += 10;cout << *it << " ";++it;}cout << endl;}void test5(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);lt.push_front(100);//lt.erase(lt.end());lt.pop_back();lt.pop_back();lt.pop_back();lt.pop_front();list<int>::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it;}cout << endl;}void test6(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);list<int> lt2(lt);list<int> lt3;lt3.push_back(10);lt3.push_back(11);lt3.push_back(12);lt3.push_back(13);//lt3.swap(lt2);list<int>::iterator it = lt3.begin();while (it != lt3.end()){cout << *it << " ";++it;}cout << endl;}void test7(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);list<int>::reserve_iterator rlt = lt.rbegin();while (rlt != lt.rend()){cout << *rlt << " ";}cout << endl;}}
四、相关细节
什么时候用struct,什么时候用class?
数据都是共有的时候就可以使用struct
模拟实现的时候,需要定义一个自己的命名空间,防止和库内冲突
将指针类型设置为模板,因为要支持不同数据的list
typedef ListNode<T> node #意为将节点设置为模板
但是,为了便于理解,我们编写代码的时候,还是使用node,便于理解
但是实际上,这个node其实是一个模板,我们用了一个typedef宏替换实现的
创建一个新节点的时候,也是,直接new node
这样就会直接开辟一个新空间节点出来,一个模板类型的空间节点
模板的理解:很简单
就是多了一个template<class T>而已
然后将对应的东西设置为T,再typedef就是了
例如:我要将list的节点设置为模板,那么:
typedef ListNode<T> node
节点设置为模板:ListNode<T>
换名字:typedef ListNode<T> node
不要把模板看的这么复杂
也不要吧typedef看的太复杂
list中at会检查越界,[]不会检查
相关文章:

C++ list介绍(迭代器失效)
一、常用接口 reverse逆置 sort排序(默认升序) 仿函数greater<int> merge合并,可以全部合并,也可以一部分合并 unique:去重(先排序,再去重) remove:删除e值&#…...
codeforces 1809C
很巧妙的构造 题目链接 题目大意 要求构造长度为 n n n的数组满足以下条件 任意 i i i, − 1000 < a [ i ] < 1000 -1000<a[i]<1000 −1000<a[i]<1000有 k k k个和为正数的子串其余子串和为负数 思路 我们发现与子数组内元素的和有关&…...

Nginx part3 创建一个https的网站
目录 HTTPS 公钥和密钥 加密解密方式: https搭建步骤 强调一下 1、准备环境 2、配置文件 3、制作证书 4、进行设置 HTTPS 啥是https,根据百度:HTTPS (全称:Hypertext Transfer Protocol Secure)&a…...

事件高级。
一、注册事件(绑定事件) 就是给元素添加事件 注册事件有两种方式:传统方式和方法监听注册方式 1 传统注册方式 方法监听注册事件 2、 addEventListener 事件监听方式 里面的事件类型是字符串,必定加引号,而且不带o…...

Vue从入门到实战Day04
一、组件的三大组成部分(结构/样式/逻辑) 1. scoped样式冲突 默认情况:写在组件中的样式会全局生效 -> 因此很容易造成多个组件之间的样式冲突问题。 1. 全局样式:默认组件中的样式会作用到全局 2. 局部样式:可以…...

Linux学习笔记:信号
信号 在Linux中什么是信号信号的产生方式硬件产生的信号软件产生的信号异常产生的信号 进程对信号的处理信号的保存信号方法更改函数signal信号处理的更改恢复默认信号忽略 信号的管理信号集 sigset_t对信号集的操作 信号的捕捉过程 在Linux中什么是信号 在 Linux 系统中&…...
C#中的隐式类型转换和显式类型转换
在C#中,类型转换分为隐式类型转换(Implicit Type Conversion)和显式类型转换(Explicit Type Conversion),也称为隐式转换和强制转换。 隐式类型转换(Implicit Type Conversion) 隐…...
linux上如何排查JVM内存过高?
在Linux上排查JVM内存过高的问题,可以采用以下几种方法: 1. **使用top命令查看进程**:通过top命令可以观察到系统中资源占用情况,包括CPU和内存。当收到内存过高的报警时,可以使用top命令来查看是哪个进程的内存使用率…...

第四届上海理工大学程序设计全国挑战赛 J.上学 题解 DFS 容斥
上学 题目描述 usst 小学里有 n 名学生,他们分别居住在 n 个地点,第 i 名学生居住在第 i 个地点,这些地点由 n−1 条双向道路连接,保证任意两个地点之间可以通过若干条双向道路抵达。学校则位于另外的第 0 个地点,第…...

word-排版文本基本格式
1、文本的基本格式:字体格式、段落格式 2、段落:word排版的基本控制单位 3、每敲一次回车,为一个段落标记,注意区分换行符和段落标记,换行符为指向下的箭头,段落标记为带拐弯的箭头,换行符&…...
目标检测YOLO实战应用案例100讲-无监督领域自适应目标检测方法研究与应用(五)
目录 多源无监督领域自适应目标检测方法 4.1研究现状及问题形成 4.2相关工作详述...

通过python实现Google的精准搜索
问题背景: 我想通过Google或者其他网站通过精准搜索确认该产品是否存在,但是即使该产品不存在Google也会返回一些相关的url链接,现在想通过python实现搜索结果的精准匹配以确认该产品是否为正确的名称【可以通过google搜索到,如果…...

Nios-II编程入门实验
文章目录 一 Verilog实现流水灯二 Nios实现流水灯2.1 创建项目2.2 SOPC添加模块2.3 SOPC输入输出连接2.4 Generate2.5 软件部分2.6 运行结果 三 Verilog实现串口3.1 代码3.2 引脚3.3 效果 四 Nios2实现串口4.1 sopc硬件设计4.2 top文件4.3 软件代码4.4 实现效果 五 参考资料六 …...

从0开始学python(七)
目录 前言 1 break、continue和pass函数 1.1 break 1.2 continue 1.3 pass 2、序列的索引及切片操作 2.1字符串的索引和切片 2.1.1 字符串索引 2.1.2 字符串切片 总结 前言 上一篇文章我们介绍了python中的循环结构,包括for和while的使用。本章接着往下讲。…...
【二叉树算法题记录】404. 左叶子之和
题目描述 给定二叉树的根节点 root ,返回所有左叶子之和。 题目分析 其实这题无论是迭代法还是递归法,最重要的是要明确判断左叶子的条件:当前节点有左孩子,且这个左孩子没有它的左孩子和右孩子。 迭代法 感觉只要二叉树相关…...

面试集中营—Spring篇
Spring 框架的好处 1、轻量:spring是轻量的,基本的版本大约2MB; 2、IOC:控制反转,Spring的IOC机制使得对象之间的依赖不再需要我们自己来控制了,而是由容易来控制,一个字:爽…...

Lia 原理
训练阶段 论文流程: 具体实现: 通过latent space传递运动信息,实现分两部分。 1)image space->latent space 将源图像映射到隐空间编码。X_s (source image )映射到编码Z_sr,通过W_rd方向上的变化,得到新的编码Z…...

文本批量操作技巧:内容查找不再繁琐,自动化批量移动至指定文件夹
在文本处理和信息管理的日常工作中,我们经常需要处理大量的文件和数据。面对这些海量的信息,如何快速而准确地查找特定的内容,并将它们批量移动至指定的文件夹,成为了一项关键的技能。本文将介绍办公提效工具一些实用的文本批量操…...

[数据结构]动画详解单链表
💖💖💖欢迎来到我的博客,我是anmory💖💖💖 又和大家见面了 欢迎来到动画详解数据结构系列 用通俗易懂的动画的动画使数据结构可视化 先来自我推荐一波 个人网站欢迎访问以及捐款 推荐阅读 如何低…...

图片批量管理迈入智能新时代:一键输入关键词,自动生成并保存惊艳图片,轻松开启创意之旅!
在数字化时代,图片已成为我们表达创意、记录生活、传递信息的重要工具。然而,随着图片数量的不断增加,如何高效、便捷地管理这些图片,却成为了一个令人头疼的问题。 第一步,进入首助编辑高手主页面,在上方…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...

手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...

QT开发技术【ffmpeg + QAudioOutput】音乐播放器
一、 介绍 使用ffmpeg 4.2.2 在数字化浪潮席卷全球的当下,音视频内容犹如璀璨繁星,点亮了人们的生活与工作。从短视频平台上令人捧腹的搞笑视频,到在线课堂中知识渊博的专家授课,再到影视平台上扣人心弦的高清大片,音…...
OCR MLLM Evaluation
为什么需要评测体系?——背景与矛盾 能干的事: 看清楚发票、身份证上的字(准确率>90%),速度飞快(眨眼间完成)。干不了的事: 碰到复杂表格(合并单元…...