【C++ STL】list

文章目录

  • list
    • 1. list的使用
      • 1.1 增删查改
      • 1.2 功能接口
    • 2. list的模拟实现
      • 2.1 list的定义
      • 2.2 默认成员函数
      • 2.3 迭代器
        • 正向迭代器
          • 解引用箭头
        • 反向迭代器
        • 迭代器接口
      • 2.4 基本功能
    • 3. list对比vector


list

vector 相比,list 的好处就是每次插⼊或删除 ⼀个 元素 就 配置或释放 ⼀个 空间,而且 原有 的 迭代器 也 不会 失 效。list 是 ⼀个 双向链表,普通 指针 已经 不能 满足 list 迭代器 的 需求,因为 list 的 存储 空间 是 不 连续 的。list 的 迭代器 必需 具备 前移 和 后退 功能,所以 list 提供 的 是 BidirectionalIteratorlist 的 数据 结构 中 只要 ⼀个 指向 node 节点 的 指针 就 可以 了。

list (cplusplus.com)

template < class T, //模板参数Tclass Alloc = allocator<T> //空间配置器> 
class list; //类模板

关于链表,数据结构处已经讨论过,不再赘述。

1. list的使用

1.1 增删查改

默认成员函数说明
list ()默认构造
list (size_type n, const value_type& val = value_type())填充构造
list (InputIter first, InputIter last)范围构造
list (const list& li)拷贝构造
list& operator= (const list& x)赋值重载
访问接口说明
reference front()取头元素
reference back()取尾元素
容量接口说明
bool empty() const判空
size_type size() const元素个数
void resize (size_type n, value_type val = value_type())修改元素个数
修改接口说明
void push_front (const value_type& val)头插
void pop_front()头删
void push_back (const value_type& val)尾插
void pop_back()尾删
iterator insert (iterator position, const value_type& val)迭代器插入
void insert (iterator position, size_type n, const value_type& val)迭代器插入
void insert (iterator position, InputIter first, InputIter last)迭代器插入
iterator erase (iterator position)迭代器删除
iterator erase (iterator first, iterator last)迭代器删除
void assign (InputIter first, InputIter last)重置链表
void assign (size_type n, const value_type& val)重置链表
void clear()清空链表

1.2 功能接口

功能接口说明
void reverse()逆置链表
void sort(Compare comp = cmp)排序链表
void unique()链表去重
void remove (const value_type& val)删除所有指定元素
void merge (list& x, Compare comp = cmp归并两个链表
void splice (iterator position, list& x)接合整个链表
void splice (iterator position, list& x, iterator i)接合单个元素
void splice (iterator position, list& x, iterator first, iterator last)接合一段区间
lt.sort();

list 不支持随机访问,所以不支持算法库中的 sort。

单独内置一个 sort 接口,使用的是归并排序,但效率不高。当需要对数据进行排序的时候,不要选择链表。

lt.sort();
lt.unique();

去重接口可以去掉链表中的重复元素,但前提是需先将链表排成有序状态

lt.remove(1);

删除链表中所有指定值的元素,不是指定下标位置的元素。

lt.splice(pos, list);              // 转移整个链表
lt.splice(pos, list, i);           // 转移单个节点
lt.splice(pos, list, first, last); // 转移一段区间

接合函数能将一个链表中的一个或多个节点,转移到另一个链表中的指定迭代器位置。可转移整个链表,可转移链表内的一个元素,转移一个迭代器区间。

splice 的功能是转移不是拷贝,原链表中的该节点将不复存在。

 

list 不支持随机访问,实际中用的不多。list 的重点在于模拟实现。

2. list的模拟实现

2.1 list的定义

// listnode
template<class T>
struct __list_node
{__list_node<T>* _prev;__list_node<T>* _next;T _data;__list_node<T>(const T& t = T()): _data(t), _prev(nullptr), _next(nullptr){}
};// list
template <class T>
class list 
{ 
public:typedef __list_node<T> Node;list() {empty_init();}
private:Node* _head;
}

__list_node 是链表节点类,list 是链表类,他的成员是 __list_node 类型的指针,作链表的带头节点。

一般供他人使用的类,都用 struct 定义,因为 struct 定义的类成员默认都是公有的。

2.2 默认成员函数

/* default constructor */
void empty_init()
{_head = new Node(x);_head->_prev = _head;_head->_next = _head;
}list()
{empty_init();
}

在这里插入图片描述

/* copy constructor */  
//传统写法
list<T>(const list<T>& lt)
{empty_init();for (auto e : lt)push_back(e);
}
//现代写法
list<T>(const list<T>& lt)
{empty_init();list<T> tmp(lt.begin(), lt.end());swap(_head, tmp._head);
}
/* = operator */
//传统写法
list<T>& operator=(const list<T>& lt)
{	if (this != &lt) {clear();for (auto e : lt)push_back(e);}return *this;
}
//现代写法
list<T>& operator=(list<T> lt)
{swap(_head, lt._head);return *this;
}
/* deconstructor */
void clear()
{iterator it = begin();while (it != end()){it = erase(it);}
}~list<T>()
{clear();delete _head;
}

2.3 迭代器

迭代器模拟指针,意图像指针一样遍历容器。

list 的底层实现并不是 vector 一样的连续空间,而是通过节点地址链接前后,故 list 实现上述操作就需要自行实现一个迭代器类再重载这些运算符。

正向迭代器
template<class T, class Ref, class Ptr>
struct __list_iterator
{typedef __list_node<T> list_node;typedef __list_iterator<T, Ref, Ptr> self;__list_iterator(list_node* n) : _node(n){}Ref operator*() {return _node->_data;}Ptr operator->() {return &_node->_data;}self& operator++() {_node = _node->_next;return *this;}self& operator--() {_node = _node->_prev;return *this;}bool operator!=(const self& s) {return _node != s._node;}list_node* _node;
};

list 容器的迭代器是双向迭代器,不支持随机访问,没有重载+,+=,-,-=

迭代器的拷贝构造、赋值重载都只需要浅拷贝指针。析构函数无需释放任何资源,节点交由链表进行管理。所以这些编译器默认生成就可以。

解引用箭头
template <class T, class Ref, class Ptr> // 交由list类控制
struct __list_iterator 
{typedef __list_iterator<T, Ref, Ptr> iterator;Ref operator* () { return  _node->_data; }Ptr operator->() { return &_node->_data; }//...
};class list
{
public:typedef __list_iterator<T, T&, T*> iterator;                   // 传入普通类型typedef __list_iterator<T, const T&, const T*> const_iterator; // 传入常量类型
}

可以给迭代器类增加引用和指针类型的模版参数,分别给*->重载函数返回值使用。

相当于把具体的元素引用和指针类型交给外界控制。就不需要单独实现一个常量迭代器了。

在这里插入图片描述

当元素是自定义类型时,箭头操作符可以让迭代器直接访问到自定义类型的内部成员。如:

struct AA
{AA(int a1 = 0, int a2 = 0) : _a1(a1), _a2(a2) {}int _a1;int _a2;
};void test_list()
{list<AA> lt;lt.push_back(AA(1, 1));lt.push_back(AA(2, 2));list<AA>::iterator it = lt.begin();while (it != lt.end()){cout << it->_a1 << ":" << it->_a2 << " "; // ->直接访问AA内部成员++it;}cout << endl;
}

it->AA()->_a1,省略了一个->

反向迭代器

反向迭代器同样是个适配器,所以它被单独实现在一个文件中。

template <class Iterator, class Ref, class Ptr>
struct reverse_iterator
{Iterator _it;typedef reverse_iterator self;reverse_iterator(Iterator it) // 利用正向迭代器构造出反向迭代器:_it(it) {}self& operator++() // 反向迭代器++,就是正向迭代器--{--_it;return *this;}self& operator--() // 反向迭代器--,就是正向迭代器++{++_it;return *this;}bool operator!=(const self& it) // 反向迭代器是否相等,就是正向迭代器是否相等{return _it != it._it;}Ref operator*() {Iterator tmp = _it;return *--tmp;       // 前一个位置的迭代器}Ptr operator->(){Iterator tmp = _it;return &*--tmp;}
};

反向迭代器是对正向迭代器的封装。源码实现使用类型萃取难度较高,我们不使用。

反向迭代器解引用和箭头不是访问当前位置,而是前一个位置

//list源码
iterator begin() { return (link_type)((*node).next); }
iterator end() { return node; }reverse_iterator rbegin() { return reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }const_iterator begin() const { return (link_type)((*node).next); }
const_iterator end() const { return node; }const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }

所有容器的迭代器begin/end()rbegin/rend()指向的位置正好对应相反。目的是设计出对称形式,因此解引用时返回的是上一个位置的数据。

在这里插入图片描述

反向迭代器遍历:

list<int>::reverse_iterator rit = it.rbegin();
while (rit != it.rend()) {// 这里可以访问rit指向的元素,比如输出元素的值cout << *rit << " ";// 向前移动迭代器++rit;
}
迭代器接口
template<class T>
class list 
{
public:typedef __list_iterator<T, T&, T*> iterator;typedef __list_iterator<T, const T&, const T*> const_iterator;typedef reverse_iterator<iterator, T&, T*> reverse_iterator;typedef reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;iterator begin() { return iterator(_head->_next); }iterator end()   { return iterator(_head); }const_iterator begin() const { return const_iterator(_head->_next); }const_iterator end()   const { return const_iterator(_head); }reverse_iterator rbegin() { return reverse_iterator(end()); }reverse_iterator rend()   { return reverse_iterator(begin()); }const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }const_reverse_iterator rend()   const { return const_reverse_iterator(begin()); }
};

在这里插入图片描述
在这里插入图片描述

2.4 基本功能

iterator insert(iterator pos, const T& x)
{Node* newNode = new Node(x);Node* prev = pos._node->_prev;Node* cur = pos._node;// prev - newNode - curprev->_next = newNode;newNode->_prev = prev;cur ->_prev = newNode;newNode->_next =  cur;return iterator(newNode); //返回迭代器
}
iterator erase(iterator pos)
{assert(pos != end());Node* prev = pos._node->_prev;Node* next = pos._node->_next;delete pos._node;// prev - pos - afterprev->_next = next;next->_prev = prev;return iterator(next);
}
  • list 的插入操作迭代器不会失效,因为迭代器 pos 的值不会改变,始终指向原来的节点。
  • list 的删除操作迭代器一定失效,因为节点已经被释放了,应修正为 pos 下一个位置。

在这里插入图片描述

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

 

3. list对比vector

vector数据结构:
vector数组 类似,拥有一段 连续 的 内存空间,并且 起始地址 不变。因此 能 高效 的 进行 随机存取,时间复杂度 为 O(1);但 因为 内存空间 是 连续 的,所以 在 进行 插入 和 删除 操作时,会 造成 内存块 的 拷贝,时间复杂度 为 O(n)。它 与 数组 最大 的 区别 就是 vector 不需 程序员 自己 去 考虑 容量问题,库 里面 本身 已经 实现 了 容量动态增长,而 数组 需要 程序员 手动 写入 扩容函数 进形 扩容。

list数据结构
list 是 由 双向链表 实现 的,因此 内存空间 是 不 连续 的。只能 通过 指针 访问 数据,所以 list随机存取 非常 没有效率,时间复杂度 为 O(n);但 由于 链表 的 特点,能 高效 地 进行 插入删除非连续存储结构list 是 一个 双链表 结构,支持 对 链表 的 双向遍历。每个 节点 包括 三个 信息:元素本身,指向 前一个元素节点prev)和 指向 下一个元素节点next)。因此 list 可以 高效率 地 对 数据元素 任意位置 进行 访问插入删除操作。由于 涉及 对 额外指针维护,所以 开销 比较 大。

区别如下

  1. vector随机访问 效率 ,但 在 插入删除 时(不包括 尾部) 需要 挪动数据不易操作
  2. list访问遍历整个链表,它 的 随机访问 效率 。但 对 数据插入删除 操作 等 都 比较 方便改变 指针指向 即可。
  3. 遍历 上 来 说,list单向 的,vector双向 的。
  4. vector 中 的 迭代器使用后失效 了,而 list迭代器使用 之后 还 可以 继续使用
容器vectorlist
底层结构连续物理空间空间不连续
随机访问支持随机访问不支持随机访问
插入删除非尾部插入删除要移动数据,效率低 O ( n ) O(n) O(n)任意位置的插入删除效率高 O ( 1 ) O(1) O(1)
空间利用率增容代价大,倍数扩容存在一定的空间浪费按需申请空间,不存在浪费
迭代器原生指针支持随机访问迭代器类模拟指针行为,支持双向访问
适用场景高效存储,随机访问,不关心增删效率频繁使用插入删除,不关心随机访问

vector与list两种容器各有优劣,实际上vector用的更多些。因为vector支持随机访问,其次空间浪费也不是太严重的问题。

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

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

相关文章

pydal,一个实用的 Python 库!

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 大家好&#xff0c;今天为大家分享一个实用的 Python 库 - pydal。 Github地址&#xff1a;https://github.com/web2py/pydal/ 在现代应用开发中&#xff0c;数据库操作是一个核心部分。为了简化与数据库的交互…

PMP–知识卡片--Scrum框架

定义 Scrum框架包含由产品负责人、开发团队、敏捷专家构成的Scrum团队&#xff0c;以及活动工件。框架中的每一个组件都服务于一个特定的目标&#xff0c;且是Scrum成功和运用的基本要素。 Scrum的规则将角色、活动和工件绑定在一起&#xff0c;管理它们之间的关系和交互。 …

【Vue3】组件通信之$parent

【Vue3】组件通信之$parent 背景简介开发环境开发步骤及源码总结 背景 随着年龄的增长&#xff0c;很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来&#xff0c;技术出身的人总是很难放下一些执念&#xff0c;遂将这些知识整理成文&#xff0c;以纪念曾经努力学习奋斗的…

基于Java的网络考试系统的设计与实现

点击下载源码 基于Java的网络考试系统的设计与实现 摘 要 科技在进步&#xff0c;人们生活和工作的方式正发生着改变&#xff0c;不仅体现在人们的衣食住行&#xff0c;也体现在与时俱进的考试形式上。以前的考试需要组织者投入大量的时间和精力&#xff0c;需要对考试的试题…

人工智能与大数据的融合:驱动未来的力量

人工智能与大数据的融合&#xff1a;驱动未来的力量 一、人工智能与大数据的概述二、人工智能与大数据在数据库中的融合三、实际应用案例四、未来发展方向总结 【纪录片】中国数据库前世今生 在数字化潮流席卷全球的今天&#xff0c;数据库作为IT技术领域的“活化石”&#xff…

【Python实战】如何优雅地实现文字 二维码检测?

前几篇&#xff0c;和大家分享了如何通过 Python 和相关库&#xff0c;自动化处理 PDF 文档&#xff0c;提高办公效率。 【Python实战】自动化处理 PDF 文档&#xff0c;完美实现 WPS 会员功能【Python实战】如何优雅地实现 PDF 去水印&#xff1f;【Python实战】一键生成 PDF…

【Linux详解】基础IO:软硬连接 | 动静态库管理

目录 软硬链接 1. 介绍 2.理解 2.1 如何理解硬链接&#xff1f; 2.2 如何理解软连接&#xff1f; 动静态库 1.介绍 1.1 使用 1.2 什么是库&#xff1f; 2.生成 2.1 静态库 2.2 动态库&#xff1a; 软硬链接 1. 介绍 1.1 软连接 是一个独立文件&#xff0c;具有独…

【Python机器学习】支持向量机——利用完整platt SMO算法加速优化

在几百个数据点组成的小规模数据集上&#xff0c;简化版SMO算法的运行是没有什么问题&#xff0c;但是在更大的数据集上的运行速度就会变慢。完整版的platt SMO算法应用了一些能够提速的启动方法。 platt SMO算法时通过一个外循环来选择第一个alpha值的&#xff0c;并且其选择…

内网穿透--ICMP隧道转发实验

实验背景 通过公司带有防火墙功能的路由器接入互联网&#xff0c;然后由于私网IP的缘故&#xff0c;公网无法直接访问内部web服务器主机。通过内网其它主机做代理&#xff0c;穿透访问内网web服务器主机边界路由器或防火墙做静态NAT映射访问内网服务器inux主机&#xff0c;且策…

MySQL的数据类型

文章目录 数据类型分类整型bit类型浮点类型字符串类型charvarchar 日期和时间类型enum和set find_ in_ set 数据类型分类 整型 在MySQL中&#xff0c;整型可以指定是有符号的和无符号的&#xff0c;默认是有符号的。 可以通过UNSIGNED来说明某个字段是无符号的。 在MySQL中如…

Tree-of-Traversals:结合知识图谱与大模型,通过树遍历和回溯寻找高置信度推理路径

Tree-of-Traversals&#xff1a;结合知识图谱与大模型&#xff0c;通过树遍历和回溯寻找高置信度推理路径 Tree-of-Traversals算法解析对比 MindMap1. 与知识图谱&#xff08;KGs&#xff09;的整合2. 推理方法3. 灵活性与可扩展性4. 在医学诊断中的应用 速度和准确1. 速度2. 推…

第十一章:Kubernetes API服务器的安全防护

本章内容包括&#xff1a; 了解认证机制ServiceAccounts是什么及使用的原因了解基于角色(RBAC)的权限控制插件使用角色和角色绑定使用集群角色和集群角色绑定了解默认角色及其绑定 1 了解认证机制 在前面的内容中&#xff0c;我们说到API服务器可以配置一个到多个认证的插件(授…

数据结构链表2(常考习题1)(C语言)

移除链表元素&#xff1a; . - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 解题思路&#xff1a; 情况1&#xff1a; 情…

python dash框架

Dash 是一个用于创建数据分析型 web 应用的 Python 框架。它由 Plotly 团队开发&#xff0c;并且可以用来构建交互式的 web 应用程序&#xff0c;这些应用能够包含图表、表格、地图等多种数据可视化组件。 Dash 的特点&#xff1a; 易于使用&#xff1a;Dash 使用 Python 语法…

深入解析 KMZ 文件的处理与可视化:从数据提取到地图展示项目实战

文章目录 1. KMZ 文件与 KML 文件简介1.1 KMZ 文件1.2 KML 文件 2. Python 环境配置与依赖安装3. 代码实现详解3.1 查找 KMZ 文件3.2 解压 KMZ 文件3.3 解析 KML 文件3.4 可视化 KMZ 数据 4. 项目实战4.1. 数据采集4.2. 项目完整代码 5. 项目运行与结果展示6. 总结与展望 在处理…

将后台传来的数据,转成easyui-tree所需格式

easyui 中文文档 EasyUI Tree组件需要一个包含特定属性&#xff08;如id, text, children等&#xff09;的JSON对象数组来初始化。 而后台返回的数据&#xff0c;它可能不是我们直接能拿来用的。 方式一&#xff1a;使用loadFilter函数处理来自Web Services的JSON数据。 $(#…

功能实现——通过阿里云 OSS 实现文件管理

目录 1.需求分析2.阿里云 OSS 开通与配置2.1.登录阿里云官网2.2.搜索 OSS 服务并开通2.3.OSS 配置 3.在项目使用阿里云 OSS3.1.项目环境搭建3.2.代码实现3.2.1.将本地文件上传到阿里云 OSS3.2.2.将前端传入的文件上传到阿里云 OSS3.2.3.下载文件到本地2.3.4.流式下载3.2.4.OSSC…

本地部署文生图模型 Flux

本地部署文生图模型 Flux 0. 引言1. 本地部署1-1. 创建虚拟环境1-2. 安装依赖模块1-3. 创建 Web UI1-4. 启动 Web UI1-5. 访问 Web UI 0. 引言 2024年8月1日&#xff0c;blackforestlabs.ai发布了 FLUX.1 模型套件。 FLUX.1 文本到图像模型套件&#xff0c;该套件定义了文本到…

【收录率高丨最快会后3-4个月EI检索 | 往届均已EI检索】第四届光学与通信技术国际学术会议(ICOCT 2024,8月9-11)

欢迎参加第四届光学与通信技术国际学术会议&#xff08;ICOCT 2024&#xff09;&#xff0c;该会议将于2024年8月9-11日在南京举办。自2021年首次会议以来&#xff0c;ICOCT已经发展成为光学和通信领域较有影响力的国际会议之一&#xff0c;聚焦最前沿的技术进展与未来发展趋势…

【Redis 进阶】哨兵 Sentinel(重点理解流程和原理)

Redis 的主从复制模式下&#xff0c;一旦主节点由于故障不能提供服务&#xff0c;需要人工进行主从切换&#xff0c;同时大量的客户端需要被通知切换到新的主节点上&#xff0c;对于上了一定规模的应用来说&#xff0c;这种方案是无法接受的&#xff0c;于是 Redis 从 2.8 开始…