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;}
....//
}

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

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

相关文章

jenkins 安装和通过gitee 拉取PHP项目

#jenkins 安装地址&#xff1a;https://pkg.jenkins.io/redhat-stable/sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key yum install fontconfig…

2023年国赛数学建模思路 - 复盘:光照强度计算的优化模型

文章目录 0 赛题思路1 问题要求2 假设约定3 符号约定4 建立模型5 模型求解6 实现代码 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 问题要求 现在已知一个教室长为15米&#xff0c;宽为12米&…

Thymeleaf快速入门及其注意事项

&#x1f600;前言 本篇博文是关于Thymeleaf的基本介绍&#xff0c;希望你能够喜欢&#x1f60a; &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;您的满意是我的…

Dev-C++

文章目录 介绍使用教程常用快捷键文件部分格式部分行操作跳转部分显示部分运行部分调试部分 调试流程 扩展增加编译选项开启优化显示最多警告信息生成调试信息 编译小 trick开大栈定义宏代码格式化 美化字体主题 介绍 Dev-C 是一套用于开发 C/C 程序的自由的集成开发环境&…

面向云思考安全

Gartner最近的一项研究表明&#xff0c;到 2025 年&#xff0c;85% 的企业会采用云战略&#xff0c;虽然这一数字是面向全球的&#xff0c;但可以看到在中国的环境中&#xff0c;基于云所带来的优势&#xff0c;越来越多的企业也同样开始积极向云转型。 但同时&#xff0c;有报…

BBS项目day02、注册、登录(登录之随机验证码)、修改密码、退出登录、密码加密加盐

一、注册 1.注册之前端页面 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>注册页面</title><!--动态引入文件-->{% load static %}<script src"{% static js/jquery.min.js %…

【springmvc系】利用RequestBodyAdviceAdapter做接口鉴权

需求 有个简单的需求&#xff0c;对于第三方接口我们需要做个简单的鉴权机制&#xff0c;这边使用的是非对称性加密的机制。我们提供三方公钥&#xff0c;他们通过公钥对接口json报文使用加密后的报文请求&#xff0c;我们通过对接收过来的请求某一个加密报文字段来进行RSA解密…

婚恋交友h5多端小程序开源版开发

婚恋交友h5多端小程序开源版开发 以下是婚恋交友H5多端小程序的功能列表&#xff1a; 用户注册和登录&#xff1a;用户可以通过手机号码或第三方账号注册和登录。个人信息填写&#xff1a;用户可以填写个人基本信息&#xff0c;包括姓名、性别、年龄、身高、体重、学历、职业等…

Java课题笔记~ 数据提交的方式

前四种数据注入的方式&#xff0c;会自动进行类型转换。但无法自动转换日期类型。 &#xff08;1&#xff09;单个数据&#xff08;基本数据类型&#xff09;注入 在方法中声明一个和表单提交的参数名称相同的参数&#xff0c;由框架按照名称直接注入。 &#xff08;2&#x…

微信小程序nfc指令异常记录

小程序nfc相关代码: readEvent(){wx.getNFCAdapter().startDiscovery({success:(res)>{console.log(--------------start--------)console.log(res);wx.getNFCAdapter().onDiscovered(callback>{console.log(------------onDiscovered----------)console.log(callback)…

问题:【IntelliJ IDEA】解决idea自动声明变量加finall修饰符问题

问题:【IntelliJ IDEA】解决idea自动声明变量加finall修饰符问题 场景复现 1 new String() 2 快捷方式生成变量 final修饰的 final String s new String();步骤一&#xff1a;确保settings配置信息 settings-----》Editor------》Code Style--------》java下的这两个选项不…

echarts 柱状图-折线图-饼图的基础使用

上图示例图表展示相关配置&#xff1a; var myChart echarts.init(this.$refs.firstMain);myChart.setOption({legend: { // 图例设置top: "15%",type: "scroll",orient: "vertical",//图例列表的布局朝向。left: "right",pageIconCo…

安全加密框架图——Oracle安全开发者

Oracle安全开发者 ACLs 设计 ACLs&#xff08;访问控制列表&#xff09;时&#xff0c;可以根据以下思路进行设计&#xff1a; 所有者文件权限&#xff1a;确定文件的所有者能够对文件执行哪些操作&#xff0c;如读取、写入、执行等。这可以根据文件的性质和拥有者的职责来决…

k8s集群部署vmalert和prometheusalert实现钉钉告警

先决条件 安装以下软件包&#xff1a;git, kubectl, helm, helm-docs&#xff0c;请参阅本教程。 1、安装 helm wget https://xxx-xx.oss-cn-xxx.aliyuncs.com/helm-v3.8.1-linux-amd64.tar.gz tar xvzf helm-v3.8.1-linux-amd64.tar.gz mv linux-amd64/helm /usr/local/bin…

12 注册登录

12 注册登录 整体概述 使用数据库连接池实现服务器访问数据库的功能&#xff0c;使用POST请求完成注册和登录的校验工作。 本文内容 介绍同步实现注册登录功能&#xff0c;具体涉及到流程图、载入数据库表、提取用户名和密码、注册登录流程与页面跳转的代码实现。 流程图&a…

六、Linux系统下,文件操作命令都有哪些?

总括&#xff1a; 创建文件/文件夹&#xff1a;touch&#xff1b; 查看&#xff1a;cat/more&#xff1b; 复制&#xff1a;copy&#xff1b; 移动文件/文件夹&#xff1a;mv&#xff1b; 删除&#xff1a;rm&#xff1b; 1、创建文件 &#xff08;1&#xff09;语法&#x…

docker私有仓库

# 有个远程仓库 &#xff0c;docker官方提供的 ---》我们可以把我们的镜像传上去 # 公司做的镜像&#xff0c;一般不放在远程仓库&#xff0c;公司会自己搭建私有仓库&#xff08;把公司制作的镜像传到私有仓库&#xff09; 1.镜像传到官方仓库 # 第0步&#xff1a;在远端创建…

阿里云与中国中医科学院合作,推动中医药行业数字化和智能化发展

据相关媒体消息&#xff0c;阿里云与中国中医科学院的合作旨在推动中医药行业的数字化和智能化发展。随着互联网的进步和相关政策的支持&#xff0c;中医药产业受到了国家的高度关注。这次合作将以“互联网 中医药”为载体&#xff0c;致力于推进中医药文化的传承和创新发展。…

AIGC绘画:基于Stable Diffusion进行AI绘图

文章目录 AIGC深度学习模型绘画系统stable diffusion简介stable diffusion应用现状在线网站云端部署本地部署Stable Diffusion AIGC深度学习模型绘画系统 stable diffusion简介 Stable Diffusion是2022年发布的深度学习文本到图像生成模型&#xff0c;它主要用于根据文本的描述…

UG NX二次开发(C++)-UI Styler中选择组件或者实体后设置为工作组件

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1、前言2、在NX2007中创建一个装配体实例2.1 装配体模型2.2 欲实现的功能3、创建对话框文件4、在VS2022中创建一个工程项目4.1 创建项目4.1 在hpp中添加头文件4.2 在cpp中添加代码4.3 生成dll5、测…