C++STL~~list

news2024/9/20 18:28:00

文章目录

    • 一、list的概念
    • 二、list的使用
    • 三、list的练习
    • 四、与vector的对比
    • 五、总结

一、list的概念

list 是一种容器,实现了双向链表结构
它具有以下特点:

  • 动态大小,可按需增减元素数量。
  • 高效的插入和删除操作,在任意位置插入和删除元素时间复杂度为 O (1)。但随机访问元素较慢,时间复杂度为 O (n)。
  • 提供双向迭代器,方便遍历、访问和修改元素。
  • 不支持随机访问,访问特定位置的元素需要通过迭代器逐步遍历,时间复杂度为线性。
    总之,list适用于需要频繁进行插入和删除操作,而对随机访问要求不高的场景。
  • 常见操作包括元素访问(如 front ()、back ())、插入(如 push_front ()、push_back ()、insert ())、删除(如 pop_front ()、pop_back ()、erase ())以及获取大小(size ())和判断是否为空(empty ())等。
  • 在这里插入图片描述

二、list的使用

list的构造
在这里插入图片描述

// list的构造
void TestList1()
{
    list<int> l1;                         // 构造空的l1
    list<int> l2(4, 100);                 // l2中放4个值为100的元素
    list<int> l3(l2.begin(), l2.end());  // 用l2的[begin(), end())左闭右开的区间构造l3
    list<int> l4(l3);                    // 用l3拷贝构造l4

    // 以数组为迭代器区间构造l5
    int array[] = { 16,2,77,29 };
    list<int> l5(array, array + sizeof(array) / sizeof(int));

    // 列表格式初始化C++11
    list<int> l6{ 1,2,3,4,5 };

    // 用迭代器方式打印l5中的元素
    list<int>::iterator it = l5.begin();
    while (it != l5.end())
    {
        cout << *it << " ";
        ++it;
    }       
    cout << endl;

    // C++11范围for的方式遍历
    for (auto& e : l5)
        cout << e << " ";

    cout << endl;
}

list iterator的使用
在这里插入图片描述
在这里插入图片描述

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

迭代器的类型
在这里插入图片描述

// list迭代器的使用
// 注意:遍历链表只能用迭代器和范围for
void PrintList(const list<int>& l)
{
    // 注意这里调用的是list的 begin() const,返回list的const_iterator对象
    for (list<int>::const_iterator it = l.begin(); it != l.end(); ++it)
    {
        cout << *it << " ";
        // *it = 10; 编译不通过
    }

    cout << endl;
}

void TestList2()
{
    int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    list<int> l(array, array + sizeof(array) / sizeof(array[0]));
    // 使用正向迭代器正向list中的元素
    // list<int>::iterator it = l.begin();   // C++98中语法
    auto it = l.begin();                     // C++11之后推荐写法
    while (it != l.end())
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;

    // 使用反向迭代器逆向打印list中的元素
    // list<int>::reverse_iterator rit = l.rbegin();
    auto rit = l.rbegin();
    while (rit != l.rend())
    {
        cout << *rit << " ";
        ++rit;
    }
    cout << endl;
}

list capacity
在这里插入图片描述

list element access
在这里插入图片描述

list modifiers
在这里插入图片描述

// list插入和删除
// push_back/pop_back/push_front/pop_front
void TestList3()
{
    int array[] = { 1, 2, 3 };
    list<int> L(array, array + sizeof(array) / sizeof(array[0]));

    // 在list的尾部插入4,头部插入0
    L.push_back(4);
    L.push_front(0);
    PrintList(L);

    // 删除list尾部节点和头部节点
    L.pop_back();
    L.pop_front();
    PrintList(L);
}
// insert /erase 
void TestList4()
{
    int array1[] = { 1, 2, 3 };
    list<int> L(array1, array1 + sizeof(array1) / sizeof(array1[0]));

    // 获取链表中第二个节点
    auto pos = ++L.begin();
    cout << *pos << endl;

    // 在pos前插入值为4的元素
    L.insert(pos, 4);
    PrintList(L);

    // 在pos前插入5个值为5的元素
    L.insert(pos, 5, 5);
    PrintList(L);

    // 在pos前插入[v.begin(), v.end)区间中的元素
    vector<int> v{ 7, 8, 9 };
    L.insert(pos, v.begin(), v.end());
    PrintList(L);

    // 删除pos位置上的元素
    L.erase(pos);
    PrintList(L);

    // 删除list中[begin, end)区间中的元素,即删除list中的所有元素
    L.erase(L.begin(), L.end());
    PrintList(L);
}

// resize/swap/clear
void TestList5()
{
    // 用数组来构造list
    int array1[] = { 1, 2, 3 };
    list<int> l1(array1, array1 + sizeof(array1) / sizeof(array1[0]));
    PrintList(l1);

    // 交换l1和l2中的元素
    list<int> l2;
    l1.swap(l2);
    PrintList(l1);
    PrintList(l2);

    // 将l2中的元素清空
    l2.clear();
    cout << l2.size() << endl;
}

list的迭代器失效
迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。

void TestListIterator1()
{
    int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    list<int> l(array, array+sizeof(array)/sizeof(array[0]));
    auto it = l.begin();
    while (it != l.end())
   {
   // erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给其赋值
    l.erase(it);  
    ++it;
   }
}
// 改正
void TestListIterator()
{
    int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    list<int> l(array, array+sizeof(array)/sizeof(array[0]));
    auto it = l.begin();
    while (it != l.end())
   {
       l.erase(it++);    // it = l.erase(it);
   }
}

三、list的练习

模拟实现list的迭代器
List 的迭代器
迭代器有两种实现方式,具体应根据容器底层数据结构实现:

  • 原生态指针,比如:vector
  • 将原生态指针进行封装,因迭代器使用形式与指针完全相同,因此在自定义的类中必须实现以下方法:
    指针可以解引用,迭代器的类中必须重载operator()
    • 指针可以通过->访问其所指空间成员,迭代器类中必须重载oprator->()
    • 指针可以++向后移动,迭代器类中必须重载operator++()与operator++(int)
      至于operator–()/operator–(int)释放需要重载,根据具体的结构来抉择,双向链表可以向前 移动,所以需要重载,如果是forward_list就不需要重载
    • 迭代器需要进行是否相等的比较,因此还需要重载operator==()与operator!=()
template<class T, class Ref, class Ptr>
	class ListIterator
	{
		typedef ListNode<T> Node;
		typedef ListIterator<T, Ref, Ptr> Self;

		// Ref 和 Ptr 类型需要重定义下,实现反向迭代器时需要用到
	public:
		typedef Ref Ref;
		typedef Ptr Ptr;
	public:
		//
		// 构造
		ListIterator(Node* node = nullptr)
			: _node(node)
		{}

		//
		// 具有指针类似行为
		Ref operator*() 
		{ 
			return _node->_val;
		}

		Ptr operator->() 
		{ 
			return &(operator*()); 
		}

		//
		// 迭代器支持移动
		Self& operator++()
		{
			_node = _node->_next;
			return *this;
		}

		Self operator++(int)
		{
			Self temp(*this);
			_node = _node->_next;
			return temp;
		}

		Self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		Self operator--(int)
		{
			Self temp(*this);
			_node = _node->_prev;
			return temp;
		}

		//
		// 迭代器支持比较
		bool operator!=(const Self& l)const
		{ 
			return _node != l._node;
		}

		bool operator==(const Self& l)const
		{ 
			return _node != l._node;
		}

		Node* _node;
	};

注意:

  • 此处typename的作用是明确告诉编译器,Ref是Iterator类中的一个类型,而不是静态成员变量
  • 否则编译器编译时就不知道Ref是Iterator中的类型还是静态成员变量
  • 因为静态成员变量也是按照 类名::静态成员变量名 的方式访问的
template<class Iterator>
	class ReverseListIterator
	{
		// 注意:此处typename的作用是明确告诉编译器,Ref是Iterator类中的一个类型,而不是静态成员变量
		// 否则编译器编译时就不知道Ref是Iterator中的类型还是静态成员变量
		// 因为静态成员变量也是按照 类名::静态成员变量名 的方式访问的
	public:
		typedef typename Iterator::Ref Ref;
		typedef typename Iterator::Ptr Ptr;
		typedef ReverseListIterator<Iterator> Self;
	public:
		//
		// 构造
		ReverseListIterator(Iterator it)
			: _it(it)
		{}

		//
		// 具有指针类似行为
		Ref operator*()
		{
			Iterator temp(_it);
			--temp;
			return *temp;
		}

		Ptr operator->()
		{
			return &(operator*());
		}

		//
		// 迭代器支持移动
		Self& operator++()
		{
			--_it;
			return *this;
		}

		Self operator++(int)
		{
			Self temp(*this);
			--_it;
			return temp;
		}

		Self& operator--()
		{
			++_it;
			return *this;
		}

		Self operator--(int)
		{
			Self temp(*this);
			++_it;
			return temp;
		}

		//
		// 迭代器支持比较
		bool operator!=(const Self& l)const
		{
			return _it != l._it;
		}

		bool operator==(const Self& l)const
		{
			return _it != l._it;
		}

		Iterator _it;
	};

list的反向迭代器
通过前面知道,反向迭代器的++就是正向迭代器的–,反向迭代器的–就是正向迭代器的++,因此反向迭代器的实现可以借助正向迭代器,即:反向迭代器内部可以包含一个正向迭代器,对正向迭代器的接口进行包装即可。

template<class Iterator>
class ReverseListIterator
{
	// 注意:此处typename的作用是明确告诉编译器,Ref是Iterator类中的类型,而不是静态成员变量
	// 否则编译器编译时就不知道Ref是Iterator中的类型还是静态成员变量
	// 因为静态成员变量也是按照 类名::静态成员变量名 的方式访问的
public:
	typedef typename Iterator::Ref Ref;
	typedef typename Iterator::Ptr Ptr;
	typedef ReverseListIterator<Iterator> Self;
public:
	//
	// 构造
	ReverseListIterator(Iterator it) : _it(it) {}
	//
	// 具有指针类似行为
	Ref operator*() {
		Iterator temp(_it);
		--temp;
		return *temp;
	}
	Ptr operator->() { return &(operator*()); }
	//
	// 迭代器支持移动
	Self& operator++() {
		--_it;
		return *this;
	}
	Self operator++(int) {
		Self temp(*this);
		--_it;
		return temp;
	}
	Self& operator--() {
		++_it;
		return *this;
	}
	Self operator--(int)
	{
		Self temp(*this);
		++_it;
		return temp;
	}
	/// 迭代器支持比较
    bool operator!=(const Self& l)const
    { 
       return _it != l._it;
    }
    bool operator==(const Self& l)const
    { 
       return _it != l._it;
    }
    Iterator _it;
};

四、与vector的对比

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

五、总结

  • 容器特点
    list是一种双向链表容器,具有以下显著特点:
    • 动态性强:可以根据需要随时添加或删除元素,无需担心预先分配固定大小的内存问题。
      插入和删除高效:在链表的任意位置进行插入和删除操作的时间复杂度为常数级别,这使得它在频繁进行此类操作的场景下表现出色。
    • 迭代灵活:提供双向迭代器,可方便地进行正向和反向遍历。但不支持随机访问,访问特定位置的元素相对较慢。
  • 适用场景
    • 频繁插入和删除:当需要在容器中频繁地进行插入和删除操作时,list是一个很好的选择。例如,在实现一些数据结构(如栈、队列)或者处理动态变化的数据集合时。
    • 顺序访问:如果主要是按照顺序访问元素,而不是随机访问,list可以满足需求。比如,依次处理一系列数据,而不需要直接跳到特定位置的元素。
  • 与其他容器对比
    与 C++ 中的其他容器(如vector、array等)相比:
    • 与vector:vector支持随机访问,但其在插入和删除元素时,可能需要移动大量元素,时间复杂度可能较高。而list在插入和删除方面更高效,但随机访问能力较弱。
    • 与array:array大小固定,不具备动态性。list则可以根据需要动态调整大小。

总之,C++ 中的list是一种功能强大的容器,在特定的场景下能够发挥出很大的优势。

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

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

相关文章

(四)进入MySQL 【事务】

一、MySQL事务的概念 MySQL 事务主要用于处理操作量大&#xff0c;复杂度高的数据。比如说&#xff0c;在人员管理系统中&#xff0c; 要删除一个人员&#xff0c;即需要删除人员的基本资料&#xff0c;又需要删除和该人员相关的信息&#xff0c;如信箱&#xff0c; 文章等等。…

unity中的InstanceID详解 即Object.GetInstanceID

GetInstanceID 是 Unity 中 Object 类的一个方法,它用于获取一个对象的唯一实例标识符。每个 Unity 对象(如游戏对象、组件、资源等)都有一个唯一的实例 ID,这个 ID 在对象的生命周期内是唯一的。 对于它的生命周期是不确定的。网上说在切换场景或者编辑器关闭重启后会变。…

红黑树刨析(删除部分)

文章目录 红黑树删除节点情景分析情景1&#xff1a;删除节点左右子树都为空情景1.1&#xff1a;删除节点为红色情景1.2&#xff1a;删除节点为黑色情况1.2.1&#xff1a;删除节点的兄弟节点是红色情景1.2.2&#xff1a;删除节点的兄弟节点是黑色情景1.2.2.1&#xff1a;删除节点…

计算机毕业设计选题推荐-大学生竞赛管理系统-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

初识Arduino

什么是Arduino Arduino是一款便捷灵活、方便上手的开源电子原型平台。它包含硬件部分&#xff08;即各种型号的Arduino板&#xff09;、软件部分&#xff08;即Arduino IDE&#xff09;&#xff0c;以及其Arduino社区平台。 Arduino由一个欧洲开发团队于2005年冬季开发&#…

56基于SpringBoot+Vue+uniapp的教学资源库的详细设计和实现(源码+lw+部署文档+讲解等)

文章目录 前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus 系统测试系统测试目的系统功能测试系统测试结论 为什么选择我代码参考数据库参考源码获取源码获取 前言 &#x1f31e;博主介绍 &#xff1a;✌全网粉丝15W,CSDN特邀作者、21…

信息学奥赛初赛天天练-80-NOIP2015普及组-基础题5-错位排列、二叉树、完全二叉树、叶子节点、完全二叉树叶子节点

NOIP 2015 普及组 基础题5 21 重新排列 1234使得每一个数字都不在原来的位置上&#xff0c;一共有( )种排法 22 一棵结点数为 2015的二叉树最多有( )个叶子结点 2 相关知识点 1) 错位排列 考虑一个有n个元素的排列&#xff0c;若一个排列中所有的元素都不在自己原来的位置…

建模杂谈系列250 Hello2Pymc

说明 pymc算是多年的老朋友了&#xff0c;中间失联了好几年。 内容 1 安装 安装更加麻烦了&#xff0c;不能很好的和其他的环境兼容。在官网上&#xff0c;也是建议用conda的方式安装。 conda create -c conda-forge -n pymc_env "pymc>5" conda activate p…

SQL基础——MySQL的优化

简介&#xff1a;个人学习分享&#xff0c;如有错误&#xff0c;欢迎批评指正。 概述 在应用的的开发过程中&#xff0c;由于初期数据量小&#xff0c;开发人员写 SQL 语句时更重视功能上的实现&#xff0c;但是当应用系统正式上线后&#xff0c;随着生产数据量的急剧增长&…

安卓15发布日期确定,安卓15 谷歌GMS认证截止日期有重大变化!安卓版本GMS认证截止时间更新,谷歌GMS认证之MADA/EDLA设备认证截止时间介绍

谷歌正式公布安卓15发布日期&#xff0c;即9月3号&#xff0c;到时&#xff0c;安卓版本的认证时间将会有改变&#xff01;以下是深光标准整理的最新安卓版本的到期时间 详细讲解如何看懂这个图 第一列&#xff1a;OS version (API level) 指安卓版本 第二列&#xff1a;AOS…

软件测试工程师必备的技术能力

今年是我从事软件测试工作的第十年&#xff0c;从功能测试进阶到自动化测试&#xff0c;然后负责稳定性测试团队&#xff0c;进而兼任整个质量团队的技术专项治理&#xff0c;再到基础架构团队的测试专家角色&#xff0c;负责多个技术项目的产品/运营和质量保障工作。可以说绝大…

GNU 汇编语法基础

目录 一、引言 二、GNU 汇编基本结构 1.指令格式 2.注释 3. 段 三、寄存器和寻址方式 1.寄存器命名 2.寻址方式 四、指令集 1.数据传送指令 2.算术运算指令 3.逻辑运算指令 4.控制流指令 五、宏和函数 1.宏定义 2. 函数定义 六、总结 一、引言 在嵌入式系统…

南京观海微电子----VCC、 VDD、VSS、VEE 电压符号解释

一般在数据手册或者是说原理图中你会看到VCC、 VDD、VEE、VSS等不同的符号&#xff0c;那它们有什么区别&#xff0c;并且该怎么记住它们呢。 解释一&#xff1a; VCC&#xff1a;电源电压&#xff08;双极器件&#xff09;&#xff1b;电源电压&#xff08;74系列数字电路&a…

机会约束转化为确定性约束-- 样本均值法

当涉及到新能源消纳的机会约束规划时&#xff0c;我们需要深入理解其背后的原理和采用的方法。以下是对上文内容的更详细且更贴切的展开解释&#xff1a; 机会约束转化为确定性约束-- 样本均值法代码获取戳此处代码获取戳此处代码获取戳此处 新能源消纳的机会约束 新能源&…

计量校准中溯源方法会有哪些不足之处?

随着新型计量器具的不断涌现&#xff0c;现有的计量检定规程或计量校准规范已不能满足计量溯源的需要。特别是一体化大型设备所配备的传感器&#xff0c;如产业生产线之上的压力传感器、流量计、在线电导率仪、在线酸度计为代表的对传感器的检测目前多依据国家检定规程或计量校…

自制深度学习推理框架之表达式层的设计与实现

文章目录 一、表达式Expression二、词法解析2.1 词法定义2.2 词法解析 三、语法解析3.1 语法树的定义3.2 语法树构建3.3 语法树的转换(逆波兰式) 四、表达式层4.1 ExpressionLayer和ExpressionParser类4.2 表达式层的注册4.3 表达式层的输入处理4.4 表达式层的计算过程 五、计算…

分布式计算架构详解以及任务调度方式

信息技术领域重要分支—分布式计算。分布式计算通过将任务分配到多个物理的计算资源上进行处理&#xff0c;以来提高计算效率和资源利用率。今天主讲分布式计算架构的关键组成以及在云服务器背景下任务调度的不同方式&#xff0c;然后再综合来看这些调度策略是怎样适应云环境的…

使用 nuxi build-module 命令构建 Nuxt 模块

title: 使用 nuxi build-module 命令构建 Nuxt 模块 date: 2024/8/31 updated: 2024/8/31 author: cmdragon excerpt: nuxi build-module 命令是构建 Nuxt 模块的核心工具,它将你的模块打包成适合生产环境的格式。通过使用 --stub 选项,你可以在开发过程中加快模块构建速度…

Linux学习——ubuntu安装qt

安装VM的教程就不过多叙述了&#xff0c;这个简单&#xff0c;大家直接下载VM然后创建虚拟机就可以了&#xff0c;那我们今天来讲讲怎么在ubuntu中安装qtcreator. 如果我们的虚拟机是连接网络的&#xff0c;我们可以直接在Ubuntu上的浏览器中直接下载Qt,我们搜索Qt.io就可以&a…

【论文阅读】:Mamba YOLO SSMs-Based YOLO For Object Detection

摘要 Mamba架构已被证明可以有效地捕获长距离的地面依赖关系。 在深度学习技术的快速发展的推动下&#xff0c;YOLO系列为实时目标探测器设定了一个新的基准。在YOLO的基础上&#xff0c;不断探索再参数化、高效层聚合网络和无锚定技术的创新应用。为了进一步提高检测性能&am…