<C++>STL->list

list的介绍

listimage-20240120113126755

  • list是一个类模板,第一个模板参数为存储数据类型;第二个模板参数为空间适配器
  • list是一个可以在常数时间内完成任意位置的插入和删除的顺序容器。
  • list容器是以双链表的形式实现的;双链表可以将其包含的每个元素存储在不同且不相关的存储位置。每个元素都有一个指向其前面元素的指针和一个指向其后面元素的指针,从而在内部保持排序。
  • 与其他基本标准序列容器(arrayvectordeque)相比,list在插入、提取和移动容器内任何位置的元素(已获得迭代器)时通常表现更好,因此在密集使用这些元素的算法(如排序算法)中也是如此。
  • 与其他序列容器相比,listforward_lists 的主要缺点是它们无法通过元素的位置直接访问元素;例如,要访问列表中的第六个元素,必须从已知位置(如开始或结束位置)迭代到该位置,而这需要花费这些位置之间距离的线性时间。此外,它们还需要消耗一些额外的内存来保存与每个元素相关的链接信息(这可能是由小尺寸元素组成的大列表的一个重要因素)。

list的使用

list的使用和vector类似,主要介绍listvector之间的不同点:

  • 时间复杂度:vector获取元素的时间复杂度为 O ( 1 ) O(1) O(1),尾插和尾删的时间复杂度为 O ( n ) O(n) O(n),其他位置插入和删除是 O ( n ) O(n) O(n);list获取元素的时间复杂度为 O ( n ) O(n) O(n),任意位置插入删除时间复杂度为 O ( 1 ) O(1) O(1)

  • 迭代器失效:vector在insert,erase之后,原本指向插入、删除后面所有元素的迭代器都会失效;list在insert,erase之后,只有指向插入或删除位置的迭代器才会失效

  • clear成员函数:vectorclear函数会将size清零,capacity保持不变;listclear函数会将size清零,只留下头节点

  • 迭代器的实现不同:

    • vector的迭代器底层就是原生指针。即typedef T* std::vector::iterator ,此后对迭代器进行解引用和++就相当于对指针进行解引用和++,可以访问容器的数据和遍历容器。
    • list的迭代器底层是指针,但不是原生指针,而是封装后的指针。在使用上我们需要解引用迭代器可以获得节点的数据,迭代器++可以遍历容器。list的数据结构是链表,链表各个节点的存储是不连续的,所以直接对指针进行解引用和++是不能实现获取链表节点的数据和遍历链表的。我们必须对指针进行运算符重载,但是内置类型不支持运算符重载,需要将指针封装成一个以该指针为成员变量的类,对类进行operator+operator*重载。
    • vector没有的成员函数:
    1. spliceimage-20240120213123046

      • 功能:将元素从 x 传输到容器中,并将它们插入到位置

      • 参数:
        position:容器内插入x元素的位置
        x:与*this具有相同类型元素的list
        i:x中的迭代器,只有单个数据会被传输
        first,last:指向容器x要被传输区间的头和尾部的下一个位置,及区间[first,last)会被传输到*thisposition位置。

      • 返回值:空

      • 注意:再调用函数后,原本指向元素的迭代器仍然指向该元素,但是迭代器可能指向的容器发生改变

      demo:

      // splicing lists
      #include <iostream>
      #include <list>int main ()
      {std::list<int> mylist1, mylist2;std::list<int>::iterator it;// set some initial values:for (int i=1; i<=4; ++i)mylist1.push_back(i);      // mylist1: 1 2 3 4for (int i=1; i<=3; ++i)mylist2.push_back(i*10);   // mylist2: 10 20 30it = mylist1.begin();++it;                         // points to 2mylist1.splice (it, mylist2); // mylist1: 1 10 20 30 2 3 4// mylist2 (empty)// "it" still points to 2 (the 5th element)mylist2.splice (mylist2.begin(),mylist1, it);// mylist1: 1 10 20 30 3 4// mylist2: 2// "it" is now invalid.it = mylist1.begin();std::advance(it,3);           // "it" points now to 30mylist1.splice ( mylist1.begin(), mylist1, it, mylist1.end());// mylist1: 30 3 4 1 10 20std::cout << "mylist1 contains:";for (it=mylist1.begin(); it!=mylist1.end(); ++it)std::cout << ' ' << *it;std::cout << '\n';std::cout << "mylist2 contains:";for (it=mylist2.begin(); it!=mylist2.end(); ++it)std::cout << ' ' << *it;std::cout << '\n';return 0;
      }// splicing lists
      #include <iostream>
      #include <list>int main ()
      {std::list<int> mylist1, mylist2;std::list<int>::iterator it;// set some initial values:for (int i=1; i<=4; ++i)mylist1.push_back(i);      // mylist1: 1 2 3 4for (int i=1; i<=3; ++i)mylist2.push_back(i*10);   // mylist2: 10 20 30it = mylist1.begin();++it;                         // points to 2mylist1.splice (it, mylist2); // mylist1: 1 10 20 30 2 3 4// mylist2 (empty)// "it" still points to 2 (the 5th element)mylist2.splice (mylist2.begin(),mylist1, it);// mylist1: 1 10 20 30 3 4// mylist2: 2// "it" is now invalid.it = mylist1.begin();std::advance(it,3);           // "it" points now to 30mylist1.splice ( mylist1.begin(), mylist1, it, mylist1.end());// mylist1: 30 3 4 1 10 20std::cout << "mylist1 contains:";for (it=mylist1.begin(); it!=mylist1.end(); ++it)std::cout << ' ' << *it;std::cout << '\n';std::cout << "mylist2 contains:";for (it=mylist2.begin(); it!=mylist2.end(); ++it)std::cout << ' ' << *it;std::cout << '\n';return 0;
      }
      

      Output:

      mylist1 contains: 30 3 4 1 10 20
      mylist2 contains: 2
      
    2. removeimage-20240120220101850

      • 功能:移除容器中所有值为val的数据,会调用移除数据对应的析构函数,同时会减少容器的size。
      • 参数:需要移除的元素,类型value_type是类模板T的重定义
      • 返回值:无
      • 注意:
        • remove函数是删除所有值为val的数据,erase删除迭代器pos处的位置。
        • 指向val数据处的迭代器会失效,其余的不会失效。

      demo:

      // remove from list
      #include <iostream>
      #include <list>int main ()
      {int myints[]= {17,89,7,14};std::list<int> mylist (myints,myints+4);mylist.remove(89);std::cout << "mylist contains:";for (std::list<int>::iterator it=mylist.begin(); it!=mylist.end(); ++it)std::cout << ' ' << *it;std::cout << '\n';return 0;
      }
      

      output:

      mylist contains: 17 7 14
      
    3. remove_if:删除满足条件的数据,条件需要通过仿函数传递, 等我们讲解仿函数后再来讲解。

    4. uniqueimage-20240120221434347

      • 功能:删除连续的重复元素,所有重复元素只保留第一个
      • 参数:
        第一个版本是严格删除所有值相等的连续元素。第二个版本我们可以根据传参自己定义“相同性”,通过仿函数实现。
      • 返回值:空
      • 注意:指向删除元素的迭代器会失效。

      demo:

      output:

    5. mergeimage-20240120222422941

      • 功能:通过将 x 的所有元素在各自有序位置传输到容器中(两个容器都应已排序),将 x 合并到列表中。
      • 参数:
        • x:与*this具有相同类型的list,函数调用结束后x会被修改
        • comp:仿函数参数,决定具体是如何比较的
      • 返回值:空
      • 注意:
        • 执行merge的前提是两个list都是有序的,list无序请使用std::list::splicex中的元素按照operator<或者comp定义的比较方法找到合适的位置进行插入,由此产生的等价元素顺序是稳定的。
        • 执行完后,xsize会变为0。
        • 执行该操作时,既不构造也不销毁任何元素。
        • 如果this==&x,则函数不会执行任何操作。
        • 调用函数之后,迭代器仍然指向之前的元素,但是指向的可能从一个容器变成另一个容器。

      demo:

      // list::merge
      #include <iostream>
      #include <list>// compare only integral part:
      bool mycomparison (double first, double second)
      { return ( int(first)<int(second) ); }int main ()
      {std::list<double> first, second;first.push_back (3.1);first.push_back (2.2);first.push_back (2.9);second.push_back (3.7);second.push_back (7.1);second.push_back (1.4);first.sort();second.sort();first.merge(second);// (second is now empty)second.push_back (2.1);first.merge(second,mycomparison);std::cout << "first contains:";for (std::list<double>::iterator it=first.begin(); it!=first.end(); ++it)std::cout << ' ' << *it;std::cout << '\n';return 0;
      }
      

      output:

      first contains: 1.4 2.2 2.9 2.1 3.1 3.7 7.1
      

      请注意,在第二次合并中,函数 mycomparison(仅比较整数部分)没有考虑 2.1 低于 2.2 或 2.9,因此它被插入到它们之后、3.1 之前。

    6. sortimage-20240120224158440

      • 功能:对列表中的元素进行排序,改变它们在容器中的位置。排序是通过使用 operator<(版本(1))或 comp(版本(2))来比较元素的算法进行的。这种比较应产生严格的元素弱排序(即一致的反式比较,不考虑其反射性)。
      • 参数:二元谓词,在取两个与列表中的值类型相同的值时,如果第一个参数在其定义的严格弱排序中位于第二个参数之前,则返回 true,否则返回 false。该谓词应是函数指针或函数对象。
      • 返回值:空
      • 注意:
        • 整个操作不涉及任何元素对象的构造、销毁或复制。元素在容器内移动。
        • 迭代器不会失效,还是只想之前的元素。

      demo:

      // list::sort
      #include <iostream>
      #include <list>
      #include <string>
      #include <cctype>// comparison, not case sensitive.
      bool compare_nocase (const std::string& first, const std::string& second)
      {unsigned int i=0;while ( (i<first.length()) && (i<second.length()) ){if (tolower(first[i])<tolower(second[i])) return true;else if (tolower(first[i])>tolower(second[i])) return false;++i;}return ( first.length() < second.length() );
      }int main ()
      {std::list<std::string> mylist;std::list<std::string>::iterator it;mylist.push_back ("one");mylist.push_back ("two");mylist.push_back ("Three");mylist.sort();std::cout << "mylist contains:";for (it=mylist.begin(); it!=mylist.end(); ++it)std::cout << ' ' << *it;std::cout << '\n';mylist.sort(compare_nocase);std::cout << "mylist contains:";for (it=mylist.begin(); it!=mylist.end(); ++it)std::cout << ' ' << *it;std::cout << '\n';return 0;
      }
      

      output:

      mylist contains: Three one two
      mylist contains: one Three two
      

      对于默认字符串,比较是严格的字符代码比较,其中所有大写字母都比所有小写字母小,在第一次排序操作中将所有以大写字母开头的字符串放在前面。

    7. reverse image-20240120224901029

      • 功能:逆置list中的数据。
      • 参数:空
      • 返回值:空
      • 注意:迭代器不会失效,让然指向之前的元素,但是位置可能发生改变

list的实现

  • 有关list的拷贝时,注意深拷贝问题!

  • 实现list的重头主要是迭代器的实现,其余的操作和双链表没有什么区别。下面直接直接看代码~

    /*list.h*/
    #define _CRT_SECURE_ 1
    #pragma once
    #include <iostream>
    #include <assert.h>
    #include <algorithm>
    #include <string>
    #include <list>
    using std::cout;
    using std::cin;
    using std::endl;
    using std::string;
    namespace myList
    {/*定义节点类型*/template<class T>struct Node{T _val;Node* _next;Node* _prev;Node(const T& val = T()):_val(val),_next(nullptr),_prev(nullptr){}};/*定义list迭代器,我们想让list迭代器的++和*与普通指针操作不同所以要将迭代器封装为一个以指向节点的指针为成员变量的类,对类进行运算符重载*/template <class T, class Ref, class Ptr>struct __list_iterator	//封装的迭代器类{typedef Node<T> Node;typedef __list_iterator<T, Ref, Ptr> self;//简化迭代器的名称/*成员变量---指向节点的指针*/Node* _node;/*成员函数*/__list_iterator( Node* node)			  //单参数构造函数:_node(node)				{}__list_iterator(const self& it)			  //拷贝构造函数{_node = it._node;}//重载后缀++								  //迭代器++相当于指向节点的指针指向链表中的下一个节点---_node=_node->nextself operator++(int){self tmp(*this);_node = _node->_next;return tmp;}//重载前缀++self& operator++(){_node = _node->_next;return *this;}//重载后缀--self operator--(int){self tmp(*this);_node = _node->_prev;return tmp;}//重载前缀--self& operator--(){_node = _node->_prev;return *this;}//重载*Ref operator*()							   //迭代器解引用相当于访问链表节点的数据与---_node->_val{return _node->_val;}//重载->Ptr operator->()						   //list存储自定义类型时,重载->使得通过it->自定义成员可以直接访问自定义类型{return &_node->_val;}//重载==bool operator==(const self& it)			   //2个迭代器指向链表的节点是否一样{return _node == it._node;}//重载!=bool operator!=(const self& it){return _node != it._node;}};template<class T>class list{private:Node<T>* _head;							  //指向头节点public:typedef Node<T> Node;//泛型编程typedef __list_iterator<T, T&, T*> iterator;//普通迭代器的operator* operator->返回值所引用/指向的对象是可以修改的,返回普通引用/指针typedef __list_iterator<T, const T&, const T*> const_iterator;//const迭代器的operator* operator->的返回值所引用/指向的对象不可以修改//无法对*it或it->__进行写入,返回常引用/指向常量的指针//下面直接返回指针,通过单参数构造函数的隐式类型转换,可以根据返回的指针构造一个对象,传值时在进行拷贝构造-->会优化为直接构造iterator begin(){return _head->_next;//单参数构造函数的隐式类型转换}const_iterator begin() const{return _head->_next;//单参数构造函数的隐式类型转换}iterator end(){return _head;		//单参数构造函数的隐式类型转换}const_iterator end() const{return _head;		//单参数构造函数的隐式类型转换}//初始化一个带头双向循环链表void empty_init(){_head = new Node;_head->_prev = _head->_next = _head;}list(){empty_init();}list(int n, const T& val = T())					//fill构造函数{assert(n >= 0);empty_init();while (n--){push_back(val);}}template<class InputIterator>list(InputIterator first, InputIterator last)  //range构造函数{empty_init();while (first != last){push_back(*first++);}}//lt1(lt2)拷贝时注意深拷贝问题,使用push_back可以进行深拷贝list(const list<T>& lt){empty_init();for (auto e : lt){push_back(e);}}/*修改类函数*/void push_back(const T& val){insert(end(), val);}void push_front(const T& val){insert(begin(), val);}void pop_back(){erase(--end());}void pop_front(){erase(begin());}iterator insert(iterator pos, const T& val)//pos位置前插入元素{Node* newnode = new Node(val);Node* cur = pos._node;Node* prev = pos._node->_prev;//连接节点prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return pos;}iterator erase(iterator pos){assert(pos != _head);//不能删除头节点Node* prev = pos._node->_prev;Node* next = pos._node->_next;prev->_next = next;next->_prev = prev;delete pos._node;return next;}void clear()//除头节点外所有节点全部删除{iterator it = begin();while (it != end()){it = erase(it);}}void swap(list<T>& lt)//交换两个list的值{std::swap(_head, lt._head);}//lt1=lt3list<T>& operator=( list<T> lt){swap(lt);return *this;}size_t size(){size_t sz = 0;iterator it = begin();while (it != end()){sz++;}return sz;}~list(){clear();delete _head;}};//test1现在不是成员函数void test1(){int arr[5] = { 1, 3 ,5, 7, 9 };list<int> lt1(arr, arr + 5);for (auto e : lt1){cout << e << " ";}cout << endl;list<int> lt2(3, 1);for (auto e : lt2){cout << e << " ";}cout << endl;cout << endl;}void test2(){list<string> lt_str;lt_str.push_back("hello");lt_str.push_back("list");lt_str.push_back("!!!");list<string> lt_copy_str(lt_str);for (auto e : lt_copy_str){cout << e << endl;}lt_copy_str.pop_back();lt_copy_str.pop_front();for (auto e : lt_copy_str){cout << e << endl;}lt_str = lt_copy_str;for (auto e : lt_str){cout << e << endl;}}
    }/*main.cpp*/
    #define _CRT_SECURE_NO_WARNINGS 1
    #include "List.h"
    //using namespace std;
    int main()
    {myList::test1();myList::test2();return 0;
    }
    

    output:image-20240121094627973

说明:list的模板参数有三个

  • 第一个参数class T决定list存储的数据类型。
  • 第二个参数class Ref决定operator*的返回值类型
    1. iterator的operator*返回值类型为可以修改的引用T&
    2. const_iterator的operator*返回值类型为不可修改的引用const T&
  • 第三个参数clss Ptr决定operator->的返回值类型
    1. iterator的operator*返回值类型为指向对象可修改的指针T*
    2. const_iterator的operator->返回值类型为指向不可修改的对象的指针const T*
struct AA
{int a;int b;
};

重载operator->后,假若list中存放的是自定义类型AA,可以通过it->a1访问自定义类型中的成员image-20240121100158968

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

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

相关文章

批量转换:速度与质量的完美结合

你是否曾经遇到过需要批量转换上千条视频格式的挑战&#xff1f;这种任务不仅耗时&#xff0c;而且需要专业的视频处理技术。那么&#xff0c;有没有一种简单而高效的方法来完成这个任务呢&#xff1f;答案是肯定的。接下来&#xff0c;我将为你介绍一种快速、稳定的一分钟批量…

VMware workstation平台下配置Fedora-Server-39-1.5虚拟机网络

VMware workstation平台下配置Fedora-Server-39-1.5虚拟机网络 Fedora包含的软件以自由及开放源码许可来发布&#xff0c;并旨在成为该技术领域的领先者。Fedora在专注创新、抢先集成新技术、与上游Linux社区紧密工作方面拥有良好名声。该文档适用于在VMware workstation平台下…

meshgrid contour contourf

meshgrid contour contourf 参考video: https://www.bilibili.com/video/BV1qW411A775/?spm_id_from333.337.search-card.all.click&vd_sourced171c31a396363b8ea8c0e92a59cee6b 官方文档: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.contourf.html#ma…

网络要素服务(WFS)详解

文章目录 1. 概述2. GetCapabilities3. DescribeFeatureType4. GetFeature4.1 Get访问方式4.2 Post访问方式 5. Transaction5.1 Insert5.2 Replace5.3 Update5.4 Delete 6 注意事项 1. 概述 前置文章&#xff1a; 地图服务器GeoServer的安装与配置 GeoServer发布地图服务&#…

Postman如何做接口测试:如何导入 swagger 接口文档

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

透明拼接屏在汽车领域的应用

随着科技的进步&#xff0c;透明拼接屏作为一种新型的显示技术&#xff0c;在汽车领域的应用越来越广泛。尼伽小编将围绕透明拼接屏在汽车本身、4S店、展会、工厂等方面的应用进行深入探讨&#xff0c;并展望未来的设计方向。 一、透明拼接屏在汽车本身的应用 车窗显示&#x…

Unity工程没有创建.sln文件,导致打开C#文件无法打开解决方案

最近又开始折腾些Unity的小项目&#xff0c;重新遇到一些常见的小问题 点击报错文件 却没有打开文件 于是查看了下打开Window->Package Manager 选择Unity Registry 搜索Visual Studio Editor&#xff0c;发现并没有安装 同理&#xff0c;也可以安装VSCode的插件 问题解决了…

【BBuf的CUDA笔记】十三,OpenAI Triton 入门笔记一

0x0. 前言 2023年很多mlsys工作都是基于Triton来完成或者提供了Triton实现版本&#xff0c;比如现在令人熟知的FlashAttention&#xff0c;大模型推理框架lightllm&#xff0c;diffusion第三方加速库stable-fast等灯&#xff0c;以及很多mlsys的paper也开始使用Triton来实现比…

谁适合选择虚拟化

情况 前些天,有人问弄虚拟化怎么样: 还有一个群里,讨论了这事: 也弄了很多年了,虽然不算深入,毕竟,也是拼尽了全力,毕竟差不多7年的时光已经投入进去了,回头时,感觉没留下什么,有些十年技术一场空的感觉,真是应了虚拟化这几个字。 现在就大体说说这事: 先看看当前…

基于TableAgent实现IT职位招聘数据分析—以传统机器学习与TableAgent 数据分析方式相对比以凸显TableAgent 特性

目录 &#x1f680;一. TableAgent—新AI时代的数据分析智能体 &#x1f50e;1.1 基于DataCanvas Alaya九章元识大模型 &#x1f50e;1.2 TableAgent的亮点 &#x1f680;二. 使用TableAgent分析数据与传统机器学习分析数据对比 &#x1f50e;2.1 项目背景 &#x1f50e;2.2 数…

vue3-组件通信

1. 父传子-defineProps 父组件&#xff1a; <script setup> import { ref } from vueimport SonCom from /components/Son-com.vueconst message ref(parent)</script><template><div></div><SonCom car"沃尔沃" :message"…

将输入框的数据输出成json

刚学&#xff0c;做一下记录 先上效果图&#xff0c;来不及解释了&#xff0c;后面再说 源码&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1pV8hDVwpB1kresHag7gIew 提取码&#xff1a;**** 操作&#xff1a; 进入项目目录下&#xff0c;cmd 清除npm缓存&#xff1…