C++中的list是标准模板库(STL)提供的双向链表容器,支持高效的元素插入和删除操作。在上一篇中讲解了vector的使用和模拟实现,vector是具有连续的空间,迭代器是可以随机的,而list却于vector不同,list是带头双向循环链表,迭代器是双向的,空间并不是连续的,所以在底层实现上比vector复杂一点。与vector 不同,list 在任意位置插入或删除元素的时间复杂度为 O(1),但随机访问的性能较差(时间复杂度为 O(n))。

目录

list的介绍

 list的使用

list的构造

list的插入和删除 

list访问元素 

list的大小和容量 

list迭代器的使用 

list的模拟实现 

迭代器中的函数 


list的介绍

1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。

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

3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高 效。

4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率 更好。

5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list 的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间 开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这 可能是一个重要的因素)

 list的使用

list是带头双向循环链表,如果迭代器要进行++、--、*(解引用)等操作是否跟vector一样呢,当然不是,两者的结构不同注定迭代器也不一样,但是为了方便大家使用将list的迭代器进行了封装,依然是lis<T>iterator来进行各种操作,list的空间不是连续的,所以在实现++、--、*等操作就需要进行运算符重载来定义该运算符的行为,在使用上迭代器vector和list没什么两样,但是在底层却大大不同。

list的构造

list (size_type n, const value_type& val = value_type()) //构造的list中包含n个值为val的元素list() //构造空的listlist (InputIterator first, InputIterator last) //用[first, last)区间中的元素构造list

使用list需要包含头文件。初始化的方式包括默认构造、指定大小初始化、通过迭代器范围初始化等。 

示例:

#include <list>
#include <iostream>
using namespace std;int main() {list<int> l1; // 空列表list<int> l2(5, 10); // 包含5个值为10的元素list<int> l3 = {1, 2, 3, 4, 5}; // 列表初始化return 0;
}

list的插入和删除 

push_back(val):在末尾插入元素。

push_front(val):在头部插入元素。

pop_back(val):删除末尾元素。

pop_front(val):删除头部元素。

insert(pos,val):在指定位置插入元素。

erase(pos):在指定位置删除元素。

示例: 

std::list<int> l = {1, 2, 3};
l.push_back(4); // {1, 2, 3, 4}
l.push_front(0); // {0, 1, 2, 3, 4}
l.pop_back(); // {0, 1, 2, 3}
l.pop_front(); // {1, 2, 3}
auto it = l.begin();
it++;
l.insert(it, 10); // {1, 10, 2, 3}
l.erase(it); // {1, 10, 3}

list访问元素 

front():返回第一个元素。

back():返回最后一个元素。

由于list的结构不支持operator【】或者at()随机访问。 

示例:

std::list<int> l = {1, 2, 3};
std::cout << l.front() << std::endl; // 输出1
std::cout << l.back() << std::endl; // 输出3

list的大小和容量 

在vector中扩容是很常见的,扩容之后需要拷贝,而在list中就不存在这样的问题,当插入一个值就new一个节点再链接起来,不需要拷贝,按需申请。

size():返回元素数量。

empty():判断是否为空。

示例:

std::list<int> l = {1, 2, 3};
std::cout << l.size() << std::endl; // 输出3
std::cout << l.empty() << std::endl; // 输出0(false)

list 提供了一些特有操作:

sort():排序(默认升序,可自定义比较函数)。
merge(other_list):合并两个有序列表。
splice(pos, other_list):将另一个列表的元素移动到指定位置。
unique():删除连续重复元素。

示例: 

std::list<int> l1 = {3, 1, 2};
std::list<int> l2 = {5, 4, 6};
l1.sort(); // {1, 2, 3}
l2.sort(); // {4, 5, 6}
l1.merge(l2); // l1: {1, 2, 3, 4, 5, 6}, l2为空
l1.unique(); // 删除重复元素

list迭代器的使用 

 list提供双向迭代器,有begin()、end()、rbegin()、和rend()。

std::list<int> l = {1, 2, 3};
for (auto it = l.begin(); it != l.end(); it++) {std::cout << *it << " ";
}
// 输出:1 2 3

想知道更多list的更多接口可以看这里:https://legacy.cplusplus.com/reference/list/list/?kw=list 

list的模拟实现 

在实现list之前我们需要知道list的结构是带头双向循环链表,迭代器也是和vector不一样,需要去实现封装迭代器,list迭代器需要和vector的迭代器有一样的功能,所以需要定义一个结构体迭代器在该结构体中去实现迭代器的各种行为。而迭代器还有const修饰的迭代器,所以模版参数可不止一个,需要注意的是const修饰的迭代器本身是可以进行++等操作的,而迭代器指向的内容是只读不可写的。只要对迭代器的实现完善好接下来的工作就十分好做,模拟实现list的重难点就在于如何去实现迭代器。

代码展示:

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)//后置{_list_iterator<T> 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){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}};

模版参数:

T:链表元素的类型(如:int、string),与节点定义中的T一致。

Ref:引用类型,用于operator*的返回类型。通常为T&(非const迭代器)或者const T& (const迭代器)

ptr:指针类型,用于operator->的返回类型。通常为T*(非const)或者const T*(const)

对于内部typedef:

typedef list_node<T> Node; 定义节点类型。

typedef _list_iterator<T, Ref, ptr> Self; 定义自身类型,方便使用。

迭代器中的函数 

Ref operator*():解引用运算符,返回当前节点值的引用。

作用:允许*it的语法访问元素值。例如it是迭代器,*it就可以获取节点储存的值。

ptr operator->():成员访问运算符,返回指向节点值的指针。

作用:支持it->member语法。例如,如果T是结构体类型,it->member访问其成员。

Self& operator++()(前置递增):移动到下一个节点,并返回更新后的迭代器引用。 

作用:用于++it语法。效率高,因为直接修改并返回自身。 

Self operator++(int)(后置递增):返回当前迭代器的副本,然后移动到下一个节点。

作用:用于it++语法。参数int是占位符,用于区分前置和后置版本。注意返回类型是Self(值类型),不是引用,因为返回的是临时副本。

Self& operator--()(前置递减):移动到前一个节点,并返回更新后的迭代器引用。

作用:用于--it语法。正确利用了双向链表的_prev指针 

Self operator--(int)(后置递减):返回当前迭代器的副本,然后移动到前一个节点。

作用:用于it--语法。

bool operator!=(const Self& it):检查两个迭代器是否指向不同节点。

作用:用于it1 != it2语法。例如,在循环中判断是否到达结尾。

此迭代器实现是STL风格双向链表的核心部分:它封装了节点指针,提供统一的元素访问和遍历接口。通过模板参数,优雅地支持const和非const迭代器。运算符重载使迭代器用法类似原生指针(如*it、it->、++it)。

list代码:

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;//const T* ptr //const 修饰的是指针指向的内容,不能修改//T* const ptr // const 修饰的是指针,指针不能修改list(){head = new Node;head->_next = head;head->_prev = head;n = 0;}list(const list<T>& lt){head = new Node;head->_next = head;head->_prev = head;n = 0;for (auto& e : lt){push_back(e);}}~list(){clear();}iterator begin(){return head->_next;}iterator end(){return head;}const_iterator begin() const{return const_iterator(head->_next);}const_iterator end() const{return const_iterator(head);}void push_back(const T& x){Node* tail = head->_prev;Node* newnode = new Node(x);tail->_next = newnode;newnode->_prev = tail;head->_prev = newnode;newnode->_next = head;n++;//insert(--end(),x);}void pop_back(){erase(--end());}void push_front(const T* x){insert(begin(), x);}void pop_front(){erase(begin());}iterator insert(iterator pos, const T& x){Node* newnode = new Node(x);Node* cur = pos._node->_prev;newnode->_next = cur->_next;newnode->_prev = cur;cur->_next->_prev = newnode;cur->_next = newnode;n++;return newnode;}iterator erase(iterator pos){assert(pos != end());Node* cur = pos._node->_next;Node* prev = pos._node->_prev;cur->_prev = prev;prev->_next = cur;/*pos._node->_next = pos._node->_prev = nullptr;*/delete pos._node;n--;return cur;}void swap(list<T>& lt){std::swap(head, lt.head);std::swap(n, lt.n);}list<T>& operator=(list<T> lt){swap(lt);return *this;}void clear(){/*Node* cur = head->_next;while (cur != head){Node* next = cur->_next;delete cur;cur = next;}head->_next = head;head->_prev = head;n = 0;*/iterator it = begin();while (it != end()){it = erase(it);}}size_t size(){return n;}private:Node* head;size_t n;};

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

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

相关文章

【编号58-61】我国四大高原矢量示意图shp数据

今天分享的是&#xff1a;中国四大高原&#xff0c;分别是青藏高原、内蒙古高原、黄土高原、云贵高原。青藏高原位置与范围&#xff1a;位于中国西南部&#xff0c;包括西藏、青海的全部&#xff0c;川西高原及滇西北高原等部分地区。它的边界&#xff0c;向东是横断山脉&#…

【AI落地应用实战】利用 Amazon Bedrock Claude3 打造个性化 AI Character 应用

目录一、引言&#xff1a;AI Character应用的市场前景与技术基础二、技术架构设计2.1、整体方案概述2.2、核心组件介绍2.3、部署架构图三、系统部署方案3.1、方案总述3.2、实践流程1️⃣. Bedrock 配置2️⃣. 安装 SillyTavern3️⃣. 配置 SillyTavern 使用 Claude3 模型4️⃣.…

Java常用日志框架介绍

Java提供了很多第三方的日志框架可供使用&#xff0c;按照现在的设计理念&#xff0c;一般把日志框架分成门面(Facade)部分和具体实现(Implementation)部分&#xff0c;门面(Facade)提供了抽象的api规范&#xff0c;实现(Implementation)负责实现api完成具体的日志记录功能。开…

飞书 —— 多维表格 —— AI生成

1.添加关联账号&#xff1a; 2.获取密钥 ARK_API_KEY 进入火山引擎服务页面&#xff1a;https://console.volcengine.com/ark/region:arkcn-beijing/model/detail?Iddeepseek-r1 先进入推理模型 > 快捷API接入 再去在线推理中创建推理接入点 点击新创建好的接入点的API调…

我的世界模组开发教程——资源(1)

下面我们来研究一下ResourceLocation,每次开启游戏时都会报这个错误:“ResourceLocation 中的 ResourceLocation(String) 已过时, 且标记为待删除”,下面我们来详细的研究一下这个类 ResourceLocation ResourceLocation 是 Minecraft 中用于唯一标识游戏资源的核心类(如方…

我从 Web2 转型到 Web3 的 9 条经验总结

作者&#xff1a;Forte Group 高级区块链工程师 Yurii Kovalchuk原文&#xff1a;https://cryptoslate.com/why-i-left-web2-for-web3-and-why-you-might-too/三年前&#xff0c;我做出了一个彻底改变职业轨迹的决定&#xff1a;离开熟悉的 Web2&#xff0c;投身于深邃、混乱却…

【MySQL 数据库】MySQL索引特性(一)磁盘存储定位扇区InnoDB页

文章目录没有索引&#xff0c;可能会有什么问题二、认识磁盘2.1 MySQL与存储2.2 磁盘&#xff1a;2.3 扇区2.4 定位扇区2.5 结论三、三者作用流程&#xff08;磁盘&#xff0c;块&#xff0c;InnoDB页&#xff09;四、MySQL与磁盘交互基本单位五、建立共识&#x1f6a9;总结没有…

2419. 按位与最大的最长子数组

Problem: 2419. 按位与最大的最长子数组 文章目录思路解题过程复杂度Code思路 按位异或只会让数值越来越小&#xff0c;因此最长的连续按位与的最大值只存在于连续最大值中。 解题过程 遍历数组取出最大值&#xff0c;再遍历找到每一次连续最大值&#xff0c;从中取出最长的连续…

基于Java(SpringBoot)+Vue+MySQL 实现(Web)的网络课程平台

基于 SpringBoot 的网络课程平台1 绪论1.1 引言本科题研究并实现了一个面向网络学习的平台&#xff0c;为需要学习的人提供了一个学习的平台。任何人都课在本平台进行注册登录&#xff0c;学习观看视频。本平台是一个关于网络课程学习平台&#xff0c;学员科自主选择视频学习&a…

Centos7 | 防火墙(firewalld)使用ipset管理ip地址的集合

文章目录一、firewalld中ipset的用途1.1 用途1.2 注意与iptables所用的ipset命令的不同&#xff0c;1.3 配置详解二、firewalld中ipset的操作例子2.1 新建一个set2.2 在set中添加ip2.3 从set中删除ip2.4 删除一个set2.5 打印一个set的文件路径2.6 打印一个set的内容2.8 判断一个…

Day06_C++编程

01.思维导图02.将鸟笼放飞所有鸟类的题&#xff0c;改成观察者模式#include <iostream> #include <cstring> #include <cstdlib> #include <unistd.h> #include <sstream> #include <vector> #include <memory>//写一个鸟类:有一个多…

【面试场景题】随机立减金额计算

文章目录背景设计思路方案结论高斯分布&#xff08;正态分布&#xff09;背景 某电商公司跟某银行有合作&#xff0c;推进银行信用卡办卡&流水&#xff0c;使用此银行信用卡用户&#xff0c;支付可以随机立减10&#xff5e;30元。其实公司每一笔都可获得30元支付立减金&…

2025年湖北中级注册安全工程师报考那些事

2025年湖北中级注册安全工程师报考那些事各位从事建筑安全的人员看过来&#xff0c;注册安全工程师是你们行业认可度较为高的证书。关于报考无论是安全相关专业跟不相关的专业都是可以报考的。只是年份要求不同。 本科&#xff1a;相关专业3年&#xff0c;不相关专业4年。 专科…

Prometheus + Grafana + Micrometer 监控方案详解

这套组合是当前Java生态中最流行的监控解决方案之一&#xff0c;特别适合云原生环境下的微服务应用监控。下面我将从技术实现到最佳实践进行全面解析。 一、技术栈组成与协作 1. 组件分工组件角色关键能力Micrometer应用指标门面(Facade)统一指标采集API&#xff0c;对接多种监…

实习小记(个人中心的编辑模块)

实习小记&#xff08;个人中心的编辑模块&#xff09; 项目需要加一个个人中心的编辑模块&#xff0c;也是差不多搞了一天下来&#xff0c;其中遇到了很多问题&#xff0c;也是来记录、分享一下。 技术栈&#xff1a;React、antd、TypeScript 需求 点击编辑&#xff0c;弹出编…

【7】串口编程三种模式(查询/中断/DMA)韦东山老师学习笔记(课程听不懂的话试着来看看我的学习笔记吧)

<1>前置概念补充在深入拆解三种模式前&#xff0c;先通过提供的 “函数对比表” 建立整体认知&#xff1a;这张表是串口收发的「武器库索引」&#xff0c;清晰标注了查询、中断、DMA 三种模式下&#xff0c;收发 / 回调函数的对应关系。后续会结合实际代码&#xff0c;讲…

【Kubernetes 指南】基础入门——Kubernetes 201(二)

二、滚动升级- 滚动升级&#xff08;Rolling Update&#xff09;通过逐个容器替代升级的方式来实现无中断的服务升级&#xff1a;- 在滚动升级的过程中&#xff0c;如果发现了失败或者配置错误&#xff0c;还可以随时回滚&#xff1a;- 需要注意的是&#xff0c; kubectl rolli…

网络资源模板--基于Android Studio 实现的图书商城App

目录 一、测试环境说明 二、项目简介 三、项目演示 四、部设计详情&#xff08;部分) 登录注册页 首页 五、项目源码 一、测试环境说明 电脑环境 Windows 11 编写语言 JAVA 开发软件 Android Studio (2020) 开发软件只要大于等于测试版本即可(近几年官网直接下载…

JavaWeb 进阶:Vue.js 与 Spring Boot 全栈开发实战(Java 开发者视角)

作为一名 Java 开发工程师&#xff0c;当你掌握了 HTML、CSS 和 JavaScript 的基础后&#xff0c;是时候接触现代前端框架了。Vue.js 以其简洁的 API、渐进式的设计和优秀的中文文档&#xff0c;成为众多 Java 开发者入门前端框架的首选。Vue.js 让你能快速构建响应式、组件化的…

智能体产品化的关键突破:企业智能化转型的“最后一公里”如何迈过?

智能体产品化的关键突破&#xff1a;企业智能化转型的“最后一公里”如何迈过&#xff1f; 在人工智能迅猛发展的当下&#xff0c;智能体&#xff08;Agent&#xff09;成为企业数字化转型的新引擎。无论是市场分析、客户服务&#xff0c;还是自动化办公&#xff0c;智能体都被…