讨论stl链表

news2024/11/18 1:35:59

讨论链表

  • list迭代器失效
  • list的模拟实现
    • 创建结点类
    • 链表迭代器
    • 完成实现代码
  • list与vector

链表是一个序列容器,在任意位置都可以用常数时间插入或者删除,并且可以在两个方向进行迭代。

链表定义

list迭代器失效

迭代器失效指迭代器所指向的结点无效,即该结点被删除了。

  • list的底层结构是双向带头循环链表,因此向list中插入数据是不会造成迭代器失效。
  • 但删除数据时,指向删除结点的迭代器会失效,其他迭代器不会受影响。

list的模拟实现

创建结点类

  • 链表是由一个一个结点组成,结点中存放储存的元素已经指向下一个以及前一个的指针。
template<class T>
struct list_node {
    T _val;
    list_node<T> *_prev;
    list_node<T> *_next;

    list_node(const T &val = T())
            : _val(val), _prev(nullptr), _next(nullptr) {}
};

链表迭代器

  • 链表的迭代器不同于顺序表。顺序表的迭代器可以直接返回头部和尾部指针的位置。++操作只需要移动相应字节数的指针即可完成。

顺序表迭代器

  • 链表迭代器++操作不能依靠简单的指针++完成,因为不是连续空间,因此需要封装一层结点结构,以_node = _node->next来达到++的效果。
template<class T, class Ref, class Ptr>
struct __list_iterator {
    typedef list_node<T> node;
    typedef __list_iterator<T, Ref, Ptr> self;

    node *_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->_prev;
        return *this;
    }

    self operator--(int) {
        self tmp(*this);
        _node = _node->_prev;
        return tmp;
    }

    Ref operator*() {
        return _node->_val;
    }

    Ptr operator->() {
        return &_node->_val;
    }

    bool operator!=(const self &s) {
        return _node != s._node;
    }

    bool operator==(const self &s) {
        return _node == s._node;
    }
};
  • 其中RefPtr模板为传入引用或者指针对象区分const和非const的模板。

完成实现代码

namespace max {
    template<class T>
    struct list_node {
        T _val;
        list_node<T> *_prev;
        list_node<T> *_next;

        list_node(const T &val = T())
                : _val(val), _prev(nullptr), _next(nullptr) {}
    };

    // 迭代器封装
    template<class T, class Ref, class Ptr>
    struct __list_iterator {
        typedef list_node<T> node;
        typedef __list_iterator<T, Ref, Ptr> self;

        node *_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->_prev;
            return *this;
        }

        self operator--(int) {
            self tmp(*this);
            _node = _node->_prev;
            return tmp;
        }

        Ref operator*() {
            return _node->_val;
        }

        Ptr operator->() {
            return &_node->_val;
        }

        bool operator!=(const self &s) {
            return _node != s._node;
        }

        bool operator==(const self &s) {
            return _node == s._node;
        }
    };

    template<class T>
    class list {
        typedef list_node<T> node;
        typedef __list_iterator<T, T &, T *> iterator;
        typedef __list_iterator<T, const T &, const T *> const_iterator;
    public:

        void empty_init() {
            _head = new node();
            _head->_next = _head;
            _head->_prev = _head;

            _size = 0;
        }

        list() {
            empty_init();
        }

        list(int n, const T &val = T()) {
            empty_init();
            for (int i = 0; i < n; ++i) {
                push_back(val);
            }
        }

        template<class Iterator>
        list(Iterator first, Iterator last) {
            empty_init();
            while (first != last) {
                push_back(*first);
                ++first;
                ++_size;
            }
        }

        list(const list<T> &lt) {
            empty_init();

            for (auto e: lt) {
                push_back(e);
            }
        }

        void swap(list<T> &tmp) {
            std::swap(_head, tmp._head);
            std::swap(_size, tmp._size);
        }

        list<T> &operator=(list<T> tmp) {
            swap(tmp);
            return *this;
        }

        ~list() {
            clear();
            delete _head;
            _head = nullptr;
        }


        void push_back(const T &val) {
            node *newNode = new node(val);
            node *end = _head->_prev;

            end->_next = newNode;
            newNode->_prev = end;
            newNode->_next = _head;
            _head->_prev = newNode;

            ++_size;
        }

        void push_front(const T &val) {
            node *newNode = new node(val);
            node *next = _head->_next;

            newNode->_next = next;
            next->_prev = newNode;
            _head->_next = newNode;
            newNode->_prev = _head;
        }

        void pop_back() {
            assert(_head->_next != _head);

            node *del = _head->_prev;
            _head->_prev = del->_prev;

            del->_prev->_next = _head;

            delete del;
            del = nullptr;
            --_size;
        }

        void pop_front() {
            assert(_head->_next != _head);

            node *del = _head->_next;

            _head->_next = del->_next;
            del->_next->_prev = _head;

            delete del;
            del = nullptr;
            --_size;
        }

        iterator begin() {
            return iterator(_head->_next);
        }

        iterator end() {
            return iterator(_head);
        }

        const_iterator begin() const {
            return const_iterator(_head->_next);
        }

        const_iterator end() const {
            return const_iterator(_head);
        }

        iterator insert(iterator pos, const T &val) {
            node *newNode = new node(val);
            node *prev = pos._node->_prev;

            prev->next = newNode;
            newNode->_prev = prev;
            newNode->_next = pos._node;
            pos._node->_prev = newNode;

            ++_size;

            return iterator(newNode);
        }

        iterator erase(iterator pos) {
            assert(_head != _head->_next);
            node *cur = pos._node;
            node *next = cur->_next;
            node *prev = cur->_prev;

            prev->_next = next;
            next->_prev = prev;

            delete cur;
            cur = nullptr;
            --_size;

            return iterator(next);
        }

        void clear() {
            iterator it = begin();
            while (it != end()) {
                it = erase(it);
                --_size;
            }
        }

        size_t size() const {
            return _size;
        }

    private:
        node *_head;
        size_t _size;
    };
}

list与vector

  • vectorlist底层结构不同,因此在使用场景上存在一定差异。
vectorlist
底层结构动态顺序表,一段连续的空间。带头双向循环链表
随机访问支持随机访问,效率为O(1)。不支持随机访问,访问某个元素的效率为O(N)。
插入和删除尾插和尾删时效率是O(1)。除此之外插入删除的效率都很低,因为需要移动数据,若插入大量数据还涉及到扩容,异地扩容需要拷贝元素和释放旧空间,效率很低。任意位置插入和删除数据效率为O(1)。不需要移动数据。
空间利用率底层为连续空间,不容易产生内存碎片,利用率高,缓存利用率高。底层为动态开辟的小结点,容易造成内存碎片,空间利用率低,缓存利用率低。
迭代器原生态指针。对原生态指针进行封装。
迭代器失效插入元素时可能会造成迭代器失效,因为可能会异地扩容。删除元素时当前迭代器会失效。插入元素时不会造成迭代器失效,删除元素时会造成当前迭代器失效,其他迭代器不受影响。

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

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

相关文章

windows@局域网或蓝牙文件传输@共享文件夹@就近共享

文章目录 windows系统下的简单共享文件方案&#x1f47a;就近共享设置共享文件夹(推荐)方法1:使用shrpubw程序引导创建方法2:使用图形界面创建右键设置共享文件夹 查看所有已经共享的文件夹&#x1f47a;停止某个文件的共享 共享文件夹的访问控制补充匿名访问问题&#x1f60a;…

JFrame和JScrollPanel布局初步使用

还不是很了解&#xff0c;做了几个程序&#xff1b; import java.awt.Container; import java.awt.Color; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.border.EmptyBorder;public class pa1 {public static void main(String[] agrs){JF…

一个多文件工程的例子

代码; main.c #include <stdio.h> #include "add.h" #include "sub.h"int main(void) {int a10,b12;float x1.23456,y9.87654321;printf("int ab IS :%d\n",add_int(a,b));printf("int a-b IS :%d\n",sub_int(a,b));printf(&q…

STM32 中断和事件的区别

原文 简述 上图蓝线为中断的处理过程&#xff0c;红线是事件处理过程。 区别 中断&#xff08;Interrupts&#xff09;&#xff1a; 简述&#xff1a;当发生中断请求后&#xff0c;CPU暂停当前任务&#xff0c;进入对应的中断服务函数&#xff0c;完成后再回到原来暂停的地方…

IP地址专用SSL证书申请指南

IP地址SSL证书是一种专门设计用于IP地址的SSL/TLS证书&#xff0c;部署IP地址SSL证书可以实现IP地址HTTPS加密。 一&#xff1a;前提条件 1&#xff1a;申请IP地址SSL证书,必须拥有这个IP地址的管理权限 2 &#xff1a;80、443、22、端口中任一个可以短暂开放 二&#xff1…

rtthread stm32h743的使用(十)i2c设备使用

我们要在rtthread studio 开发环境中建立stm32h743xih6芯片的工程。我们使用一块stm32h743及fpga的核心板完成相关实验&#xff0c;核心板如图&#xff1a; 1.建立新工程&#xff0c;选择相应的芯片型号及debug引脚及调试器 2.打开cubemux&#xff0c;设置外部时钟及串口外设…

Java文件操作小项目-带GUI界面统计文件夹内文件类型及大小

引言 在Java编程中&#xff0c;文件操作是一项基本且常见的任务。我们经常需要处理文件和文件夹&#xff0c;例如读取、写入、删除文件&#xff0c;或者遍历文件夹中的文件等。本文将介绍如何使用Java的File类和相关API来统计一个文件夹中不同类型文件的数量和大小。 准备工作…

气膜体育馆的使用年限有多少—轻空间

气膜体育馆作为一种新兴的建筑形式&#xff0c;因其独特的结构和功能而备受青睐。它不仅在建设速度、成本控制和环保方面具有显著优势&#xff0c;还在使用年限上展现出良好的性能。轻空间将探讨气膜体育馆的使用年限及其影响因素。 气膜体育馆的基本结构 气膜体育馆主要由膜材…

我教你做不花钱的SEO

我教你做不花钱的SEO **SEO&#xff08;搜索引擎优化&#xff09;投入不一定有产出**不花钱的SEO标题标签元描述标题标签URL结构图片优化内容优化内部链接外部链接结构化数据页面速度优化移动友好性社交媒体整合 结论 SEO&#xff08;搜索引擎优化&#xff09;投入不一定有产出…

Sql审核平台Archery的搭建和简单配置

Sql审核平台Archery的搭建和简单配置 Archery是一个开源的Web应用&#xff0c;基于Python开发&#xff0c;利用Flask作为后端框架&#xff0c;前端采用Vue.js&#xff0c;构建了一个现代化的数据操作界面。提供了SQL审核、数据查询、报表生成等功能&#xff0c;同时支持多种数据…

使用 frida hook Android app

Frida&#xff1a; 一种基于动态插装&#xff08;dynamic instrumentation&#xff09;技术的工具包&#xff0c;它主要是为测试人员、开发人员和逆向工程爱好者创建&#xff0c;在目标程序运行时&#xff0c;允许用户将 JavaScript代码注入其中&#xff0c;实现动态修改和调试…

if action和Switch之间该怎么选择?

1. Switch 2. If及If Action Subsystem 3.结论 元素很多&#xff0c;用switch 元素少&#xff0c;用if或switch 如果...很多&#xff0c;用if

Go语言之集合类型

个人网站&#xff1a; http://hardyfish.top/ 免费书籍分享&#xff1a; 资料链接&#xff1a;https://url81.ctfile.com/d/57345181-61545511-81795b?p3899 访问密码&#xff1a;3899 免费专栏分享&#xff1a; 资料链接&#xff1a;https://url81.ctfile.com/d/57345181-6…

姚期智、张亚勤、薛澜、Stuart Russell、Max Tegmark,DeepMind研究员等共话全球AI治理丨大会回顾...

为什么AI安全已迫在眉睫&#xff1f;如何构建全球范围内的合作&#xff1f;民众该如何参与到其中&#xff1f;未来的AI系统将是什么样的&#xff1f; 2024年6月15日&#xff0c;智源大会第二天&#xff0c;多位AI安全领域专家进行圆桌讨论&#xff0c;连接中国北京和美国加利福…

从概念到现实:数字孪生技术在智慧充电站的实践

在电动汽车蓬勃发展的今天&#xff0c;充电基础设施的智能化升级成为了推动新能源汽车产业跃进的关键一环。数字孪生技术&#xff0c;作为智能制造和工业4.0的核心&#xff0c;正在逐渐渗透到智慧充电站的每一个角落——从提高能源效率到增强用户体验&#xff0c;为智慧充电站的…

DockerDesktop中mysql容器无法使用Exec窗口解决

解决前 需要登陆&#xff1a; 登陆后需要升级才能启动调试模式 需要订阅才能使用 解决后&#xff1a; 正常使用 解决方法&#xff1a; 不要在DockerDesktop中启动mysql容器&#xff0c;使用命令行启动 启动命令 docker run --name mysql_docker -e MYSQL_ROOT_PASSWORD12345…

MySQL递归查询(with recursive)

背景 日常开发中经常会有那种 阶梯式 数据&#xff0c;比如做地图、菜单&#xff0c;裂变给上级、上上级分红等等这样的需求的时候 你需要找个一个对象的 上级&#xff0c;上上级&#xff0c;上上上级 建了一张很容易理解阶级的表&#xff0c;一目了然 很多时候我们的需求就是…

仓库管理系统09--修改用户密码

1、添加窗体 2、窗体布局控件 UI设计这块还是传统的表格布局&#xff0c;采用5行2列 3、创建viewmodel 4、前台UI绑定viewmodel 这里要注意属性绑定和命令绑定及命令绑定时传递的参数 <Window x:Class"West.StoreMgr.Windows.EditPasswordWindow"xmlns"http…

缓存问题二、缓存雪崩

缓存雪崩 缓存雪崩&#xff1a;是指在同一时段大量的缓存key同时失效或者Redis服务宕机&#xff0c;导致大量请求到达数据库&#xff0c;带来巨大压力。 缓存雪崩的解决方案&#xff1a; 给不同的Key的TTL添加随机值利用Redis集群提高服务的可用性给缓存业务添加降级限流策略…

转行AI产品经理,这些“门槛”你得先迈过!

前言 随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;AI产品经理这一岗位逐渐崭露头角&#xff0c;成为许多追求创新和挑战的职业人士的新选择。但转行并非易事&#xff0c;特别是进入到一个全新的领域。在决定踏上AI产品经理这条道路之前&#xff0c;以下…