list~模拟实现

目录

list的介绍及使用

list的底层结构

节点类的实现

list的实现

构造函数

拷贝构造

方法一:方法二:

 析构函数

赋值重载

insert  /  erase

push_/pop_(尾插/尾删/头插/头删)

begin和end(在已建立迭代器的基础上)

迭代器实现

迭代器类模板参数说明

list包含在头文件

 部分摘自— list模拟实现


list的介绍及使用

list的介绍
1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向 其前一个元素和后一个元素。ps—>2
3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。
4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list 的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间 开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这 可能是一个重要的因素)

list的底层结构

ps—>2

list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向 其前一个元素和后一个元素


节点类的实现

但我们要去实现list,就首先需要定义实现好一个结点类。

而一个结点需要存储:

需要存储的数据、前驱节点、后继节点的地址。

这里用struct定义这个节点类不用class,因为很多地方都要用到和访问这个节点类,成员变量就都共有。

这里也不需要专门单独写一个析构函数,因为这个节点类并没有对应的资源需要清理;

template <class T>
struct ListNode
{ListNode<T>* _prev;ListNode<T>* _next;T _data;ListNode(const T& data = T()):_prev(nullptr), _next(nullptr), _data(data){}
};

list的实现

链表其实就是用一个指向头节点的指针管理起来的,所以,我们可以定义一个list类,它的成员变量是一个指向头节点的指针,因为,list的底层是一个带头双向循环链表,所以这里的指针,应该指向,带有哨兵位的头节点。

template<class T>
class list
{
public:/*typedef ListNode<T> Node;typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;list(){_head = new Node();_head->_prev = _head;_head->_next = _head;}iterator begin(){return iterator(_head->_next);}iterator end(){return iterator(_head);}*/private:Node* _head;
};

构造函数

ist是一个带头双向循环链表,在构造一个list对象时,首先要申请一个头结点,并让其前驱指针和后继指针都指向自己。

list()
{_head = new node; //申请头结点_head->_next = _head; //头结点的后继指针指向自己_head->_prev = _head; //头结点的前驱指针指向自己
}


拷贝构造

拷贝构造有两种方式:        ①用一个list构造方法二        ②迭代器区间

为了方便起见,我们可以先封装手撕一个empty()函数,去申请头节点,生成一个空链表,在构造和拷贝构造的时候,可以更加方便地调用。

void empty()
{_head = new Node;_head->_prev = _head;_head->_next = _head;
}

方法一:

已经申请一个头结点,并让其前驱指针和后继指针都指向自己,然后将所给容器当中的数据,通过遍历的方式一个个尾插到新构造的容器后面即可。

list(const list<T>& lt)
{empty();for (const auto& e : lt){push_back(e);}
}

方法二:

我们先实现一个使用迭代器区间构造的函数。

template <class Iterator>
list(Iterator first, Iterator last)
{empty;//不加会出问题while (first != last){push_back(*first);++first;}
}

然后,我们先创建一个临时对象让他利用被拷贝对象的迭代器构造出来,然后再交换,被利用完后的临时对象会在栈帧结束后被清除。

我们先实现swap函数,很简单,交换头节点的指针就可以。

void swap(list<T>& tmp)
{std::swap(_head, tmp._head);
}
list(const list<T>& lt)
{empty();list<T> tmp(lt.begin(), lt.end());swap(tmp);
}


 析构函数

我们可以调用clear函数清理容器当中的数据,然后将头结点释放,最后将头指针置空即可

void clear()//将头结点释放
{iterator it = begin();while (it != end()){//it = erase(it);erase(it++);}
}
~list()
{clear();//将头结点释放delete _head;//最后将头指针置空即可_head = nullptr;
}


赋值重载

通过编译器自动调用list的拷贝构造函数构造出来一个list对象,然后调用swap函数将原容器与该list对象进行交换即可。

list<T>& operator=(list<T> tmp)
{swap(tmp);return *this;
}

insert  /  erase

insert函数的作用时在指定迭代器的位置之前插入一个数据。

iterator insert(iterator pos, const T& x)
{Node* node = pos._node;//记录当前节点Node* prev = node->_prev;//记录前驱节点Node* newnode = new Node(x);//记录要插入节点newnode->_prev = prev;newnode->_next = node;prev->_next = newnode;node->_prev = newnode;return iterator(newnode);
}

erase函数用来删除指定迭代器位置的数据。

iterator erase(iterator pos)
{assert(pos != end());//防止删除头节点Node* del = pos._node;//记录要删除节点Node* prev = del->_prev;//记录前驱节点Node* next = del->_next;//记录后续节点prev->_next = next;next->_prev = prev;return iterator(next);//返回所给迭代器pos的下一个迭代器,防止迭代器失效
}

push_/pop_(尾插/尾删/头插/头删)


void push_back(const T& val) 
{ insert(end(), val); 
}void pop_back() 
{ erase(--end()); 
}void push_front(const T& val) 
{ insert(begin(), val); 
}void pop_front() 
{ erase(begin()); 
}


begin和end(在已建立迭代器的基础上)

begin函数返回的是第一个有效数据的迭代器,end函数返回的是最后一个有效数据的下一个位置的迭代器。

iterator begin()
{return iterator(_head->_next);
}const_iterator begin() const
{return const_iterator(_head->_next);
}iterator end()
{return iterator(_head);
}const_iterator end() const
{return const_iterator(_head);
}


迭代器实现

对于list来说,它的数据不是连续存储的而是通过一个一个节点通过指针连接到一起的,所以,_head++并不能到下一个数据的位置,但是可以通过_head = _head->_next实现,所以这里我们可以使用节点的指针单独封装一个类,通过运算符重载模拟指针的行为。

(vector迭代器可以使用原生指针来实现,因为vector的空间时连续的,可以直接支持运算符++的重载,_start时指向第一个数据的指针,_start++就指向下一个位置了。

迭代器类模板参数说明

template<class T, class Ref, class Ptr>

 在list的模拟实现当中,我们typedef了两个迭代器类型,普通迭代器和const迭代器

typedef _list_iterator<T, T&, T*> iterator;
typedef _list_iterator<T, const T&, const T*> const_iterator;

所以,迭代器类的模板参数列表当中的RefPtr分别代表的是解引用类型指针类型

当我们使用普通迭代器时,编译器就会实例化出一个普通迭代器对象;当我们使用const迭代器时,编译器就会实例化出一个const迭代器对象。

template <class T,class Ref,class Ptr>
struct ListIterator
{typedef ListNode<T> Node;Node* _node;typedef ListIterator<T,Ref,Ptr> self;ListIterator(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;}Ref operator*(){return _node->_data;}bool operator!=(const self& node){return _node != node._node;}bool operator==(const self& node){return _node == node._node;}Ptr operator->(){return &_node->_data;}
};


struct Pos{int _row;int _col;Pos(int row = 0, int col = 0):_row(row),_col(col){}};void test_list2(){list<Pos> lt1;lt1.push_back(Pos(100, 100));lt1.push_back(Pos(200, 200));lt1.push_back(Pos(300, 300));list<Pos>::iterator it = lt1.begin();while (it != lt1.end()){//cout << (*it)._row << ":" << (*it)._col << endl;// 为了可读性,省略了一个->cout << it->_row << ":" << it->_col << endl;//cout << it->->_row << ":" << it->->_col << endl;cout << it.operator->()->_row << ":" << it.operator->()->_col << endl;++it;}cout << endl;}


list包含在头文件

因为list类是一种模板,不建议list内部内容函数声明定义分开来写,所以都包含在同一个文件当中

#pragma once
#include<assert.h>namespace bit
{template<class T>struct ListNode{ListNode<T>* _next;ListNode<T>* _prev;T _data;ListNode(const T& data = T()):_next(nullptr),_prev(nullptr),_data(data){}};template<class T, class Ref, class Ptr>struct ListIterator{typedef ListNode<T> Node;typedef ListIterator<T, Ref, Ptr> Self;Node* _node;ListIterator(Node* node):_node(node){}// ++it;Self& operator++(){_node = _node->_next;return *this;}Self& operator--(){_node = _node->_prev;return *this;}Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}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& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}};//template<class T>//class ListConstIterator//{//	typedef ListNode<T> Node;//	typedef ListConstIterator<T> Self;//	Node* _node;//public://	ListConstIterator(Node* node)//		:_node(node)//	{}//	// ++it;//	Self& operator++()//	{//		_node = _node->_next;//		return *this;//	}//	Self& operator--()//	{//		_node = _node->_prev;//		return *this;//	}//	Self operator++(int)//	{//		Self tmp(*this);//		_node = _node->_next;//		return tmp;//	}//	Self& operator--(int)//	{//		Self tmp(*this);//		_node = _node->_prev;//		return tmp;//	}//	//*it//	const T& operator*()//	{//		return _node->_data;//	}//	const T* operator->()//	{//		return &_node->_data;//	}//	bool operator!=(const Self& it)//	{//		return _node != it._node;//	}//	bool operator==(const Self& it)//	{//		return _node == it._node;//	}//};template<class T>class list{typedef ListNode<T> Node;public:// 不符合迭代器的行为,无法遍历//typedef Node* iterator;//typedef ListIterator<T> iterator;//typedef ListConstIterator<T> const_iterator;typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;iterator begin(){//iterator it(_head->_next);//return it;return iterator(_head->_next);}const_iterator begin() const{return const_iterator(_head->_next);}iterator end(){return iterator(_head);}const_iterator end() const{return const_iterator(_head);}list(){_head = new Node();_head->_next = _head;_head->_prev = _head;}void push_back(const T& x){/*Node* newnode = new Node(x);Node* tail = _head->_prev;tail->_next = newnode;newnode->_prev = tail;newnode->_next = _head;_head->_prev = newnode;*/insert(end(), x);}void pop_back(){erase(--end());}void push_front(const T& x){insert(begin(), x);}void pop_front(){erase(begin());}// 没有iterator失效iterator insert(iterator pos, const T& x){Node* cur = pos._node;Node* newnode = new Node(x);Node* prev = cur->_prev;// prev  newnode  curprev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return iterator(newnode);}// erase 后 pos失效了,pos指向节点被释放了iterator erase(iterator pos){assert(pos != end());Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;return iterator(next);}private:Node* _head;};void Func(const list<int>& lt){// const iterator const 迭代器不能普通迭代器前面加const修饰// const 迭代器目标本身可以修改,指向的内容不能修改 类似const T* p list<int>::const_iterator it = lt.begin();while (it != lt.end()){// 指向的内容不能修改//*it += 10;cout << *it << " ";++it;}cout << endl;}void test_list1(){list<int> lt1;// 按需实例化(不调用就不实例化这个成员函数)lt1.push_back(1);lt1.push_back(2);lt1.push_back(3);lt1.push_back(4);lt1.push_back(5);Func(lt1);//ListIterator<int> it = lt1.begin();list<int>::iterator it = lt1.begin();while (it != lt1.end()){*it += 10;cout << *it << " ";++it;}cout << endl;for (auto e : lt1){cout << e << " ";}cout << endl;}struct Pos{int _row;int _col;Pos(int row = 0, int col = 0):_row(row),_col(col){}};void test_list2(){list<Pos> lt1;lt1.push_back(Pos(100, 100));lt1.push_back(Pos(200, 200));lt1.push_back(Pos(300, 300));list<Pos>::iterator it = lt1.begin();while (it != lt1.end()){//cout << (*it)._row << ":" << (*it)._col << endl;// 为了可读性,省略了一个->cout << it->_row << ":" << it->_col << endl;//cout << it->->_row << ":" << it->->_col << endl;cout << it.operator->()->_row << ":" << it.operator->()->_col << endl;++it;}cout << endl;}void test_list4(){list<int> lt1;lt1.push_back(1);lt1.push_back(2);lt1.push_back(3);lt1.push_back(4);lt1.push_back(5);Func(lt1);lt1.push_front(10);lt1.push_front(20);lt1.push_front(30);Func(lt1);lt1.pop_front();lt1.pop_front();Func(lt1);lt1.pop_back();lt1.pop_back();Func(lt1);lt1.pop_back();lt1.pop_back();lt1.pop_back();lt1.pop_back();//lt1.pop_back();Func(lt1);}
}


 部分摘自— list模拟实现

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

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

相关文章

kafka命令--简单粗暴有效

zookeeper bin目录下执行 启动&#xff1a;./zkServer.sh start 停止&#xff1a;./zkServer.sh stop 重启&#xff1a;./zkServer.sh restart 状态&#xff1a;./zkServer.sh status kafka bin目录下执行 启动&#xff1a;./kafka-server-start.sh -daemon …/config/server.…

直播预告|手把手教你玩转 Milvus Lite !

Milvus Lite&#xff08;https://milvus.io/docs/milvus_lite.md&#xff09;是一个轻量级向量数据库&#xff0c;支持本地运行&#xff0c;可用于搭建 Python 应用&#xff0c;由 Zilliz 基于全球最受欢迎的开源向量数据库 Milvus&#xff08;https://milvus.io/intro&#xf…

使用python优雅的将PDF转为Word

使用python优雅的将PDF转为Word 先装这个优雅的库 pip install pdf2docx然后运行下面优雅的代码&#xff0c;将pdf路径和docx路径修改 from pdf2docx import Converter # path pdf_file C:\\Users\\phl\\Desktop\\软件工程期末\\软件工程模拟试题5.pdf docx_file C:\\User…

【iOS】UI学习——导航控制器、分栏控制器

UI学习&#xff08;三&#xff09; 导航控制器导航控制器基础导航控制器切换导航栏和工具栏 分栏控制器分栏控制器基础分栏控制器高级 导航控制器 导航控制器负责控制导航栏&#xff08;navigationBar&#xff09;&#xff0c;导航栏上的按钮叫UINavigationItem&#xff08;导航…

【STL源码剖析】deque 的使用

别院深深夏席清&#xff0c;石榴开遍透帘明。 树阴满地日当午&#xff0c;梦觉流莺时一声。 目录 deque 的结构 deque 的迭代器剖析 deque 的使用 ​编辑 deque 的初始化 deque 的容量操作 deque 的访问操作 在 pos 位置插入另一个向量的 [forst&#xff0c;last] 间的数据​编…

【人工智能Ⅱ】实验9:强化学习Q-Learning算法

实验9&#xff1a;强化学习Q-Learning算法 一&#xff1a;实验目的 1&#xff1a;了解强化学习的基本概念。 2&#xff1a;学习强化学习经典算法——Q-Learing算法。 3&#xff1a;通过Q-Learing算法解决问题。 二&#xff1a;实验内容 2.1 强化学习 强化学习&#xff08;…

iOS18新功能大爆料,打破常规,全面升级,这些变化不容错过!

众所周知&#xff0c;苹果 iOS 操作系统近年来都没有发生重大变化&#xff0c;主要是添加小部件、锁屏编辑和手机屏幕编辑等功能&#xff0c;再加上bug偏多&#xff0c;以至于越来越多iPhone用户不愿意再升级系统了。这一点&#xff0c;从 iOS 17 明显降低的安装率中就能看出一…

对人脸图像进行性别和年龄的判断

判断性别和年龄 导入必要的库加载预训练的人脸检测模型加载预训练的性别和年龄识别模型定义性别和年龄的标签列表创建Tkinter窗口&#xff1a;定义选择图片的函数&#xff1a;创建一个按钮&#xff0c;用于打开文件选择对话框定义显示图片的函数创建预测性别和年龄的函数创建预…

Docker大学生看了都会系列(二、Mac通过Homebrew安装Docker)

系列文章目录 第一章 Docker介绍 第二章 Mac通过Homebrew安装Docker 文章目录 前言Mac通过Homebrew安装本机环境系统要求terminal命令安装查看安装信息配置阿里云镜像加速登陆阿里云配置加速地址其他国内加速地址 总结 前言 在上一章了解了Docker容器是什么之后&#xff0c;本…

solidworks二维样条曲线使用实例

单位mm 绘制一个圆 直径为50mm&#xff0c; 基准面 上视基准面&#xff0c;距离50mm&#xff0c; 2个六边形 一个内嵌圆 另一个直径60mm&#xff0c; 将两个六边形改成构造线 选择样条曲线&#xff0c;将六边形的顶点连接在一起 放样曲面 插入–曲面–放样曲面 平面区域…

RabbitMQ不完整的笔记

同步的不足 1、拓展性差&#xff0c;当要添加功能时&#xff0c;需要在原来的功能代码上做修改&#xff0c;高耦合。 2、性能下降&#xff0c;调用者需要等待服务提供者执行完返回结果后&#xff0c;才能继续向下执行 3、级联失败&#xff0c;由于我们是基于OpenFeign调用交易…

Visual Studio Code使用(C++项目新建,运行)

VS Code 直接在官网下载安装。 接下来安装插件&#xff0c;下图是C所需的对应插件 1.新建项目 VS Code下载安装完成后&#xff0c;直接进入欢迎页&#xff1a; 在访达/文件夹中新建一个文件夹&#xff0c;欢迎页点击【打开】&#xff0c;选择刚刚新建的文件夹。点击第一个图…

华为OD机试 - 最大坐标值(Java 2024 D卷 100分)

华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试…

数据结构第三篇【链表的相关知识点一及在线OJ习题】

数据结构第三篇【链表的相关知识点一及在线OJ习题】 链表链表的实现链表OJ习题顺序表和链表的区别和联系 本文章主要讲解关于链表的相关知识&#xff0c;喜欢的可以三连喔 &#x1f600;&#x1f603;&#x1f604;&#x1f604;&#x1f60a;&#x1f60a;&#x1f643;&#…

Makefile的入门学习

一、Makefile的入门学习 编译工具及构建工具介绍 在之前的课程&#xff0c;都是直接使用gcc对代码进行编译&#xff0c;这对简单的工程是可以的&#xff0c;但当我们遇到复杂的工程时&#xff0c;每次用gcc等编译工具去操作就会显得很低效。因此make工具就出现了&#xff0c;…

LeetCode1137第N个泰波那契数

题目描述 泰波那契序列 Tn 定义如下&#xff1a; T0 0, T1 1, T2 1, 且在 n > 0 的条件下 Tn3 Tn Tn1 Tn2给你整数 n&#xff0c;请返回第 n 个泰波那契数 Tn 的值。 解析 递归应该会超时&#xff0c;可以用循环&#xff0c;或者官方解法的矩阵的幂。 public int tr…

(4) qml动态元素

文章目录 概述注意 动画元素变化的策略Animation on 变化behavior on⽤standalone animation注意 缓冲曲线&#xff08;Easing Curves&#xff09;动画分组 概述 这⼀章介绍如何控制属性值的变化&#xff0c;通过动画的⽅式在⼀段时间内来改变属性值。这项技术是建⽴⼀个现代化…

获取(复制)网页上的文字

获取&#xff08;复制&#xff09;网页上的文字 今天在搜索历史课本上一段文言文的翻译时&#xff0c;找到的网页&#xff0c;屏蔽了右键&#xff0c;不能选择&#xff0c;当然不让复制啦。对于这样的网站可以采用如下方法进行数据的获取&#xff0c;以chrome为例。 1、网页另…

keil5常见使用技巧记录(更新)

快速到函数定义 F12或自己定义快捷键CTRLK&#xff08;个人设定&#xff09; 修改快捷键 下图实例是快速跳转到函数或变量定义位置&#xff0c;当然可以定义其他功能快捷键&#xff0c;如快速注释多行&#xff0c;快速消除注释等 标记全部查找变量的蓝色框取消 CTRLshiftF2…

【YOLOv10改进[Backbone]】图像修复网络AirNet助力YOLOv10目标检测效果 + 含全部代码和详细修改方式 + 手撕结构图 + 全网首发

本文带来的是图像复原网络AirNet&#xff0c;它由基于对比度的退化编码器( CBDE )和退化引导的恢复网络( DGRN )两个模块组成。可以在一个网络中恢复各种退化图像。AirNet不受损坏类型和级别的先验限制&#xff0c;仅使用观察到的损坏图像进行推理。本文中将使用图像修复网络Ai…