当前位置: 首页 > news >正文

list的使用和模拟实现

目录

1.list的介绍及使用

1.1 list的介绍

1.2 list的使用

1.2.1 list的构造

1.2.2 list iterator的使用

1.2.3 list capacity

1.2.4 list element access

1.2.5 list modifiers

2.为什么使用迭代器?

3.list的模拟实现

3.1完整代码

3.2代码解析

4.list与vector的对比


1.list的介绍及使用

1.1 list的介绍

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

1.2 list的使用

list中的接口比较多,此处类似,只需要掌握如何正确的使用,然后再去深入研究背后的原理,已达到可扩展的能力。以下为list中一些常见的重要接口

1.2.1 list的构造

构造函数

接口说明

list (size_type n, const value_type& val = value_type())

构造的list中包含n个值为val的元素

list ()

构造空的list

list (const list& x)

拷贝构造函数

list (InputIterator first, InputIterator last)

用两个迭代器[firs, last)区间中的元素构造list

1.2.2 list iterator的使用

此处,可暂时将迭代器理解成一个指针,该指针指向list中的某个节点

函数声明

接口说明

begin+end

返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器

rbegin+rend

返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的reverse_iterator,即begin的前一个位置

1. beginend为正向迭代器,对迭代器执行++操作,迭代器向后移动
2. rbegin(end)rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动

1.2.3 list capacity

函数声明

接口说明

empty

检查list是否为空,是返回true,否则返回false

size

返回list中有效节点的个数

1.2.4 list element access

函数声明

接口声明

front

返回list的第一个节点中值的引用

back

返回list的最后一个节点中值的引用

1.2.5 list modifiers

函数声明

接口说明

push_front

在list首元素前插入值为val的元素

pop_front

删除list中第一个元素

push_back

在list尾部插入值为val的元素

pop_back

删除list中最后一个元素

insert

在list position位置的元素

erase

删除list position位置的元素

swap

交换两个list中的元素

clear

清空list中的有效元素

2.为什么使用迭代器?

容器类使用迭代器进行访问和遍历的主要原因包括以下几点:

  1. 抽象数据结构访问接口:通过迭代器,容器类可以提供一种统一的、抽象的方法来访问和操作容器中的元素。这样,无论容器内部的数据结构是什么,用户都可以使用相同的方式来访问和操作元素,提高了代码的可复用性和可维护性。
  2. 封装容器的内部实现细节:容器的内部实现可能采用各种不同的数据结构,例如数组、链表、树等。通过迭代器,容器可以隐藏内部实现细节,只提供迭代器的接口给用户使用,从而保护容器内部数据的完整性和安全性。
  3. 支持灵活的遍历方式:迭代器提供了多种灵活的遍历方式,例如正向遍历、反向遍历、随机遍历等。这使得用户可以根据实际需求选择最适合的遍历方式,提高了代码的灵活性和效率。
  4. 方便的算法和函数库使用:许多算法和函数库都是基于迭代器的,例如STL中的算法库、boost库等。通过使用迭代器,可以方便地在容器上应用这些算法和函数,提高了开发效率和代码的重用性。

综上所述,使用迭代器可以提供一种统一的、抽象的方法来操作容器中的元素,封装容器的内部实现细节,提供灵活的遍历方式,并支持方便的算法和函数库的使用。这使得容器的访问和遍历更加方便、灵活和高效

3.list的模拟实现

3.1完整代码

//ReverseIterator.h
#define _CRT_SECURE_NO_WARNINGS 1namespace bit
{template<class Iterator, class Ref, class Ptr>//Ref表示引用类型,Ptr表示指针类型struct ReverseIterator{typedef ReverseIterator<Iterator, Ref, Ptr> Self;//重命名Iterator _it;//成员ReverseIterator(Iterator it):_it(it){}Ref operator*(){Iterator tmp = _it;return *(--tmp);}Ptr operator->(){return &(operator*());}Self& operator++(){--_it;return *this;}Self operator++(int){Self tmp = *this;--_it;return tmp;}Self& operator--(){++_it;return *this;}Self& operator--(int){Self tmp = *this;++_it;return tmp;}bool operator!=(const Self& s) const{return _it != s._it;}};
}
//list.h
#define _CRT_SECURE_NO_WARNINGS 1#pragma once#include <assert.h>
#include "ReverseIterator.h"namespace bit
{template<class T>struct list_node{list_node<T>* _next;list_node<T>* _prev;T _val;list_node(const T& val = T()):_next(nullptr), _prev(nullptr), _val(val){}};template<class T, class Ref, class Ptr>struct __list_iterator//迭代器{typedef list_node<T> Node;typedef __list_iterator<T, Ref, Ptr> self;Node* _node;__list_iterator(Node* node):_node(node){}Ref operator*(){return _node->_val;}Ptr operator->(){return &_node->_val;}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;}bool operator!=(const self& it) const{return _node != it._node;}bool operator==(const self& it) const{return _node == it._node;}};template<class T>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;typedef ReverseIterator<iterator, T&, T*> reverse_iterator;typedef ReverseIterator<const_iterator, const T&, const T*> const_reverse_iterator;reverse_iterator rbegin(){return reverse_iterator(end());}reverse_iterator rend(){return reverse_iterator(begin());}iterator begin(){//return _head->_next;//C++单参数类型支持隐式转换return iterator(_head->_next);//这两种写法是一样的}iterator end(){//return _head;//C++单参数类型支持隐式转换//虽然两种写法都是一样的,不过这种写法能更加明确的告诉我们返回的是迭代器类型的对象return iterator(_head);//匿名对象}const_iterator begin() const{//return _head->_next;//C++单参数类型支持隐式转换return const_iterator(_head->_next);//这两种写法是一样的}const_iterator end() const{return _head;}void empty_init(){_head = new Node;_head->_prev = _head;_head->_next = _head;_size = 0;}list(){empty_init();}list(const list<T>& lt){empty_init();for (auto& e : lt){push_back(e);}}void swap(list<T>& lt){std::swap(_head, lt._head);std::swap(_size, lt._size);}list<T>& operator=(list<T> lt){swap(lt);return *this;}~list(){clear();delete _head;_head = nullptr;}void clear(){iterator it = begin();while (it != end()){it = erase(it);}_size = 0;}void push_back(const T& x){//第一种写法/*Node* tail = _head->_prev;Node* newnode = new Node(x);tail->_next = newnode;newnode->_prev = tail;newnode->_next = _head;_head->_prev = newnode;*///第二种写 -- 复用insert函数insert(end(), x);}void push_front(const T& x){insert(begin(), x);}void pop_back(){erase(--end());}void pop_front(){erase(begin());}iterator insert(iterator pos, const T& x){Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(x);prev->_next = newnode;newnode->_next = cur;cur->_prev = newnode;newnode->_prev = prev;++_size;return newnode;}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;--_size;delete cur;return next;//因为迭代器失效问题所以要返回下一个迭代器//为什么失效?因为删除之后迭代器原来的指向失效,需要返回下一个节点作为新的迭代器}size_t size(){return _size;}private:Node* _head;size_t _size;};struct A{A(int a1 = 0, int a2 = 0):_a1(a1), _a2(a2){}int _a1;int _a2;};
}
//main.cpp
#define _CRT_SECURE_NO_WARNINGS 1#include <iostream>
#include <list>
using namespace std;
#include "list.h"void Print(const bit::list<int>& lt)
{bit::list<int>::const_iterator it = lt.begin();while (it != lt.end()){//(*it)++;cout << *it << " ";++it;}cout << endl;
}void test_list1()
{bit::list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);//因为链表的底层不一样,所以我们需要对其iterator进行封装然后重载运算符bit::list<int>::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it;}cout << endl;Print(lt);
}void test_list2()
{bit::list<bit::A> lt;lt.push_back(bit::A(1, 1));lt.push_back(bit::A(2, 2));lt.push_back(bit::A(3, 3));lt.push_back(bit::A(4, 4));bit::list<bit::A>::iterator it = lt.begin();while (it != lt.end()){cout << it->_a1 << " " << it->_a2 << endl;++it;}cout << endl;
}void test_list3()
{bit::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_back(6);lt.push_back(7);lt.push_back(8);for (auto e : lt){cout << e << " ";}cout << endl;lt.pop_front();lt.pop_back();for (auto e : lt){cout << e << " ";}cout << endl;
}void test_list4()
{bit::list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);bit::list<int> lt1(lt);for (auto e : lt1){cout << e << " ";}cout << endl;bit::list<int> lt2;lt2.push_back(10);lt2.push_back(20);lt2.push_back(30);lt2.push_back(40);lt1 = lt2;for (auto e : lt1){cout << e << " ";}cout << endl;
}void test_list5()
{bit::list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);//bit::list<int> lt1(lt);//for (auto e : lt1)//{//	cout << e << " ";//}//cout << endl;bit::list<int>::reverse_iterator rit = lt.rbegin();while (rit != lt.rend()){cout << *rit << " ";++rit;}cout << endl;}void test_list6()
{bit::list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);bit::list<int>::iterator it = lt.begin();while (it != lt.end()){if (*it == 2){it = lt.erase(it);}cout << *it << " ";++it;}cout << endl;
}int main()
{test_list6();return 0;
}

3.2代码解析

4.list与vector的对比

vector与list都是STL中非常重要的序列式容器,由于两个容器的底层结构不同,导致其特性以及应用场景不同,其主要不同如下:

vector

list

底层结构

动态顺序表,一段连续空间

带头节点的双向循环链表

随机访问

支持随机访问,访问某个元素效率O(1)

不支持随机访问,访问某个元素效率O(N)

插入和删除

任意位置插入和删除效率低,需要搬移元素,时间复杂度O(N),插入时有可能需要增容,增容:开辟新空间,拷贝元素,释放旧空间,导致效率更低

任意位置插入和删除效率高,不需要搬移元素,时间复杂度为O(1)

空间利用率

底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高

底层节点动态开辟,小节点容易造成内存碎片,空间利用率低缓存利用率低

迭代器

原生态指针

对原生态指针(节点指针)进行封装

迭代器失效

在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器需要重新赋值否则会失效

插入元素不会导致迭代器失效,删除元素时,只会导致当前迭代器失效,其他迭代器不受影响

使用场景

需要高效存储,支持随机访问,不关心插入删除效率

需要大量插入和删除操作,不关心随机访问

 

相关文章:

list的使用和模拟实现

目录 1.list的介绍及使用 1.1 list的介绍 1.2 list的使用 1.2.1 list的构造 1.2.2 list iterator的使用 1.2.3 list capacity 1.2.4 list element access 1.2.5 list modifiers 2.为什么使用迭代器&#xff1f; 3.list的模拟实现 3.1完整代码 3.2代码解析 4.list与…...

Kubernetes 部署DolphinScheduler 创建租户失败

创建租户 报错创建租户失败。后台日志如下 源代码跟踪 org.apache.dolphinscheduler.api.service.impl.TenantServiceImpl / if hdfs startup if (PropertyUtils.getResUploadStartupState()) {createTenantDirIfNotExists(tenantCode); }需要将 resource.storage.type 置为…...

uniapp 获取 view 的宽度、高度以及上下左右左边界位置

<view class"cont-box"></view> /* 获取节点信息的对象 */ getElementRect() {const query uni.createSelectorQuery().in(this);query.select(".cont-box").boundingClientRect(res > {console.log(res);console.log(res.height); // 10…...

财务数据分析之现金流量表模板分享

现金流量表是我们常说的财务数据分析三表之一。它可以呈现一个企业的现金流情况&#xff0c;揭示企业经营管理健康状态&#xff0c;但在实际使用中却有总给人一种用不上、用不好的矛盾感。怎么才能把现金流量表做好&#xff1f;不如借鉴下大神的现金流量表模板。 下面介绍的是…...

日常BUG——通过命令行创建vue项目报错

&#x1f61c;作 者&#xff1a;是江迪呀✒️本文关键词&#xff1a;日常BUG、BUG、问题分析☀️每日 一言 &#xff1a;存在错误说明你在进步&#xff01; 一、问题描述 在使用vue命令行创建一个vue项目时&#xff0c;出现一下的错误&#xff1a; vue create my…...

CSS3 新特性

圆角阴影文字阴影线性渐变变换&#xff08;transform&#xff09;背景rgba伪元素&#xff1a;伪类 伪元素区别动画&#xff08;animate&#xff09;...

微信记录---推荐系统---23/8/14 小总结

推荐系统---23/8/14 小总结 1. ACM推荐系统专题研讨会2.图神经网络推荐系统3.表1 模型效果对标:MovieLens 1M4.爬虫技术5.TF-IDF算法6.图 2 海量学术大数据推荐系统技术架构7.图 4 CADAL 平台推荐系统框架设计8.企业推荐系统发展概述MLR(Mixed Logistic Regression)DIEN(Deep…...

学习笔记整理-正则表达式-01-认识正则

一、基本认识 1. 什么是正则表达式 正则表达式(regular expression)描述了字符串"构成模式"&#xff0c;经常被用于检查字符串是否符合预定的格式要求。 用一个例子快速演示正则表达式基本使用方法&#xff1a;检查某个字符串是否是6位数字 // 要检查的字符串va…...

windows10/11 修改docker镜像存储目录

windows10/11 修改docker镜像存储目录 windows10/11 修改docker镜像存储目录查看docker的状态关闭所有正在运行的实例导出WSL子系统镜像注销现有的wsl重新创建wsl系统 windows10/11 修改docker镜像存储目录 docker默认pull的镜像在c盘&#xff0c;随着镜像的增加&#xff0c;C…...

AI黑马挑战赛,探索研发新趋势丨IDCF

随着AI的出现&#xff0c;获取知识的成本大幅降低&#xff0c;当DevOps与AI相结合时&#xff0c;必将产生全新的化学反应。不断涌现的AI新工具提醒我们&#xff0c;一个全新的研发工作范式正在逐渐形成。而DevOps的核心理念是敏捷协同&#xff0c;作为工程师&#xff0c;如何通…...

关于onload事件

onload事件是在网页中的所有内容&#xff08;包括图片、样式表、脚本等&#xff09;都加载完成后触发的事件。它常用于在页面加载完成后执行一些操作&#xff0c;例如初始化页面元素、绑定事件监听器等。 可以通过以下方式来使用onload事件&#xff1a; 在HTML标签中直接添加…...

合并单元格

需求&#xff1a; 合并 相同名称的产品 先说下elementUI合并单元格的方法&#xff0c;先计算好要合并的行数rowspan&#xff0c;return {rowspan&#xff0c;colspan}&#xff0c;其他的单元格return{0,0} getData(params) {//临时数组&#xff0c;存放产品名称相同的数量this…...

Spring Boot @Validated 验证注解的使用

1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId> </dependency> 2、使用 2.1、非对象参数 参数如果是非对象格式&#xff0c;需要在controller类上面添…...

如何理解“对矩阵进行初等行变换不改变其列向量的线性关系”?

对矩阵A进行初等行变换相当于左乘一个可逆矩阵P。 把A看作是列向量组&#xff0c;若有Ax0&#xff0c;则其中的x就说明了列向量的线性关系&#xff1a; [ α 1 , α 2 , α 3 ] [ x 1 x 2 x 3 ] [ 0 ] \left[ \alpha_1 ,\alpha_2, \alpha_3 \right] \begin{bmatrix} x_1\\ x…...

书店行业小程序开发攻略

随着移动互联网的快速发展&#xff0c;小程序成为了各行各业的热门选择&#xff0c;包括书店行业。书店小程序的开发可以为书店提供在线销售渠道&#xff0c;提高销售额&#xff0c;增强用户粘性。本文将介绍如何从搭建到上线开发一款书店行业小程序。 首先&#xff0c;我们需要…...

情感分析工具: TextBlob 与 VADER 的对比

一、说明 在本文我们将看到&#xff0c;在情感分析方面&#xff0c;我们更喜欢哪个库。这取决于很多情况。例如。数据采集。在我们进行分析之前&#xff0c;让我们先看看这两个库是关于什么的。 二、亮相工具库 2.1. 工具库TextBlob介绍&#xff1a; 图像。TextBlob: Simplif…...

uft8和utf8mb4的区别

文章目录 1、Unicode字符集2、UTF-8 编码3、utf8mb3 字符集4、utf8mb4 字符集5、utf8mb3和utf8mb4的区别 1、Unicode字符集 Unicode&#xff08;统一码、万国码、单一码&#xff09;是计算机科学领域里的一项业界标准&#xff0c;包括字符集、编码方案等。Unicode 是为了解决传…...

针对低分辨率或小目标的卷积-SPDConv

针对低分辨率或小目标的卷积-SPDConv 摘要引言A New Building Block:SPD-Conv附录代码&#xff1a; 摘要 卷积神经网络在许多计算机视觉任务中取得了巨大成功。然而&#xff0c;在图像低分辨率或目标较小任务上&#xff0c;他们的性能迅速下降。在本文中&#xff0c;我们指出&…...

vue基础-vue监听当前屏幕大小做不同的操作

文章目录 前言一、代码如下&#xff1a;总结 前言 在vue项目开发过程中&#xff0c;有个需求&#xff0c;就是当屏幕大于1024时&#xff0c;我们默认为PC模式。小于1024时&#xff0c;我们默认为H5模式。但是有的界面我们想在PC和H5上面展示不同的数据&#xff0c;请求不同的接…...

Unity框架学习--3

单例模式基类 构造函数私有化&#xff0c;防止外部创建对象 提供一个属性给外部访问&#xff0c;这个属性就相当于是这个类的唯一对象 分为懒汉模式和饿汉模式 不继承MonoBehaviour的单例模式 public static MyUiManager Instance {get{if (instance null){instance new …...

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

HTML 语义化

目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案&#xff1a; 语义化标签&#xff1a; <header>&#xff1a;页头<nav>&#xff1a;导航<main>&#xff1a;主要内容<article>&#x…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

测试markdown--肇兴

day1&#xff1a; 1、去程&#xff1a;7:04 --11:32高铁 高铁右转上售票大厅2楼&#xff0c;穿过候车厅下一楼&#xff0c;上大巴车 &#xffe5;10/人 **2、到达&#xff1a;**12点多到达寨子&#xff0c;买门票&#xff0c;美团/抖音&#xff1a;&#xffe5;78人 3、中饭&a…...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版&#xff01;&#xff01;&#xff01;6.8截至答题&#xff0c;大家注意呀&#xff01; 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:&#xff08; B &#xff09; A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序

一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...