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

list的构造

请添加图片描述

explicit list (const allocator_type& alloc = allocator_type());

  • 解释:构造一个空的容器,里面没有任何元素
    `explicit list (size_type n, const value_type& val = value_type(),const allocator_type& alloc = allocator_type());
  • 解释:构造一个包含 n 个元素的容器。每个元素都是 val 的副本
    list (InputIterator first, InputIterator last)
  • 解释:构造一个容器,里面的元素是[first,last),里面的每个元素都来自这个范围里对应的元素,顺序相同 。
    list (const list& x)
  • 解释:构造一个容器,其中包含 x 中每个元素的,顺序相同。
  • 示例:
void test_list1()
{list<int> l1;                         // 构造空的l1list<int> l2(4, 100);                 // l2中放4个值为100的元素list<int> l3(l2.begin(), l2.end());  // 用l2的[begin(), end())左闭右开的区间构造l3list<int> l4(l3);                    // 用l3拷贝构造l4// 以数组为迭代器区间构造l5int array[] = { 16,2,77,29 };list<int> l5(array, array + sizeof(array) / sizeof(int));// 列表格式初始化C++11list<int> l6{ 1,2,3,4,5 };// 用迭代器方式打印l5中的元素list<int>::iterator it = l5.begin();while (it != l5.end()){cout << *it << " ";  //16 2 77 29++it;}cout << endl;// C++11范围for的方式遍历for (auto& e : l5)cout << e << " ";  //16 2 77 29cout << endl;
}

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

1.list iterator的使用

此处,大家可暂时将迭代器理解成一个指针,该指针指向list中的某个节点。
iterator begin();const_iterator begin() const;
iterator end();const_iterator end() const;
reverse_iterator rbegin();const_reverse_iterator rbegin() const
reverse_iterator rend();const_reverse_iterator rend() const;
这里的使用方法和string、vector一样,就不再过多介绍了。

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


2.list capacity

empty
bool empty() const;

  • 解释:检测list是否为空,是返回true,否则返回false
  • 示例:
void test_list2()
{list<int> l1;list<int> l2;l1.push_back(1);l1.push_back(2);l1.push_back(3);l1.push_back(4);cout << l1.empty() << endl;  //0cout << l2.empty() << endl;  //1
}

size
size_type size() const;

  • 解释:返回list中有效节点的个数
    用法跟前面的一样,不在过多阐述。

3.list 的元素的访问

front
reference front();const_reference front() const;

  • 解释:返回list的第一个节点中值的引用
  • 示例:
void test_list3()
{list<int> mylist;mylist.push_back(77);mylist.push_back(22);// now front equals 77, and back 22mylist.front() -= mylist.back();cout << "mylist.front() is now " << mylist.front() << '\n';  //mylist.front() is now 55}

back
reference back();const_reference back() const;

  • 解释:返回list的最后一个节点中值的引用
  • 示例:
void test_list3()
{list<int> mylist;mylist.push_back(10);while (mylist.back() != 0){mylist.push_back(mylist.back() - 1);}cout << "mylist contains:";for (list<int>::iterator it = mylist.begin(); it != mylist.end(); ++it)cout << ' ' << *it;  //mylist contains: 10 9 8 7 6 5 4 3 2 1 0
}

4.list的插入、删除、交换、清空

push_front
请添加图片描述

  • 解释:在list首元素前插入值为val的元素
    pop_front
    请添加图片描述

  • 解释:删除list中第一个元素
    push_back
    请添加图片描述

  • 解释:在list尾部插入值为val的元素
    pop_back
    请添加图片描述

  • 解释:删除list中最后一个元素

  • 示例:

void test_list4()
{int array[] = { 1, 2, 3 };list<int> L(array, array + sizeof(array) / sizeof(array[0]));// 在list的尾部插入4,头部插入0L.push_back(4);L.push_front(0);list<int>::iterator it = L.begin();while (it != L.end()){cout << *it << " ";  //0 1 2 3 4++it;}cout << endl;// 删除list尾部节点和头部节点L.pop_back();L.pop_front();list<int>::iterator it1 = L.begin();while (it1 != L.end()){cout << *it1 << " ";  //1 2 3++it1;}
}

insert
请添加图片描述

  • 解释:在position位置之前,插入值为val的元素。其它形式的用法和之前一样。
    erase
    请添加图片描述

  • 解释:删除position位置的值或者删除某个区间的所有元素。

  • 示例:

void test_list5()
{//这里的PrintList(L)就是上面list的遍历int array1[] = { 1, 2, 3 };list<int> L(array1, array1 + sizeof(array1) / sizeof(array1[0]));// 获取链表中第二个节点auto pos = ++L.begin();cout << *pos << endl;  //2// 在pos前插入值为4的元素L.insert(pos, 4);PrintList(L);  //1 4 2 3// 在pos前插入5个值为5的元素L.insert(pos, 5, 5);PrintList(L);  //1 4 5 5 5 5 5 2 3// 在pos前插入[v.begin(), v.end)区间中的元素vector<int> v{ 7, 8, 9 };L.insert(pos, v.begin(), v.end());PrintList(L);  //1 4 5 5 5 5 5 7 8 9 2 3// 删除pos位置上的元素L.erase(pos);PrintList(L);  //1 4 5 5 5 5 5 7 8 9 3// 删除list中[begin, end)区间中的元素,即删除list中的所有元素L.erase(L.begin(), L.end());PrintList(L);  //
}

swap
请添加图片描述

  • 解释交换两个list中的元素。
    clear
    请添加图片描述

  • 解释:清空list中的有效元素。

  • 示例:

void test_list6()
{// 用数组来构造listint array1[] = { 1, 2, 3 };list<int> l1(array1, array1 + sizeof(array1) / sizeof(array1[0]));PrintList(l1);  //1 2 3// 交换l1和l2中的元素list<int> l2(3, 1);l1.swap(l2);PrintList(l1); //1 1 1PrintList(l2); //1 2 3// 将l2中的元素清空l2.clear();cout << l2.size() << endl;  //0
}

5.list的其它操作

sort
请添加图片描述

  • 解释:对列表中的元素进行排序,更改它们在容器中的位置。(默认是按照升序排列)
  • 示例:
void test_list7()
{list<int> l1 = { 3,2,1,4,5 };auto it = l1.begin();while (it != l1.end()){cout << *it << " ";  //3 2 1 4 5++it;}cout << endl;//升序排列:lessl1.sort();for (list<int>::iterator i = l1.begin(); i != l1.end(); i++){cout << *i << " ";  //1 2 3 4 5}
}

如果想按照降序排列,就按以下的方式写。这里就先展示如何写,到后面的学习在深入讲解这个知识点,也就是“仿函数”。

void test_list7()
{list<int> l1 = { 3,2,1,4,5 };auto it = l1.begin();while (it != l1.end()){cout << *it << " ";++it;}cout << endl;//降序排列:greater//greater<int> gt;//l1.sort(gt);l1.sort(greater<int>());  //推荐这种写法for (list<int>::iterator i = l1.begin(); i != l1.end(); i++){cout << *i << " ";  //5 4 3 2 1}
}

请添加图片描述

  • 解释:对[first,last)范围内的元素进行排序。也可以通过迭代器对指定范围内的元素进行排序。默认是按升序排序,当然也可以通过仿函数来按照降序排列。
  • 示例:
int main()
{vector<int> v = { 6,3,4,5,2,1,7,9,8 };sort(v.begin(), v.end());for (auto i : v){cout << i << " ";  //1 2 3 4 5 6 7 8 9}cout << endl;vector<int> v1 = { 3,2,4,5,6,8,1 };sort(v1.begin(), v1.begin() + 4);for (auto x : v1){cout << x << " ";  //2 3 4 5 6 8 1}return 0;
}

通过对list和算法库里的sort对比。我们可以知道list里的排序没法使用迭代器来进行排序,因为list的底层是带头双向循环链表,当使用end()时,由于像vector和string里的迭代器不同,它们的end是最后一个元素的下一个位置,而在链表中,链表的物理空间并不连续,end的下一个数据就会指向头结点。所以我们要对迭代器进行一定的封装。让迭代器符合统一的迭代器使用规则。
请添加图片描述

那么如何进行封装呢?等到list的模拟实现的时候在给各位细心讲解。


unique
请添加图片描述

  • 解释:从容器中每个连续的相等元素组中删除除第一个元素之外的所有元素,也就是去除重复元素。注意,只有当元素与紧接其前面的元素相等时,才会从列表容器中删除该元素。因此,此函数对于排好序的列表特别有用。(只对于版本1)
  • 示例:
void test_list9()
{list<int> l1 = { 1,2,2,2,3,4,4,2,2,5 };l1.unique();auto i = l1.begin();while (i != l1.end()){cout << *i << " ";  //1 2 3 4 2 5++i;}cout << endl;list<int> l2 = { 1,1,3,4,2,5,5,5,6 };l2.sort();l2.unique();auto x = l2.begin();while (x != l2.end()){cout << *x << " ";  //1 2 3 4 5 6++x;}
}

reverse
请添加图片描述

  • 解释:逆置列表中元素的顺序
  • 示例:
int main()
{list<int> mylist;for (int i = 1; i < 10; ++i) mylist.push_back(i);mylist.reverse();cout << "mylist contains:";for (list<int>::iterator it = mylist.begin(); it != mylist.end(); ++it)cout << ' ' << *it;  //mylist contains: 9 8 7 6 5 4 3 2 1return 0;
}

list的模拟实现

List的模拟实现

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与vector的对比

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

vectorlist
底层结构动态顺序表,一段连续空间带头结点的双向循环链表
随机访问支持随机访问,访问某个元素效率O(1)不支持随机访问,访问某个元素
效率O(N)
插入和删除任意位置插入和删除效率低,需要搬移元素,时间复杂度为O(N),插入时有可能需要增容,增容:开辟新空间,拷贝元素,释放旧空间,导致效率更低任意位置插入和删除效率高,不需要搬移元素,时间复杂度为O(1)
空间利用率底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高底层节点动态开辟,小节点容易造成内存碎片,空间利用率低,缓存利用率低
迭代器原生态指针对原生态指针(节点指针)进行封装
迭代器失效在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器需要重新赋值否则会失效。插入元素不会导致迭代器失效,删除元素时,只会导致当前迭代器失效,其他迭代器不受影响
使用场景需要高效存储,支持随机访问,不关心插入删除效率大量插入和删除操作,不关心随机访问

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

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

相关文章

生物信息学技能树(Bioinformatics)与学习路径

李升伟 整理 生物信息学是一门跨学科领域&#xff0c;涉及生物学、计算机科学以及统计学等多个方面。以下是关于生物信息学的学习路径及相关技能的详细介绍。 一、基础理论知识 1. 生物学基础知识 需要掌握分子生物学、遗传学、细胞生物学等相关概念。 对基因组结构、蛋白质…

AOSP Android14 Launcher3——远程窗口动画关键类SurfaceControl详解

在 Launcher3 执行涉及其他应用窗口&#xff08;即“远程窗口”&#xff09;的动画时&#xff0c;例如“点击桌面图标启动应用”或“从应用上滑回到桌面”的过渡动画&#xff0c;SurfaceControl 扮演着至关重要的角色。它是实现这些跨进程、高性能、精确定制动画的核心技术。 …

超详细实现单链表的基础增删改查——基于C语言实现

文章目录 1、链表的概念与分类1.1 链表的概念1.2 链表的分类 2、单链表的结构和定义2.1 单链表的结构2.2 单链表的定义 3、单链表的实现3.1 创建新节点3.2 头插和尾插的实现3.3 头删和尾删的实现3.4 链表的查找3.5 指定位置之前和之后插入数据3.6 删除指定位置的数据和删除指定…

17.整体代码讲解

从入门AI到手写Transformer-17.整体代码讲解 17.整体代码讲解代码 整理自视频 老袁不说话 。 17.整体代码讲解 代码 import collectionsimport math import torch from torch import nn import os import time import numpy as np from matplotlib import pyplot as plt fro…

前端性能优化:所有权转移

前端性能优化&#xff1a;所有权转移 在学习rust过程中&#xff0c;学到了所有权概念&#xff0c;于是便联想到了前端&#xff0c;前端是否有相关内容&#xff0c;于是进行了一些实验&#xff0c;并整理了这些内容。 所有权转移&#xff08;Transfer of Ownership&#xff09;…

Missashe考研日记-day23

Missashe考研日记-day23 0 写在前面 博主前几天有事回家去了&#xff0c;断更几天了不好意思&#xff0c;就当回家休息一下调整一下状态了&#xff0c;今天接着开始更新。虽然每天的博客写的内容不算多&#xff0c;但其实还是挺费时间的&#xff0c;比如这篇就花了我40多分钟…

Docker 中将文件映射到 Linux 宿主机

在 Docker 中&#xff0c;有多种方式可以将文件映射到 Linux 宿主机&#xff0c;以下是常见的几种方法&#xff1a; 使用-v参数• 基本语法&#xff1a;docker run -v [宿主机文件路径]:[容器内文件路径] 容器名称• 示例&#xff1a;docker run -it -v /home/user/myfile.txt:…

HarmonyOS-ArkUI-动画分类简介

本文的目的是,了解一下HarmonyOS动画体系中的分类。有个大致的了解即可。 动效与动画简介 动画,是客户端提升界面交互用户体验的一个重要的方式。可以使应用程序更加生动灵越,提高用户体验。 HarmonyOS对于界面的交互方面,围绕回归本源的设计理念,打造自然,流畅品质一提…

C++如何处理多线程环境下的异常?如何确保资源在异常情况下也能正确释放

多线程编程的基本概念与挑战 多线程编程的核心思想是将程序的执行划分为多个并行运行的线程&#xff0c;每个线程可以独立处理任务&#xff0c;从而充分利用多核处理器的性能优势。在C中&#xff0c;开发者可以通过std::thread创建线程&#xff0c;并使用同步原语如std::mutex、…

区间选点详解

步骤 operator< 的作用在 C 中&#xff0c; operator< 是一个运算符重载函数&#xff0c;它定义了如何比较两个对象的大小。在 std::sort 函数中&#xff0c;它会用到这个比较函数来决定排序的顺序。 在 sort 中&#xff0c;默认会使用 < 运算符来比较两个对象…

前端配置代理解决发送cookie问题

场景&#xff1a; 在开发任务管理系统时&#xff0c;我遇到了一个典型的身份认证问题&#xff1a;​​用户登录成功后&#xff0c;调获取当前用户信息接口却提示"用户未登录"​​。系统核心流程如下&#xff1a; ​​用户登录​​&#xff1a;调用 /login 接口&…

8.1 线性变换的思想

一、线性变换的概念 当一个矩阵 A A A 乘一个向量 v \boldsymbol v v 时&#xff0c;它将 v \boldsymbol v v “变换” 成另一个向量 A v A\boldsymbol v Av. 输入 v \boldsymbol v v&#xff0c;输出 T ( v ) A v T(\boldsymbol v)A\boldsymbol v T(v)Av. 变换 T T T…

【java实现+4种变体完整例子】排序算法中【冒泡排序】的详细解析,包含基础实现、常见变体的完整代码示例,以及各变体的对比表格

以下是冒泡排序的详细解析&#xff0c;包含基础实现、常见变体的完整代码示例&#xff0c;以及各变体的对比表格&#xff1a; 一、冒泡排序基础实现 原理 通过重复遍历数组&#xff0c;比较相邻元素并交换逆序对&#xff0c;逐步将最大值“冒泡”到数组末尾。 代码示例 pu…

系统架构设计(二):基于架构的软件设计方法ABSD

“基于架构的软件设计方法”&#xff08;Architecture-Based Software Design, ABSD&#xff09;是一种通过从软件架构层面出发指导详细设计的系统化方法。它旨在桥接架构设计与详细设计之间的鸿沟&#xff0c;确保系统的高层结构能够有效指导后续开发。 ABSD 的核心思想 ABS…

Office文件内容提取 | 获取Word文件内容 |Javascript提取PDF文字内容 |PPT文档文字内容提取

关于Office系列文件文字内容的提取 本文主要通过接口的方式获取Office文件和PDF、OFD文件的文字内容。适用于需要获取Word、OFD、PDF、PPT等文件内容的提取实现。例如在线文字统计以及论文文字内容的提取。 一、提取Word及WPS文档的文字内容。 支持以下文件格式&#xff1a; …

Cesium学习笔记——dem/tif地形的分块与加载

前言 在Cesium的学习中&#xff0c;学会读文档十分重要&#xff01;&#xff01;&#xff01;在这里附上Cesium中英文文档1.117。 在Cesium项目中&#xff0c;在平坦坦地球中加入三维地形不仅可以增强真实感与可视化效果&#xff0c;还可以​​提升用户体验与交互性&#xff0c…

Spring Boot 断点续传实战:大文件上传不再怕网络中断

精心整理了最新的面试资料和简历模板&#xff0c;有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 一、痛点与挑战 在网络传输大文件&#xff08;如视频、数据集、设计稿&#xff09;时&#xff0c;常面临&#xff1a; 上传中途网络中断需重新开始服务器内…

数码管LED显示屏矩阵驱动技术详解

1. 矩阵驱动原理 矩阵驱动是LED显示屏常用的一种高效驱动方式&#xff0c;利用COM&#xff08;Common&#xff0c;公共端&#xff09;和SEG&#xff08;Segment&#xff0c;段选&#xff09;线的交叉点控制单个LED的亮灭。相比直接驱动&#xff0c;矩阵驱动可以显著减少所需I/…

【上位机——MFC】菜单类与工具栏

菜单类 CMenu&#xff0c;封装了关于菜单的各种操作成员函数&#xff0c;另外还封装了一个非常重要的成员变量m_hMenu(菜单句柄) 菜单使用 添加菜单资源加载菜单 工具栏相关类 CToolBarCtrl-》父类是CWnd&#xff0c;封装了关于工具栏控件的各种操作。 CToolBar-》父类是CC…

liunx中常用操作

查看或修改linux本地mysql端口 cat /etc/my.cnf 如果没有port可以添加&#xff0c;有可以修改 查看本地端口占用情况 bash netstat -nlt | grep 3307 HADOOP集群 hdfs启动与停止 # 一键启动hdfs集群 start-dfs.sh # 一键关闭hdfs集群 stop-dfs.sh #除了一键启停外&#x…