一、list简介及用法

1. list简介
list是可以在常数范围内任意位置进行插入、删除、修改操作的有顺序性的容器,而且支持双向迭代,其底层是双链表结构,逻辑上连续但物理空间上不连续,只能通过指针来进行元素访问,无法使用下标随机访问。在任意位置的增删查改的实现上,list效率比vector高;在快排中,因为要取中的原因,vector支持下标随机访问,而list只能从开头或者尾部逐步迭代到对应位置,而且还需要额外的空间开销来存储节点的信息,这使得list的排序效率比vector要低。
在这里插入图片描述
2. 重要接口及使用
构造函数:
在这里插入图片描述

迭代器iterator
在这里插入图片描述

capacity
在这里插入图片描述

元素访问
在这里插入图片描述

增删查改
在这里插入图片描述

注意:在vector中进行增删操作会面临迭代器失效的风险,而在list中增加元素操作不会造成迭代器失效问题,只有删除操作会影响指向被删除元素的迭代器,并且其它的迭代器不会被影响。我们可以通过接收erase函数返回值的办法使迭代器仍然有效。

二、模拟实现

list的模拟实现有几个要点:

  1. 需要模拟出双链表的节点,所以我们需要构建节点结构体,这个结构体要包含节点的元素,指向前一个和后一个节点的该结构体的指针;
  2. 我们需要构建list的迭代器模板,这个迭代器要有非const及const类型,所以我们要用到模板,模板要有元素类型自定义,元素的const和非const的引用和指针,下面的代码中可以看到T代表元素类型,Ref代表元素引用,Ptr代表指向元素的指针;
  3. 开始构建list类,其成员变量要包含哨兵位节点和代表链表内元素个数的size,成员函数则是实现list功能的重要接口。

list的模拟实现如下:

#pragma once
#include<iostream>
#include<list>
#include<vector>
#include<algorithm>
#include<assert.h>
#include"my_reverse_iterator.h"using namespace std;// 注意:因为物理上结构的差异,list的迭代器与vector的不一样,需要封装成一个结构体并且重载引用等运算符
// 通过迭代器封装来改变迭代器的行为
// 单参数类型构造函数支持隐式类型转换
// 迭代器结构体内部不能处理节点的创建和释放template<class T>
struct _list_node
{typedef struct _list_node<T> list_node;list_node* _prev;list_node* _next;T _val;_list_node(const T& val = T()):_prev(nullptr),_next(nullptr),_val(val){}};template<class T, class Ref, class Ptr > //T代表数据类型,Ref用于区分const与非const迭代器,Ptr用于箭头运算符重载返回_val
struct _list_iterator
{typedef _list_node<T> node;typedef _list_iterator<T, Ref, Ptr> self; //self代表iterator自身node* _node;_list_iterator(node* new_node):_node(new_node){}Ref operator*(){return _node->_val;}bool operator==(const self& it){return (_node == it._node);}bool operator!=(const self& it) const{return (_node != it._node);}self operator++(int) //这里不能加引用,因为返回的是temp,{self temp(*this); //采用了拷贝构造函数_node = _node->_next;return temp;}self& operator++(){_node = _node->_next;return *this;}self operator--(int) //这里不能加引用,因为返回的是temp,{self temp(*this); //采用了拷贝构造函数_node = _node->_prev;return temp;}self& operator--(){_node = _node->_prev;return *this;}Ptr operator->() //注意:这里经过特殊处理(C++标准)it->_val即可获得值,不用it->->_val//(正常情况下返回指针得再经过解引用才能得到值){return &_node->_val; //返回地址}};template<class T>
class my_list
{
public:typedef _list_node<T> node;typedef _list_iterator<T, T&, T*> iterator;typedef _list_iterator<T, const T&, T*> const_iterator;typedef my_reverse_iterator<iterator, T&, T*> reverse_iterator;typedef my_reverse_iterator<const_iterator, T&, T*> const_reverse_iterator;private:node* _head;size_t _size;public:void empty_init(){_head = new node;_head->_next = _head;_head->_prev = _head;_size = 0;}my_list(){empty_init();}void clear() //删除链表除哨兵位外所有元素{iterator it = begin();while (it != end()){it = erase(it);}_size = 0;}~my_list(){clear();delete _head;_head = nullptr;}my_list(const my_list<T>& list) //形参必须是引用才是拷贝构造函数{empty_init();for (auto& e : list) //节省栈帧空间{push_back(e);}}void swap(my_list<T>& list){std::swap(_head, list._head);std::swap(_size, list._size);}my_list<T>& operator=(my_list<T> list){swap(list);return *this;}iterator insert(iterator pos, const T& val) //默认在pos位置前插入数据{node* new_node = new node(val);node* prev = (pos._node)->_prev;new_node->_next = pos._node;new_node->_prev = prev;(pos._node)->_prev = new_node;prev->_next = new_node;_size++;return --pos; //这里return new_node; 也可以}void push_back(const T& val){//node* new_node = new node(val); //使用了默认构造函数(系统生成,无参,全缺省)//new_node->_next = _head; //使用了赋值运算符重载//new_node->_prev = _head->_prev;//_head->_prev->_next = new_node;//_head->_prev = new_node;insert(end(), val);}void push_front(const T& val){insert(begin(), val);}iterator erase(iterator pos) //返回删除元素的后一个元素的位置{assert(pos != end()); //注意:这里要检查链表内是否只有哨兵位,不然会出问题!!!node* next = (pos._node)->_next;node* prev = (pos._node)->_prev;next->_prev = prev;prev->_next = next;//iterator temp = ++pos; //注意:pos也改变了!!!delete pos._node;pos = nullptr;_size--;return next; //函数返回值是iterator类型,为什么这里可以返回node*类型?//因为这里隐式调用了iterator的构造函数,即iterator(next)!!!//如果不希望被隐式类型转换,那么就在构造函数前用explicit修饰//return temp;}void pop_back(){iterator temp = --end();erase(temp);}void pop_front(){erase(begin());}size_t size(){return _size;}iterator begin(){//return _head->_next; //注意,返回值的类型是iterator,用node*返回是错误的!return iterator(_head->_next); //采用迭代器的构造函数来返回}iterator end(){return iterator(_head);}const_iterator begin() const{//return _head->_next; //注意,返回值的类型是iterator,用node*返回是错误的!return const_iterator(_head->_next); //采用迭代器的构造函数来返回}const_iterator end() const{return const_iterator(_head);}reverse_iterator rbegin(){return end();}const_reverse_iterator rbegin() const{return end();}reverse_iterator rend(){return begin();}const_reverse_iterator rend() const{return begin();}void print(){for (auto e : *this){cout << e << " ";}cout << endl;}
};

我们知道,list支持反向迭代,所以我们也需要模拟实现反向迭代器,以下是实现反向迭代器的一些要点:

  1. 构建反向迭代器只需复用正向迭代器即可,正向迭代器自加那么反向迭代器就自减;
  2. 在设计中,反向迭代器是正向迭代器的镜像,rbegin指向end,rend指向begin,所以在反向迭代器解引用操作符重载中,需要让指针自减再解引用;
  3. 同样地,反向迭代器模拟实现也需要用到模板,模板要实现可以自定义是哪种迭代器(vector or list?),const还是非const类型。类的成员变量仅有iterator _it。

如下是反向迭代器的模拟实现:

#pragma once
#include"my_list.h"template<class iterator, class ref, class ptr> //用普通迭代器来做反向迭代器,ref为类型引用,ptr是指针
class my_reverse_iterator
{
public:iterator _it;typedef my_reverse_iterator<iterator, ref, ptr> self;my_reverse_iterator(iterator it):_it(it){}ref operator*(){self temp = *this;return *(--temp._it);}self& operator++() //前置++{_it--;return *this;}self operator++(int) //后置++{iterator temp = _it;_it--;return temp;}self& operator--(){_it++;return *this;}self operator--(int){iterator temp = _it;_it++;return temp;}ptr operator->(){return &(operator*()); //直接返回解引用后内容的地址即可}bool operator!=(const self& it) const{return (_it != it._it);}
};

三、与vector对比

这里是引用

四、小结

本文一开始简要介绍了list的定义和重要接口,接着进行list的模拟实现,在模拟实现内容中列出了实现的数个要点,这些要点是关于list和反向迭代器的构建思路的,最后则是总结了list和vector的不同点,使读者可以根据需求来选择容器。
如有错误,请批评指正,谢谢。

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

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

相关文章

Spring Boot 参数校验:@Valid 与 @Validated

在日常开发中&#xff0c;参数校验是保障接口健壮性与数据安全的第一道防线。Spring Boot 为我们提供了基于 JSR-303/JSR-380 的强大校验机制&#xff0c;通过注解与 AOP 实现了灵活且高效的数据校验方式。本篇博客将详细介绍 Spring Boot 中 Valid、Validated 注解的使用方法&…

linux看门狗重启定位思路总结

1&#xff0c;看门狗定位思路&#xff08;1&#xff09;是否是死锁导致查看日志查看是否有RCU install或者deadlock相关打印&#xff0c;如果有的话可以考虑使用lockdep死锁检测工具&#xff08;2&#xff09;中断风暴查看中断&#xff0c;抓中断打印&#xff0c;可以查看/proc…

基于单片机直流电机测速中文液晶显示设计

摘 要 在现在工业自动化高度发展的时期&#xff0c;几乎所有的工业设备都离不开旋转设备&#xff0c;形形色色的电机在不同领域发挥着很重要的作用。不同场合对电机控制要求是不同的&#xff0c;但大部分都会涉及到旋转设备的转速测量&#xff0c;从而利用转速来实施对旋转设备…

c# sqlsugar 主子表明细 查询

在使用 SqlSugar ORM 进行数据库操作时&#xff0c;特别是在处理主子表关系时&#xff0c;通常需要执行关联查询来获取主表和其子表的数据。SqlSugar 提供了强大的查询能力&#xff0c;支持多种方式的关联查询&#xff0c;包括左连接&#xff08;Left Join&#xff09;、内连接…

研华PCI-1285/1285E 系列------(一概述)

PCI-1285/1285E 系列是基于 DSP 的 SoftMotion PCI 总线控制器卡,专为各种电机自动 化和其它机器自动化的广泛应用设计。板卡配有高性能 DSP,其中包括 SoftMotion算法,能够实现运动轨迹和时间控制,以满足精确运动中的同步应用需求。 研华 SoftMotion 支持以下特性:龙门…

二代身份证识别技术的发展:从机器学习到深度学习

一、技术发展历程1. 传统机器学习时代&#xff08;2000-2012&#xff09;特征工程方法&#xff1a;主要依赖手工设计的特征&#xff08;HOG、SIFT、LBP等&#xff09;分类器技术&#xff1a;支持向量机(SVM)、随机森林、AdaBoost等OCR技术&#xff1a;基于模板匹配和连通区域分…

云服务器如何设置防火墙和安全组规则?

一、安全组&#xff08;Security Group&#xff09;设置安全组是云平台提供的虚拟防火墙&#xff0c;用于控制 入站&#xff08;Ingress&#xff09;和出站&#xff08;Egress&#xff09;流量。1. 基本安全组规则&#xff08;推荐&#xff09;协议端口源IP用途是否必需TCP22你…

排序【各种题型+对应LeetCode习题练习】

目录 常用排序 快速排序 LeetCode 912 排序数组 归并排序 LeetCode 912 排序数组 常用排序 名称排序方式时间复杂度是否稳定快速排序分治O(n log n)否归并排序分治O(n log n)是冒泡排序交换O(n)是插入排序插入O(n)是选择排序选择最值O(n)否C STL sort快排内省排序O(n log…

鸿蒙与web混合开发双向通信

鸿蒙与web混合开发双向通信用runJavaScript和registerJavaScriptProxy web entry/src/main/resources/rawfile/1.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content&q…

unity Physics.RaycastNonAlloc

Physics.RaycastNonAlloc 是 Unity 中用于 3D 物理射线检测的高性能方法&#xff0c;它是 Physics.Raycast 的非分配版本。 方法签名 public static int RaycastNonAlloc(Ray ray, RaycastHit[] results, float maxDistance Mathf.Infinity, int layerMask DefaultRaycastLay…

数据库(five day finally)——物物而不物于物,念念而不念于念。(数据库到此结束!祝世间美好与各位不期而遇,善意常伴汝身!)

1.子查询&#xff08;1&#xff09;where 子查询①多行单列配合in和not in操作&#xff08;类似于数据范围查询&#xff09;例&#xff1a;显示工资与各个经理相同的雇员信息&#xff08;包含经理本身&#xff09;。select * from empwhere sal(select sal from emp where jobM…

【甲烷数据集】Sentinel-5P 卫星获取的全球甲烷数据集-TROPOMI L2 CH₄

目录 数据概述 传感器 & 卫星信息 监测目标:甲烷(CH₄) 数据产品内容 空间与时间覆盖 云筛选与协同观测 技术文档资源 数据下载 Python 代码绘制 CH4 数据 参考 数据概述 Sentinel-5 Precursor Level 2 Methane (TROPOMI L2 CH₄) 数据集是由欧洲哥白尼计划的 Sentinel…

【数据结构】单链表练习(有环)

1.判断是否是环形链表 141. 环形链表 - 力扣&#xff08;LeetCode&#xff09; bool hasCycle(struct ListNode *head) {struct ListNode *fast,*slow;fastslowhead;while(fast&&fast->next){fastfast->next->next;slowslow->next;if(fastslow)return tr…

VR 污水厂初体验:颠覆传统认知​

第一次戴上 VR 设备走进 VR 污水厂时&#xff0c;那种震撼的感觉至今难以忘怀。仿佛一瞬间&#xff0c;我被传送到了一个全新的世界&#xff0c;平日里只能在图纸或实地看到的污水厂&#xff0c;此刻就立体地呈现在眼前。脚下是纵横交错的管道&#xff0c;头顶巨大的处理设备有…

父类 div 自适应高度 子类如何撑满其高度

使用绝对定位 如果你想要子元素完全撑满父元素的高度&#xff0c;可以使用绝对定位。这种方法适用于当子元素需要完全覆盖父元素时。<div class"parent"><div class"child"><!-- 子类内容 --></div> </div>.parent {positio…

从0开始学习R语言--Day51--PH检验

在用cox回归做分析时&#xff0c;我们一般会得出各种变量在结局的风险影响&#xff08;HR大于1&#xff0c;就代表变量值增大&#xff0c;对应结局影响的风险就随之增大&#xff09;&#xff0c;但是这里有个坏处是&#xff0c;cox回归得到的是瞬时风险值&#xff0c;我们最多得…

Docker 网络原理

Linux 常见网络虚拟化 虚拟网卡:tun/tap虚拟网卡&#xff08;又称虚拟网络适配器&#xff09;&#xff0c;即用软件模拟网络环境&#xff0c;模拟网络适配器。在计算机网络中&#xff0c;tun 与 tap 是操作系统内核中的虚拟网络设备。不同于普通靠硬件网络适配器实现的设备&…

【通识】PCB文件

1. PCB文件的导入 在PORTEL99 PCB编辑器的文件菜单中选择导入先前绘制的CAD文件。导入成功后&#xff0c;编辑器将显示出元件封装的基本图形&#xff0c;为后续操作奠定基础。将需要抄板的PCB放置于扫描仪中随后启动扫描仪&#xff0c;之后启动AUTO CAD软件&#xff0c;之后插入…

分布式弹性故障处理框架——Polly(1)

1 前言之服务雪崩 在我们实施微服务之后&#xff0c;服务间的调用变得异常频繁&#xff0c;多个服务之前可能存在互相依赖的关系&#xff0c;当某个服务出现故障或者是因为服务间的网络出现故障&#xff0c;导致服务调用的失败&#xff0c;进而影响到某个业务服务处理失败&…

【机器学习深度学习】大模型推理速度与私有化部署的价值分析

目录 前言 一、主流推理框架速度对比 二、为什么 HuggingFace 框架更适合微调验证&#xff1f; 三、大模型私有化部署的必要性分析 ✅ 私有化部署的主要动因 1. 数据隐私与业务安全 2. 可控性与性能保障 ❌ 哪些情况不建议私有部署&#xff1f; 四、总结与选型建议 &…