C++STL【list链表】

list

1. list介绍

  • list文档(非官方)
    官方文档
  • list是双向带头循环链表,它可以在常数范围内的任意位置进行插入和删除操作。
  • list的迭代器是双向迭代器(bidirectional iterator),它可以前后双向迭代。
    由容器的底层结构决定,STL迭代器有不同的种类:
    在这里插入图片描述
  • list的底层空间如下图所示:
    在这里插入图片描述
  • 正因list的底层空间如此,它相比其他容器:
    优点:在任意位置插入、删除元素的执行效率更高(相比vector、deque等);
    缺点:不支持位置的随机访问,要访问某个节点,需要迭代器迭代;需要开辟额外的空间来储存下一个节点、上一个节点的地址;缓存命中率低。

2. list的常用接口

  • 与vector一样,list的许多接口中有很多别名:
    在这里插入图片描述
  1. 构造函数(constructor)

    (constructor)功能
    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值初始化
    template <class InputIterator>
    list (InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type());
    用迭代器初始化(可以允许其他类型作为参数初始化)
    list (const list& x);拷贝构造函数
    list<int> lt1;
    list<int> lt2(3, 2);
    int nums[] = { 1,2,3 };
    list<int> lt3(arr, arr + 3);
    list<int> lt4(v3);
    
  2. 容量操作

    函数功能
    size_type size(); const返回有效元素个数
    bool empty(); const判断是否为空
    void resize (size_type n, value_type val = value_type());改变有效元素个数
  3. 迭代器

    函数功能
    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;
    返回容器开头的前一个位置的迭代器

    关于list的迭代器:

    1. 迭代器一般是指针,在string和vector中,因为底层空间是连续的,所以它们的迭代器可以直接使用指针;但是list不行,因为它的空间不是连续的,是多块由指针连接的小空间。因此list的迭代器必须做出改变,即重新封装。

    2. list的迭代器如下:
      在这里插入图片描述
      list迭代器被重新封装成为一个类,它的成员变量是一个节点的类型的指针,指向节点;另外在迭代器类中,还实现了一系列运算符重载,如*/->/!=/==/++/--,使得其与别的容器的迭代器功能一致。

    3. 关于迭代器内运算符->的重载:

    重载operator->,编译器进行了特殊处理。

    struct A
    {A(int a1 = 0, int a2 = 0): _a1(a1), _a2(a2){ }int _a1;int _a2;
    };
    void Test()
    {list<A> la;A aa(1, 2);la.push_back(aa);list<A>::iterator it = la.begin();cout << it->_a1 << endl;
    }
    

    operator->返回的是A*it->_a1就等价于 A * _ a1,这不合语法,正确写法应该是it->->_a1,但是这么写很难看,于是编译器做了优化,自动替我们加上了一个->,于是我们只需写成这样即可:it->_ val

    1. 迭代器提供了一种不暴露容器底层设计的方法来访问容器内容。
  4. 访问元素

    函数功能
    reference front();
    const_reference front() const;
    访问链表开头一个元素
    reference back();
    const_reference back() const;
    访问链表最后一个元素
    int nums[] = { 1,2,3 };
    list<int> lt(arr, arr + 3);
    cout << lt.front() << endl;
    cout << lt.back() << endl;
    
  5. 修改操作

    函数功能
    void push_back (const value_type& val);尾插一个值
    void pop_back();尾删一个值
    void push_front (const value_type& val);头插一个值
    void pop_front();头删一个值
    insert在某个位置插入元素
    erase删除某个位置的元素
    void swap (list& x);交换两个list对象
    void clear();清空有效元素
  6. 链表操作
    STL还提供了一些链表操作,如sort、reverse等,但是不常使用,这里就不多介绍了,若要查看,请移步官方文档。

3. 模拟实现list(部分接口)

#pragma once
#include<iostream>
#include<assert.h>using namespace std;namespace Myspace
{template <class T>struct list_node{list_node(const T& value = T()): _next(nullptr), _pre(nullptr), _val(value){ }list_node<T>* _next;list_node<T>* _pre;T _val;};// 正向迭代器template<class T, class Ref, class Ptr>struct __list_iterator{typedef __list_iterator<T, Ref, Ptr> Self; // Self表示自己typedef list_node<T> Node;__list_iterator(Node* node = nullptr): _node(node){ }Self& operator++(){_node = _node->_next;return *this;}Self operator++(int){Self tmp = *this;_node = _node->_next;return tmp;}Self& operator--(){_node = _node->_pre;return *this;}Self operator--(int){Self tmp = *this;_node = _node->_pre;return tmp;}Ref operator*(){return _node->_val;}Ptr operator->(){return &(_node->_val);}bool operator!=(const Self& it) const{return it._node != _node;}bool operator==(const Self& it) const{return it._node == _node;}Node* _node; // 当前迭代器所指向的节点的指针};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;///// <构造>void CreatHead(){_head = new Node;_head->_next = _head->_pre = _head;}list(){CreatHead();}list(int n, const T& _value = T()){CreatHead();while (n--){push_back(_value);}}template<class InputIterator>list(InputIterator first, InputIterator last){CreatHead();while (first != last){push_back(*first);++first;}}list(const list<T>& lt){CreatHead();//for (auto e : lt)//{//	push_back(e);//}list<T> temp(lt.begin(), lt.end());this->swap(temp);}list<T> operator=(list<T> lt){if (lt._head != _head){swap(lt);}return *this;}///// <迭代器>iterator begin(){return _head->_next; // 单参数的构造函数,可以进行整型提升,Node* 提升为iterator}iterator end(){return _head;}const_iterator begin() const{return _head->_next; // 单参数的构造函数,可以进行整型提升,Node* 提升为iterator}const_iterator end() const{return _head;}///// <容量>size_t size(){size_t sz = 0;Node* cur = _head->_next;while (cur != _head){sz++;++cur;}return sz;}bool empty(){return _head == _head->_pre;}void resize(size_t n, const T& value = T()){size_t sz = size();if (n < sz){while (n < sz){pop_back();sz--;}}else{while (sz < n){push_back(value);sz++;}}}///// <访问>T& front(){return _head->_next->_val;}const T& front() const{return _head->_next->_val;}T& back(){return _head->_pre->_val;}const T& back() const{return _head->_pre->_val;}///// <删除、插入>void push_back(const T& value){insert(end(), value);}void pop_back(){erase(--end());}void push_front(const T& value = T()){insert(begin(), value);}void pop_front(){erase(begin());}iterator insert(iterator pos, const T& value = T()){Node* newnode = new Node(value); // 待插入的新节点Node* cur = pos._node; // 取出pos迭代器的节点newnode->_pre = cur->_pre;newnode->_next = cur;cur->_pre->_next = newnode;cur->_pre = newnode;return newnode;}iterator erase(iterator pos){assert(pos._node != _head);Node* cur = pos._node;Node* ret = cur->_next; // 保留一下返回值cur->_pre->_next = cur->_next;cur->_next->_pre = cur->_pre;delete cur;return ret;}void clear(){Node* cur = _head->_next;while (cur != _head){_head->_next = cur->_next;delete cur;cur = _head->_next;}_head->_next = _head->_pre = _head;}void swap(list<T>& lt){std::swap(lt._head, _head);}private:Node* _head;};
}

注意:

  1. 构造函数vector(int n, const T& value = T())接口需要重载多个(int/size_t/long),以防止创建对象时(vector<int> v(2,3);)编译器自动匹配到vector(InputIterator first, InputIterator last)这个接口。
    原因:编译器总会选择最匹配的接口。
  2. 在扩容时拷贝数据的时候,不要使用memcpy(上述代码的第151行),原因如下:
    如果模板实例化为string,那么此时就相当于memcpy(string1, string2, size),将两个string类memcpy,一定是浅拷贝,因为string类本身有个char*的指针,需要动态开辟空间,memcpy仅仅是将两个指针指向了同一块空间,并没有开辟新的空间。
    直接赋值即可,赋值会调用string自己的赋值运算符重载,它自己会实现深拷贝。
    不止是string,其他任何动态管理空间的类都是如此。

4. 迭代器失效

list的结构是链状表,正因为它不是一块连续的空间,它的迭代器失效问题没有string和vector多。

只有在删除数据的时候,才可能引起迭代器失效。

  • 链表的insert没有迭代器失效的问题!
    因为没有扩容,不需要挪动数据,位置没有改变,迭代器还是指向那个节点。

  • 但是earse有迭代器失效的问题!
    因为节点释放了,迭代器却依旧指向那个位置。
    STL为erase增添了返回值,我们用完erase后要接收它的返回值,它返回的是删除的节点的下一个节点。

`,将两个string类memcpy,一定是浅拷贝,因为string类本身有个char*的指针,需要动态开辟空间,memcpy仅仅是将两个指针指向了同一块空间,并没有开辟新的空间。
直接赋值即可,赋值会调用string自己的赋值运算符重载,它自己会实现深拷贝。
不止是string,其他任何动态管理空间的类都是如此。

4. 迭代器失效

list的结构是链状表,正因为它不是一块连续的空间,它的迭代器失效问题没有string和vector多。

只有在删除数据的时候,才可能引起迭代器失效。

  • 链表的insert没有迭代器失效的问题!
    因为没有扩容,不需要挪动数据,位置没有改变,迭代器还是指向那个节点。

  • 但是earse有迭代器失效的问题!
    因为节点释放了,迭代器却依旧指向那个位置。
    STL为erase增添了返回值,我们用完erase后要接收它的返回值,它返回的是删除的节点的下一个节点。

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

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

相关文章

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之RowSplit容器组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之RowSplit容器组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、RowSplit容器组件 将子组件横向布局&#xff0c;并在每个子组件之间插入一…

数据处理分类、数据仓库产生原因

个人看书学习心得及日常复习思考记录&#xff0c;个人随笔。 数据处理分类 操作型数据处理&#xff08;基础&#xff09; 操作型数据处理主要完成数据的收集、整理、存储、查询和增删改操作等&#xff0c;主要由一般工作人员和基层管理人员完成。 联机事务处理系统&#xff…

Spring MVC HandlerMethodArgumentResolver原理解析

在Spring MVC框架中&#xff0c;HandlerMethodArgumentResolver接口扮演着非常重要的角色。它负责解析请求中的参数&#xff0c;并将其转换为处理器方法所需要的参数值。这种解析过程确保了HTTP请求的参数能够被正确地传递给后端控制器&#xff0c;从而实现请求到处理器方法的绑…

网民用户身份信息实名认证-身份证号+姓名实名认证 GO语言接口代码

翔云身份证二要素实名认证接口&#xff0c;是保护网民数字身份安全的重要防线&#xff0c;更是人们心中守护信息安全的坚实盾牌。有效阻止了网民信息泄露。 以下是go语言调用翔云身份证号实名认证API的代码&#xff1a; package mainimport ("fmt""bytes"…

Common Sense Machines(CSM):立志成为图像生成适用于游戏引擎的3D资产AI产品

详细说明 Common Sense Machines&#xff08;CMS&#xff09;&#xff1a;立志成为图像生成适用于游戏引擎的3D资产AI产品-喜好儿aigc详细说明&#xff1a;https://heehel.com/CSM-3d 官方网站&#xff1a;https://www.csm.ai/ 使用体验网址&#xff1a;https://3d.csm.ai/ 来…

lowcode-engine接入编辑器

https://lowcode-engine.cn/site/docs/guide/create/useEditor 方案1 pnpm init pnpm add "alilc/create-elementlatest"pnpm create "alilc/element" editor-project-name选择编辑器 进入执行pnpm install命令安装包 pnpm start报错 pnpm add &qu…

苹果曝出两个 iOS 系统 0-Day 漏洞

最近&#xff0c;苹果公司发布了紧急安全更新&#xff0c;解决了两个 iOS 零日漏洞。这些漏洞存在于 iOS 内核&#xff08;CVE-2024-23225&#xff09;和 RTKit&#xff08;CVE-2024-23296&#xff09;中&#xff0c;威胁攻击者可利用其绕过内核内存保护&#xff0c;这就给了具…

解决vue项目本地开发完成后部署到服务器后报404的问题

一、如何部署 前后端分离开发模式下&#xff0c;前后端是独立布署的&#xff0c;前端只需要将最后的构建物上传至目标服务器的web容器指定的静态目录下即可 我们知道vue项目在构建后&#xff0c;是生成一系列的静态文件 常规布署我们只需要将这个目录上传至目标服务器即可 /…

linux循环之while循环

1.while循环语法 while 循环控制条件 do commands done #这种结构在循环的开头判断循环控制条件是否满足&#xff0c;如果条件一直满足&#xff0c;那么就一直循环下去。与for循环的区别是&#xff0c;while循环更适合在循环次数未知的情况下使用。 2.while循环案例 [ro…

ChatGPT提问技巧——控制温度和TOP-P样本

ChatGPT提问技巧——控制温度和TOP-P样本 “控制温度和Top-P抽样”在自然语言处理中&#xff0c;控制温度是指通过调整生成文本的随机性和多样性&#xff0c;而Top-P抽样是一种生成文本的策略&#xff0c;它选择概率最高的前P个词作为候选词汇。这两个技术常用于生成文本的质量…

Android学习笔记 Dialog

## Android Dialog 基础用法 Dialog 类是对话框的基类&#xff0c;但应该避免直接实例化 Dialog&#xff0c;而应使用其子类&#xff0c;比如 AlertDialog。此类对话框可以显示标题、提示信息、按钮、可选择项列表或自定义布局等。另外还有 DatePickerDialog 或 TimePickerDia…

文本溢出隐藏 显示省略号,鼠标悬浮展示 el-tooltip(TooltipIsShowMixin封装)

目录 mixins 封装使用 TooltipIsShowMixin效果展示 mixins 封装 TooltipIsShowMixin.js export const TooltipIsShowMixin {data() {return {tooltipIsShow: false}},methods: {tooltipIsDisHandler(className) {this.$nextTick(() > {const dom document.querySelector…

golang 糟糕的错误处理

关于golang的糟糕错误处理&#xff0c;我持反对意见&#xff0c;因此写个博客记录一下 golang的书中说&#xff1a;像下面代码一样&#xff0c;创建一个布尔型变量用于测试错误条件是多余的&#xff1a; 然而在个人看来&#xff0c;代码非常完美&#xff0c;言简意赅&#xff0…

PDF处理控件aspose.PDF功能演示:将 PDF 转换为 Word 文档

在 Web 应用程序中处理文档时&#xff0c;将 PDF 文件无缝转换为 Word 文档的能力是一项宝贵的资产。此任务不仅常见&#xff0c;而且对于文档转换器和编辑器、从编辑和协作到内容提取的各种应用程序来说也是必不可少的。在这篇博文中&#xff0c;我们将探讨如何使用 JavaScrip…

算法中常用知识

定义大值和小值 NT_MIN在标准头文件limits.h中定义。 #define INT_MAX 2147483647 #define INT_MIN (-INT_MAX - 1) int ans INT_MIN;在C/C语言中&#xff0c;不能够直接使用-2147483648来代替最小负数&#xff0c;因为这不是一个数字&#xff0c;而是一个表达式。表达式的意…

JVM性能优化之--JIT即时编译

如果我们想将源Java文件运行&#xff0c;需要进行如下步骤&#xff1a; 使用类加载器将Java文件实时编译成class文件&#xff0c;也就是字节码指令使用JVM将编译后的字节码指令&#xff0c;解释为机器码&#xff0c;这是计算机可以直接执行的指令计算机直接执行 但是这其中由…

挑战杯 基于深度学习的植物识别算法 - cnn opencv python

文章目录 0 前言1 课题背景2 具体实现3 数据收集和处理3 MobileNetV2网络4 损失函数softmax 交叉熵4.1 softmax函数4.2 交叉熵损失函数 5 优化器SGD6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习的植物识别算法 ** …

预览和真机调试无法连接服务器(报网络错误),开发者工具可以正常用

预览和真机调试无法连接服务器&#xff08;报网络错误&#xff09;&#xff0c;开发者工具可以正常用 方法&#xff1a; localhost替换为下面的ip&#xff0c;手机和电脑都链接同一个wifi // let RootPath http://127.0.0.1:8081;//或者http://localhost:8081let RootPath ht…

CSS:让动画流畅生动的缓动函数

在CSS中&#xff0c;可以使用transition属性或者keyframes关键帧动画来创建动画效果。 使用缓动函数则可以让动画更加流畅和生动。 div {transition: <property> <duration> <timing-function> <delay>; }div {animation: <keyframes-name> &l…

CSS、less、Sass、Scss、Stylus的认识

Sass、Scss、Less和Stylus区别总结_stylus scss-CSDN博客