C++——list的简要介绍
list的介绍
详细请看(https://cplusplus.com/reference/list/list/?kw=list)
1.list是一个可以在常数范围内在任意位置,进行插入和删除的序列式容器,并且此容器可以前后双向迭代。
2.list的底层实质是一个双向链表结构,双向链表里每个元素的存放都互不相关,在节点中可以通过指针来指向前一个元素和后一个元素
3.相对于vector等序列式容器,list在任意位置上的插入、删除元素的效率会更高。
4.但是list与其他序列式容器相比,最大缺陷是不支持任意位置的随机访问,必须要从已知位置迭代到当前的位置,只有这样才可以进行数据的读取。
简要使用
建立及其数据的尾插
void test_list1()
{list<int> l1;l1.push_back(1);l1.push_back(2);l1.push_back(3);l1.push_back(4);l1.push_back(5);list<int>::iterator it = l1.begin();//读取需要用迭代器读取,不能用下标while (it != l1.end()){cout << *it << " ";++it;}cout << endl;for (auto e : l1){cout << e << " ";}cout << endl;
}
排序
list<int> l1;
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);
l1.push_back(5);l1.reverse();//进行了逆序
l1.sort();//默认升序//降序
greater<int> gt;
l1.sort(gt);//传入这个即可l1.sort(greater<int>());//也可以用匿名函数//升序限定范围
sort(v.begin(), v.end());
数据的拷贝
lt2.assign(v.begin(), v.end());//粘贴
去重函数
list<int> L;
L.push_back(1);
L.push_back(4);
L.push_back(3);
L.push_back(3);L.unique();
//但在注意的是,去重函数本质是用一个双指针进行删除,连续相同的会留下一个,若是多个重复数据,若不是连//续的,那么结果还是会出现重复的元素。
//——所以需要先进行排序sort
分割函数
list<int> l1, l2;
for (int i = 1; i <= 4; i++)
{l1.push_back(i);
}
for (int i = 5; i <= 8; i++)
{l2.push_back(i);
}auto it = l1.begin();
l1.splice(it, l2);//将l2中的数据,全部插入到l1的it处
list的模拟实现
现在复现list类的简要底层代码——实现的结构体逻辑和用C实现相似。
namespace bit
{template<class T>struct list_node//节点的结构,并且对节点进行初始化{T _data;list_node<T>* _next;list_node<T>* _prev;list_node(const T& x = T())
//给缺省值,由于不知道是什么类型,所以用模板名T进行位置类型变量的初始化(作为缺省)->T():_data(x),_next(nullptr),_prev(nullptr){}};template<class T>class list//链表的结构,需要一个头结点即可{typedef list_node<T> Node;public:private:Node* _head;};
}
构造函数
void empty_init()
{_head=new Node;_head->_next=_head;_head->_prev=_head;
}list()
{empty_init();
}
尾插
void push_back(const T& x)
{Node*tail=_head->_prev;Node* newnode=new Node(x);//建立一个包含x的节点tail->_next=newnode;newnode->_prev=tail;newnode->_next=_head;_head->_prev=newnode;++_size;
}
节点迭代器
倘若我们有以下的代码
list<int> l1;
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);
l1.push_back(5);list<int>::iterator it = l1.begin();
while (it != l1.end())
{cout << *it << " ";++it;
}
这样子会显示报错?这是为什么?——结构体存放的是结点,解引用出来的是一整个结构体
而且节点存放的空间不是连续存放的,所以需要写一个结构体,进行对于' 节点的指针 '的封装。
template<class T>
struct _list_iterator
{ typedef _list_iterator<T> self;typedef list_node<T> Node;Node* _node;//结构体里存放的就是节点
}
迭代器的构造
_list_iterator(Node* node)//此迭代器的本质也就是用节点的指针:_node(node)
{}
节点指针++
self& operator()
{_node=_node->next;return *this;
}
解引用获取数据
T& operator*()//解引用也要进行一个函数的封装,要的是这个数据,所以用T
{return _node->_data;
}
指针的比较
bool operator!=(const self& s)//结点之间比较,所以用迭代器的结构体名称
{return _node != s._node;
}
list类
有了迭代器之后,对于list类作补充
template<class T>
class list
{
public:typedef _list_iterator<T> iterator;iterator begin(){return _head->_next;}iterator end(){return _head;}}
插入
iterator insert(iterator pos, const T& val)//由于插入是利用节点之间的连接进行的,且需要用迭代器
{Node* cur=pos._node;Node* newnode=new Node(val);Node* prev=cur->_prev;prev->_next=newnode;newnode->_prev=prev;newnode->_next=cur;cur->_prev=newnode;++_size;return iterator(newnode);//insert插入函数的结果会返回插入节点的位置
}
删除
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;return iterator(next);//erase要返回下一个元素的指针
}
有了insert和erase后,可以方便地实现其他函数
void push_front(const T& x)//头插
{insert(begin(), x);
}
void pops_front(const T& x)//头删
{erase(begin());
}
void pops_back(const T& x)//尾删
{erase(end());
}
清理函数
void clear()
{iterator it=begin();while(it!=end()){it=erase(it);//erase会返回一个指向下一个位置的地址,用erase可以减少代码量}
}
拷贝重载/赋值拷贝
void swap(list<T>& lt)
{std::swap(_head, lt._head);std::swap(_size, lt._size);
}list<int>& operator=(list<int>& lt)
{swap(lt);return *this;
}list(const list<T>& lt)
{empty_init();for (auto e : lt){push_back(e);//直接用push_back进行数据的插入即可,不需要再作拷贝结点}
}
析构函数
~list()
{clear();delete _head;_head = nullptr;
}
完善节点指针的结构体
template<class T>//用一个迭代器的结构体进行结点的封装 ++
//struct 默认公有,但是class类需要做些声明
struct _list_iterator
{typedef list_node<T> Node;typedef _list_iterator<T> self;Node* _node;//创造一个结点_list_iterator(Node* node)//用一个结点的指针就能够造出一个迭代器:_node(node)//传入begin,就会有对应位置的一个初始化结点出来{}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;}T& operator*()//解引用也要进行一个函数的封装,要的是这个数据,所以用T{return _node->_data;}T* operator->(){return &_node->_data;}bool operator!=(const self& s)//结点之间比较,所以用迭代器的结构体名称{return _node != s._node;}bool operator==(const self& s)//结点之间比较,所以用迭代器的结构体名称{return _node == s._node;}
};
const迭代器
我们知道,若无const修饰,那么就可以进行' 读写 ',若有const修饰,那么就只能进行' 读 '。
倘若用const修饰迭代器呢?(const iterator)——err,这样子会是迭代器本身不能修改,那么应该怎么表示?
——const_iterator 重新定义的一个类型,这样本身可以修改,指向的内容不能修改。
那么可以在迭代器基础上进行修改,成为const迭代器
template<class T>//用一个迭代器的结构体进行结点的封装 ++
//struct 默认公有,但是class类需要做些声明
struct _list_const_iterator
{typedef list_node<T> Node;typedef _list_const_iterator<T> self;Node* _node;_list_const_iterator(Node* node):_node(node){}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;}//加上const修改后,迭代器里的内容就不能被修改const T& operator*()//解引用也要进行一个函数的封装,要的是这个数据,所以用T{return _node->_data;}const T* operator->(){return &_node->_data;}bool operator!=(const self& s)//结点之间比较,所以用迭代器的结构体名称{return _node != s._node;}bool operator==(const self& s)//结点之间比较,所以用迭代器的结构体名称{return _node == s._node;}
};
现在有了两个迭代器,但是这两个迭代器高度相似,仅有两个成员函数的返回值类型不同,那么有什么方法可以像模板那样子实现类型的简化呢?——同一个类模板,实例化参数不同,就是完全不同的类型。
改进
两个类归为一类
template<class T,class Ref,class Ptr>
struct _list_iterator
{typedef list_node<T> Node;typedef _list_iterator<T,Ref,Ptr> self;//模板中的类型T,可以用Ref和Ptr来分别取代//其中,T& 又可以被Ref代替 T* 可以被Ptr代替Node* _node;//创造一个结点_list_iterator(Node* node)//用一个结点的指针就能够造出一个迭代器:_node(node)//传入begin,就会有对应位置的一个初始化结点出来{}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;}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const self& s)//结点之间比较,所以用迭代器的结构体名称{return _node != s._node;}bool operator==(const self& s)//结点之间比较,所以用迭代器的结构体名称{return _node == s._node;}
};
那么对于list类,要通过两个模板参数进行相关的控制
class list
{typedef list_node<T> Node;
public:typedef _list_iterator<T,T&,T*> iterator;typedef _list_iterator<T, const T&, const T*> const_iterator;iterator begin(){return iterator(_head->_next);//两种写法}iterator end(){return _head;}const_iterator begin()const{return const_iterator(_head->_next);}const_iterator end()const{return _head;}
....//
}
相关文章:
C++——list的简要介绍
list的介绍 详细请看(https://cplusplus.com/reference/list/list/?kwlist) 1.list是一个可以在常数范围内在任意位置,进行插入和删除的序列式容器,并且此容器可以前后双向迭代。 2.list的底层实质是一个双向链表结构…...

Java自学网站推荐,专业教学快速提升
Java自学书籍推荐,很多同学在找小编要一些比较适合初学者的学习书籍,Java自学书籍可以帮助您学习和掌握Java编程语言。以下是一些常见的Java自学书籍,它们涵盖了Java的基础知识、编程技巧和应用开发等方面: 1."Java核心技术&…...

深入学习SpringCloud Alibaba微服务架构,揭秘Nacos、Sentinel、Seata等核心技术,助力构建高效系统!
课程链接: 链接: https://pan.baidu.com/s/1hRN0R8VFcwjyCTWCEsz-8Q?pwdj6ej 提取码: j6ej 复制这段内容后打开百度网盘手机App,操作更方便哦 --来自百度网盘超级会员v4的分享 课程介绍: 📚【第01阶段】课程简介:全…...

【iMessage频發软件苹果群发技术开源原创】当 APNs 发送通知到一个离线设备时,APNs 会把通知存储起来(一定的时间内),当设备上线时再递送给设备。
推荐内容IMESSGAE相关 作者✈️IMEAE推荐内容iMessage苹果推软件 *** 点击即可查看作者要求内容信息作者✈️IMEAE推荐内容1.家庭推内容 *** 点击即可查看作者要求内容信息作者✈️IMEAE推荐内容2.相册推 *** 点击即可查看作者要求内容信息作者✈️IMEAE推荐内容3.日历推 *** …...
【数据结构】_8.二叉树OJ
目录 1. 题目1:检查两棵树是否相同 2. 题目2:判断一棵树是否为另一棵树的子树 3. 题目3:翻转二叉树 4. 题目4:判断一棵树是否为平衡二叉树 5. 题目5:判断一棵树是否为对称二叉树 6. 题目6:二叉树的层序…...

酷开系统 | 酷开科技大数据,更好的与目标消费人群建立联系
众所周知,OTT的一大优势在于强曝光,能够给消费者带来强烈的视觉冲击,强化品牌认知。但是,要想达到提升品牌认知,首先要保证OTT的流量规模,实现对目标人群的有效覆盖。得年轻消费者得“天下”,年…...

无涯教程-Perl - study函数
描述 此功能需要花费额外的时间来研究EXPR,以改善在EXPR上执行的正则表达式的性能。如果省略EXPR,则使用$_。实际的速度增益可能非常小,具体取决于您希望搜索字符串的次数。 您一次只能学习一种表达式或标量。 语法 以下是此函数的简单语法- study EXPRstudy返回值 此函数…...
dfs深度搜索入门之滑雪
P1434 [SHOI2002] 滑雪 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 本题我们主要使用了深度搜索和记忆化搜所。 首先我们可从任意一点开始滑行,这要求我们每一个点都进行一次深搜。但是如果每个点进行的话肯定会有许多个点重复被寻找最长滑雪长度,…...

Python程序设计——元组、集合和字典
可以使用元组存储一个固定的元素列表,使用集合存储和快速访问不重复的元素、使用字典存储键值对并使用这些关键字来快速访问元素。 一、元组 元组跟列表类似,但是元组中的元素是固定的;也就是说,一旦一个元组被创建,就无法对元组中的元素进行…...

八股文之框架篇(Spring Boot、SSM)
文章目录 Spring中的单例bean是线程安全的吗什么是AOP,项目中有没有使用到AOPSpring中的事务是如何实现的Spring中事务失效的场景有哪些Bean的生命周期Spring中的循环依赖(循环引用)SpringMVC的执行流程SpringBoot自动配置原理Spring、Spring…...

[PaddlePaddle] [学习笔记] [上] 计算机视觉(卷积、卷积核、卷积计算、padding计算、BN、缩放、平移、Dropout)
1. 计算机视觉的发展历程 计算机视觉作为一门让机器学会如何去“看”的学科,具体的说,就是让机器去识别摄像机拍摄的图片或视频中的物体,检测出物体所在的位置,并对目标物体进行跟踪,从而理解并描述出图片或视频里的场…...
【JS 贪心算法常见步骤】
贪心算法是一种解决优化问题的算法,其思想是在每一步选择中选择当前状态下最优解,从而达到全局最优解的目的。 以下是贪心算法的一些常见步骤: 将问题模型化为一个包含若干子问题的问题集合,每个子问题都有一个最优解。 对于每个…...

应用案例|基于三维机器视觉的机器人纸箱拆码垛应用解决方案
Part.1 项目背景 在现代物流和制造行业中,纸箱的拆码垛操作是一项重要且频繁的任务。传统的纸箱拆码垛工作通常由人工完成,这种方式存在劳动强度大、生产效率低以及人为操作容易导致错误等问题,严重影响物料的安全运输和质量。为了满足物流行…...
【ARM 嵌入式 编译 Makefile 系列 10 - Makefile sort 函数详细介绍】
文章目录 Makefile 函数 sort 学习Makefile 函数 sort 学习 sort 是Makefile的一个内建函数,它用于将列表中的词进行排序,并删除重复的词。sort函数的语法如下: $(sort list)list是你想要排序的单词列表。 下面是一个使用sort函数的简单示例: FOO = c b a c b a BAR =…...
Flask下载文件报错304 NOT MODIFIED
文章目录 问题描述解决方案参考文献 问题描述 前端 Vue 下下来的文件无法正常打开,大小比正常的略大一点,通过 Postman 直接调用是正常的 解决方案 由前端解决 如果响应大小比文件略大一点,从 responses 中取出关键数据再组成文件如果响应…...

AI Chat 设计模式:15. 桥接模式
本文是该系列的第十五篇,采用问答式的方式展开,问题由我提出,答案由 Chat AI 作出,灰色背景的文字则主要是我的一些思考和补充。 问题列表 Q.1 如果你是第一次接触桥接模式,那么你会有哪些疑问呢?A.1Q.2 什…...
Python批量替换Excel和Word中的关键字
一、问题的提出 有时,我们手头上有多个Excel或者Word文件,但是领导突然要求对某几个术语进行批量的修改,你是不是有要崩溃的感觉。因为这么多文件,要一个一个地打开文件,再进行批量替换修改,几个文件还好&…...

Codeforces算法心得——A. Array Coloring
大家好,我是晴天学长,确实全世界最大的算法竞赛平台有很多独特且创新的地方,后面我会持续的更新的!加油!💪💪💪 1 )A. Array Coloring 2) .算法思路 数组中的奇数个数一…...

论文阅读:《Waymo Public Road Safety Performance Data》
文章目录 1 背景2 方法2.1 数据来源2.2 碰撞数据 3 碰撞事件分析4 讨论 1 背景 这篇文章是讲waymo道路安全性能数据分析的,主要想表达的是waymo自动驾驶系统在安全上面的出色表现,以向政府、大众提高自己产品的公信力。 这篇文章分析的数据是自从2019年到…...
url中的特殊符号及特殊字符编码对照表
有些符号在URL中是不能直接传递的,如果要在URL中传递这些特殊符号,那么就要使用他们的编码了。 编码的格式为:%加字符的ASCII码,即一个百分号%,后面跟对应字符的ASCII(16进制)码值。例如 空格的…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...

在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...