首页 > 编程语言 >C++ STL -- list

C++ STL -- list

时间:2024-04-20 12:00:12浏览次数:23  
标签:Node head STL nullptr list -- tail 节点

list

list是一种基于双向链表的数据结构,适用于需要在序列中执行频繁插入和删除操作的场景

特性

  1. 本质上是一个双向链表,允许在序列的两端和中间执行插入和删除操作
  2. 只能通过迭代器访问元素,即访问元素的时间复杂度为\(O(n)\)
  3. 动态内存管理,内部通过类似指针的节点实现元素存储,一个节点存储了当前元素和前后节点的指针
  4. 迭代器在增加和删除之后仍然有效

基础成员变量

头指针,尾指针以及List大小三个变量
对于list内部每个元素即一个节点,其具有当前元素值,前指针(第一个元素指向最后一个元素),后指针(最后一个元素指向第一个元素)

//整个成员变量
  //节点结构体
  struct Node
  {
    T data;
    Node* prev;
    Node* next;
    Node(const T& value,Node* prevnode = nullptr,Node* nextnode = nullptr):data(value),prev(prevdata),next(nextdata){}
  };
  Node* head;
  Node* tail;
  size_t size;

相关操作的实现

<<运算符重载

template <typename T>
friend std::ostream& operator<<(std::ostream& os,const List<T>& pt)
{
  for(typename List<T>::Node *cur = pt.head,cur;cur = cur->next)
  {
    os << " " << cur->data;
  }
   os << std::endl
   return os;
}

压入头部和尾部

需要考虑原始list为空的情况

 //在末尾添加元素,变换tail节点
    void push_back(const T& value)
    {
        //创建新的节点
        Node* newnode = new Node(value,nullptr,tail);
        //如果tail指针指向的不为空
        if(tail)
        {
            tail->next = newnode;
        }
        //如果tail为空,说明链表为空
        else
        {
            head = newnode;
        }
        tail = newnode;
        size++;
    }
    //在头部添加元素,变换head节点
    void push_front(const T& value)
    {
        Node* newnode = new Node(value,head,nullptr);
        if(head)
        {
            head->prev = newnode;
        }
        else
        {
            tail = newnode;
        }
        head = newnode;
        size++;
    }

从头部和尾部弹出元素

只需要考虑size >0 的情况,利用delete删除节点

void pop_back()
    {
        if(size > 0)
        {
            Node* temp = tail->prev;
            delete tail;
            tail = temp;
            if(tail)
            {
                tail->next = nullptr;
            }
            else
            {
                head = nullptr;
            }
            size--;
        }
    }
    //删除头部元素,改变head节点
    void pop_front()
    {
        if(size > 0)
        {
            Node* temp = head->next;
            delete head;
            head = temp;
            if(head)
            {
                head->prev = nullptr;
            }
            else
            {
                tail = nullptr;
            }
            size--;
        }
    }

根据值删除节点

需要考虑当前list为空,或者当前节点为头节点尾节点的情况

void remove(const T& value)
    {
        Node* node = getNode(value);
        if(!node) return ;
        else if(node!=head && node != tail)
        {
            Node* prevnode = node->prev;
            Node* nextnode = node->next;
            prevnode->next = nextnode;
            nextnode->prev = prevnode;
        }
        else if(node == head && node == tail)
        {
            head = nullptr;
            tail = nullptr;
        }
        else if(node == head)
        {
            head = node->next;
            head->prev = nullptr;
        }
        else
        {
            tail = node->prev;
            tail->next = nullptr;
        }
        size--;
    }

总结

删除list中的特定元素

list中元素不是连续存储的,而且插入和删除不会导致迭代器失效
通过遍历,利用erase实现

template <typename T>
void removeins(std::list<T>& lst, const T& value)
{
  for(auto it = lst.begin();it!=lst.end();)
  {
    if(*it == value)
    {
      it = lst.erase(it);
    }
    else
    {
      it++;
    }
  }
}

list迭代器失效的情况

在插入操作时不会导致任何迭代器失效,而删除操作会导致指向被删除元素的迭代器失效,而其他迭代器不会失效

与vector的关系

内部实现

  • list是双向链表,不支持随机访问
  • vector是一个动态数组,支持随机访问

性能特点

  • list 常数时间内进行插入和删除操作,但访问为\(O(n)\),每个元素存放在单独的内存块中,用指针连接,不需要重新分配整个容器的内存空间
  • vector 常数时间内进行访问,但插入和删除为\(O(n)\),在内存中连续,超出容量,需要进行扩容操作

内存开销

  • list 每个元素都多存储了指向前面和后面的节点指针
  • vector 较小的内存开销

标签:Node,head,STL,nullptr,list,--,tail,节点
From: https://www.cnblogs.com/XTG111/p/18146692

相关文章

  • com.alibaba.druid.pool.DataSourceClosedException: dataSource already closed at S
     适用的druid数据库连接池一直有问题,无法连接,但是什么都没改过。排查了数据库(数据库单独连接没问题)、防火墙、IP白名单等步骤后,重启服务器、重启应用后都无法解决。重启应用过程中发现了应用无法正常启动的情况,这点让人觉得很意外,于是想看下现在服务器上运行的jar包情况,命令是......
  • P8595 「KDOI-02」一个网的路
    P8595「KDOI-02」一个网的路树形dp显然我们贪心的先执行第一种操作,最后再连边。森林中不同棵树互不影响,所以考虑最小化每棵树的操作次数。考虑dp。我们要把一棵树分成若干条链,那么考虑每个子树\(u\)中,节点\(u\)的情况有三种:执行了第一种操作,成为单独一个点。\(u\)......
  • 2023 5月 dp做题记录
    目录5月dp做题记录P1064[NOIP2006提高组]金明的预算方案P1941[NOIP2014提高组]飞扬的小鸟P2679[NOIP2015提高组]子串P1850[NOIP2016提高组]换教室P2831[NOIP2016提高组]愤怒的小鸟P5020[NOIP2018提高组]货币系统P6064[USACO05JAN]NaptimeGP9344去年天......
  • PowerShell 遇到 .ps1,因为在此系统上禁止运行脚本
    PowerShell遇到.ps1,因为在此系统上禁止运行脚本 解决方法:以管理员身份打开PowerShell:查看当前的执行策略:Get-ExecutionPolicy *`Restricted`:不允许任何脚本运行。这是默认设置,也是最安全的设置。*`AllSigned`:只允许运行由受信任的发布者签名的脚本。*`RemoteSign......
  • FFmpeg开发笔记(十五)详解MediaMTX的推拉流
    ​MediaMTX是个开源的轻量级流媒体服务器,它的安装过程参见《FFmpeg开发实战:从零基础到短视频上线》一书的“10.2.2 FFmpeg向网络推流”。MediaMTX下载后的压缩包包括可执行程序mediamtx.exe和配置文件mediamtx.yml,看起来非常简约,但它提供的流媒体服务一点也没缩水。双击mediamtx......
  • 分布式文件系统FastDFS安装教程
     转载链路地址https://www.cnblogs.com/handsomeye/p/9451568.html  前言centos7x642009、vmware16pro(网络仅主机模式)安装libfastcommon获取libfastcommon安装包:wgethttps://github.com/happyfish100/libfastcommon/archive/V1.0.38.tar.gz解压安......
  • L1-009 N个数求和
    #include<bits/stdc++.h>usingnamespacestd;constintinf=0x3f3f3f3f;#definelllonglongstructFraction{ llfenzi,fenmu;};intgcd(inta,intb){ if(b==0)returna; returngcd(b,a%b);}intlcm(inta,intb){ returna/gcd(a,b)*b;}Fracti......
  • js 小写转换,取后缀
    varstrfileExt=tmpFinanceReportFileName.substr(tmpFinanceReportFileName.lastIndexOf(".")).toLowerCase();    if(strfileExt!==".xls"&&strfileExt!=".xlsx"){      alertMsg.error('文件格式只能为Excel文件!&#......
  • 2023 6月 dp做题记录
    目录6月dp做题记录P5664[CSP-S2019]Emiya家今天的饭P8867[NOIP2022]建造军营[ARC115E]LEQandNEQP3800Power收集P3594[POI2015]WIL6月dp做题记录P5664[CSP-S2019]Emiya家今天的饭分析条件,我们要选出来的菜的集合需要满足的限制,集合不为空和烹饪方法互不相同都好......
  • 2023 7月 dp做题记录
    目录7月dp做题记录TheBakeryP5785[SDOI2012]任务安排P3195[HNOI2008]玩具装箱P3648[APIO2014]序列分割7月dp做题记录TheBakery这道题的状态转移并不难列,经典的分段问题,设状态\(dp_{i,j}\)表示前\(i\)个数字分了\(j\)段的最大价值,转移可以写成\(dp_{i,j}=\max(......