STL-list的使用及其模拟实现

      在C++标准库中,list 是一个双向链表容器,用于存储一系列元素。与 vector 和 deque 等容器不同,list 使用带头双向循环链表的数据结构来组织元素,因此list插入删除的效率非常高。

list的使用

list的构造函数

list迭代器

list的成员函数

list的模拟实现

template<class T>
struct list_node {
    list_node<T>* _next;
    list_node<T>* _prev;
    T _data;

    list_node(const T& x=T())
        :_next(nullptr)
        , _prev(nullptr)
        , _data(x)
    {}
};
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* n)
        :_node(n)
    {}

    Ref operator*() {
        return _node->_data;
    }
    Ptr operator->() {
        return &_node->_data;
    }
    self& operator++() {
        _node = _node->_next;
        return *this;
    }
    self& operator--() {
        _node = _node->_prev;
        return *this;
    }
    self operator++(int) {
        self tmp(*this);
        _node = _node->_next;
        return tmp;
    }
    self operator--(int) {
        self tmp(*this);
        _node = _node->_prev;
        return tmp;
    }
    bool operator==(const self& s) {
        return _node == s._node;
    }
    bool operator!=(const self& s) {
        return _node != s._node;
    }

};

/*template<class T>
struct _list_const_iterator {
    typedef list_node<T> node;
    typedef _list_const_iterator<T> self;
    node* _node;
    _list_const_iterator(node* n)
        :_node(n)
    {}

    const T& operator*() {
        return _node->_data;
    }
    self& operator++() {
        _node = _node->_next;
        return *this;
    }
    self& operator--() {
        _node = _node->_prev;
        return *this;
    }
    self operator++(int) {
        self tmp(*this);
        _node = _node->_next;
        return tmp;
    }
    self operator--(int) {
        self tmp(*this);
        _node = _node->_prev;
        return tmp;
    }
    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;
public:
    typedef _list_iterator<T,T&,T*> iterator;
    typedef _list_iterator<T,const T&,const T*> const_iterator;
    //typedef _list_const_iterator<T> const_iterator;
    typedef ReverseIterator<iterator, T&, T*> reverse_iterator;
    typedef ReverseIterator<const_iterator, const T&, const T*> const_reverse_iterator;
    /*reverse_iterator rbegin()
    {
        return reverse_iterator(_head->_prev);
    }

    reverse_iterator rend()
    {
        return reverse_iterator(_head);
    }*/
    reverse_iterator rbegin()
    {
        return reverse_iterator(end());
    }

    reverse_iterator rend()
    {
        return reverse_iterator(begin());
    }
    iterator begin() {
        //iterator it(_head->next);
        //return it;
        return iterator(_head->_next);
    }
    const_iterator begin() const{
        return const_iterator(_head->_next);
    }
    iterator end() {
        //iterator it(_head);
        //return it;
        return iterator(_head);
    }
    const_iterator end() const{
        return const_iterator(_head);
    }
    void empty_init() {
        _head = new node;
        _head->_next = _head;
        _head->_prev = _head;
    }
    list() {
        empty_init();
    }

    template <class Iterator>
    list(Iterator first, Iterator last)
    {
        empty_init();
        while (first != last) 
        {
            push_back(*first);
            ++first;
        }
    }
    // lt2(lt1)
    /*list(const list<T>& lt) {
        empty_init();
        for (auto e : lt) {
            push_back(e);
        }
    }*/
    void swap(list<T>& tmp) {
        std::swap(_head, tmp._head);
    }
    list(const list<T>& lt) {
        empty_init();

        list<T> tmp(lt.begin(), lt.end());
        swap(tmp);
    }
    //lt1=lt3
    list<T>& operator=(list<T> lt) {
        swap(lt);
        return *this;
    }
    ~list() {
        clear();
        delete _head;
        _head = nullptr;
    }
    void clear() {
        iterator it = begin();
        while (it != end()) {
            //it = erase(it);
            erase(it++);
        }
    }
    void push_back(const T& x) {
        /*node* tail = _head->_prev;
        node* new_node = new node(x);

        tail->_next = new_node;
        new_node->_prev = tail;
        new_node->_next = _head;
        _head->_prev = new_node;*/
        insert(end(), x);

    }
    void push_front(const T& x) {
        insert(begin(), x);
    }
    void pop_back() {
        erase(--end());
    }
    void pop_front() {
        erase(begin());
    }
    void insert(iterator pos,const T& x) {
        node* cur = pos._node;
        node* prev = cur->_prev;

        node* new_node = new node(x);
        prev->_next = new_node;
        new_node->_prev = prev;
        new_node->_next = cur;
        cur->_prev = new_node;
    }
    //会导致迭代器失效 pos
    iterator erase(iterator pos, const T& x=0) {
        assert(pos != end());
        node* prev = pos._node->_prev;
        node* next = pos._node->_next;
        prev->_next = next;
        next->_prev = prev;
        delete pos._node;

        return iterator(next);
    }
private:
    node* _head;
};

迭代器和成员函数的模拟实现

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* n)
        :_node(n)
    {}

    Ref operator*() {
        return _node->_data;
    }
    Ptr operator->() {
        return &_node->_data;
    }
    self& operator++() {
        _node = _node->_next;
        return *this;
    }
    self& operator--() {
        _node = _node->_prev;
        return *this;
    }
    self operator++(int) {
        self tmp(*this);
        _node = _node->_next;
        return tmp;
    }
    self operator--(int) {
        self tmp(*this);
        _node = _node->_prev;
        return tmp;
    }
    bool operator==(const self& s) {
        return _node == s._node;
    }
    bool operator!=(const self& s) {
        return _node != s._node;
    }

};

/*template<class T>
struct _list_const_iterator {
    typedef list_node<T> node;
    typedef _list_const_iterator<T> self;
    node* _node;
    _list_const_iterator(node* n)
        :_node(n)
    {}

    const T& operator*() {
        return _node->_data;
    }
    self& operator++() {
        _node = _node->_next;
        return *this;
    }
    self& operator--() {
        _node = _node->_prev;
        return *this;
    }
    self operator++(int) {
        self tmp(*this);
        _node = _node->_next;
        return tmp;
    }
    self operator--(int) {
        self tmp(*this);
        _node = _node->_prev;
        return tmp;
    }
    bool operator==(const self& s) {
        return _node == s._node;
    }
    bool operator!=(const self& s) {
        return _node != s._node;
    }

};*/

迭代器共有两种:

1.迭代器要么就是原生指针
2.迭代器要么就是自定义类型对原生指针的封装,模拟指针的行为

迭代器类的模板参数列表当中的Ref和Ptr分别代表的是引用类型和指针类型。

当我们使用普通迭代器时,编译器就会实例化出一个普通迭代器对象;当我们使用const迭代器时,编译器就会实例化出一个const迭代器对象。这样就不用专门写两种不同类型的迭代器,泛型编程减少了代码的复用,提高了效率。

list的构造函数

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

list的拷贝构造

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

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

    list<T> tmp(lt.begin(), lt.end());
    swap(tmp);
}

list的析构函数

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

赋值运算符重载

//lt1=lt3
list<T>& operator=(list<T> lt) {
    swap(lt);
    return *this;
}

list的插入和删除


void push_back(const T& x) {
    /*node* tail = _head->_prev;
    node* new_node = new node(x);

    tail->_next = new_node;
    new_node->_prev = tail;
    new_node->_next = _head;
    _head->_prev = new_node;*/
    insert(end(), x);

}
void push_front(const T& x) {
    insert(begin(), x);
}
void pop_back() {
    erase(--end());
}
void pop_front() {
    erase(begin());
}
void insert(iterator pos,const T& x) {
    node* cur = pos._node;
    node* prev = cur->_prev;

    node* new_node = new node(x);
    prev->_next = new_node;
    new_node->_prev = prev;
    new_node->_next = cur;
    cur->_prev = new_node;
}
//会导致迭代器失效 pos
iterator erase(iterator pos, const T& x=0) {
    assert(pos != end());
    node* prev = pos._node->_prev;
    node* next = pos._node->_next;
    prev->_next = next;
    next->_prev = prev;
    delete pos._node;

    return iterator(next);
}

清空list中所有元素

void clear() {
    iterator it = begin();
    while (it != end()) {
        //it = erase(it);
        erase(it++);
    }
}

list迭代器失效

      迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。

      解决方法:可以使用 erase 函数的返回值,它会返回一个指向下一个有效元素的迭代器。

list和vector区别

底层实现:

      list 通常是一个双向链表,每个节点都包含了数据和指向前一个和后一个节点的指针。这使得在任何位置进行插入和删除操作都是高效的,但随机访问和内存占用可能相对较差。
     vector 是一个动态数组,元素在内存中是连续存储的。这使得随机访问非常高效,但插入和删除操作可能需要移动大量的元素,效率较低。

插入和删除:

     在 list 中,插入和删除操作是高效的,无论是在容器的任何位置还是在开头和结尾。这使得 list 在需要频繁插入和删除操作时非常适用。
     在 vector 中,插入和删除操作可能需要移动元素,特别是在容器的中间或开头。因此,当涉及大量插入和删除操作时,vector 可能不如 list 效率高。

随机访问:

list 不支持随机访问,即不能通过索引直接访问元素,必须通过迭代器逐个遍历。
vector 支持随机访问,可以通过索引快速访问元素,具有良好的随机访问性能。

迭代器失效:

在 list 中,插入和删除操作不会导致迭代器失效,因为节点之间的关系不会改变。
在 vector 中,插入和删除操作可能导致内存重新分配,从而使原来的迭代器失效。

综上所述,如果你需要频繁进行(头部和中间)插入和删除操作,而对于随机访问性能没有特别高的要求,可以选择list;如果想要随机访问以及尾插和尾删vector是更好的选择。 

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

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

相关文章

深度神经网络(DNN)

通过5个条件判定一件事情是否会发生&#xff0c;5个条件对这件事情是否发生的影响力不同&#xff0c;计算每个条件对这件事情发生的影响力多大&#xff0c;写一个深度神经网络&#xff08;DNN&#xff09;模型程序,最后打印5个条件分别的影响力。 示例 在深度神经网络&#xf…

动态规划相关

动态规划相关 力扣509 斐波那契数列 完全递归解法 / 设置备忘录减少递归次数解法 都是 自顶向下力扣 509 斐波那契数列 动态规划 自底向上 力扣509 斐波那契数列 完全递归解法 / 设置备忘录减少递归次数解法 都是 自顶向下 public int fib(int n) {/** if(n<2){* return n;…

Matlab新手快速上手2(粒子群算法)

本文根据一个较为简单的粒子群算法框架详细分析粒子群算法的实现过程&#xff0c;对matlab新手友好&#xff0c;源码在文末给出。 粒子群算法简介 粒子群算法&#xff08;Particle Swarm Optimization&#xff0c;PSO&#xff09;是一种群体智能优化算法&#xff0c;灵感来源于…

【测试总结】测试时如何定位一个bug?是前端还是后端?

作为一道面试题&#xff0c;它算高频了么&#xff1f;我面试别人问多挺多次&#xff0c;我也被面试官问过... 相对来说多少能看出一点测试经验&#xff0c;实际测试中的排查问题能力... 1、前后端bug有各自的一些特点&#xff1a; 前端bug特性&#xff1a;界面相关&#xff0c…

计算机网络(第7版谢希仁)笔记

计算机网络 第一章 概述第二章 物理层第三章、数据链路层第四章 网络层第五章 运输层第六章、应用层第七章 网络安全 第一章 概述 1、三大类网络&#xff1a;电信网络、有线电视网络、计算机网络。 电信网络&#xff1a;提供电话、电报及传真服务。 有线电视网络&#xff1a;向…

目标检测YOLO数据集的三种格式及转换

目标检测YOLO数据集的三种格式 在目标检测领域&#xff0c;YOLO&#xff08;You Only Look Once&#xff09;算法是一个流行的选择。为了训练和测试YOLO模型&#xff0c;需要将数据集格式化为YOLO可以识别的格式。以下是三种常见的YOLO数据集格式及其特点和转换方法。 1. YOL…

计算机系统结构(二) (万字长文建议收藏)

计算机系统结构 (二) 本文首发于个人博客网站&#xff1a;http://www.blog.lekshome.top/由于CSDN并不是本人主要的内容输出平台&#xff0c;所以大多数博客直接由md文档导入且缺少审查和维护&#xff0c;如果存在图片或其他格式错误可以前往上述网站进行查看CSDN留言不一定能够…

大话设计模式-里氏代换原则

里氏代换原则&#xff08;Liskov Substitution Principle&#xff0c;LSP&#xff09; 概念 里氏代换原则是面向对象设计的基本原则之一&#xff0c;由美国计算机科学家芭芭拉利斯科夫&#xff08;Barbara Liskov&#xff09;提出。这个原则定义了子类型之间的关系&#xff0…

【设计模式】7、decorate 装饰模式

文章目录 七、decorate 装饰模式7.1 饮料&#xff1a;类型配料7.1.1 drink_with_ingredient_test.go7.1.2 drink_with_ingredient.go7.1.3 drink.go 7.2 notifier7.2.1 notifier_decorator_test7.2.2 notifier_decorator7.2.3 notifier 7.3 idraw7.3.1 idraw_test7.3.2 idraw7.…

【人工智能基础】经典逻辑与归结原理

本章节的大部分内容与离散数学的命题、谓词两章重合。 假言推理的合式公式形式 R,R→P⇒PR,R∨P⇒P 链式推理 R→P,P→Q⇒R→QR∨P,P∨Q⇒R∨Q 互补文字&#xff1a;P和P 亲本子句&#xff1a;含有互补文字的子句 R∨P,P∨Q为亲本子句 注意&#xff1a; 必须化成析取范式…

命理八字之电子木鱼的代码实现

#uniapp# #电子木鱼# 不讲废话&#xff0c;上截图 目录结构如下图 功能描述&#xff1a; 点击一下&#xff0c;敲一下&#xff0c;伴随敲击声&#xff0c;可自动点击。自动点击需看视频广告&#xff0c;或者升级VIP会员。 疑点解答&#xff1a; 即animation动画的时候&…

Window中Jenkins部署asp/net core web主要配置

代码如下 D: cd D:\tempjenkins\src\ --git工作目录 dotnet restore -s "https://nuget.cdn.azure.cn/v3/index.json" --nuget dotnet build dotnet publish -c release -o %publishPath% --发布路径

装饰器学习

【一】什么是装饰器 装饰器指的就是为被装饰的对象添加新的功能 器 代表工具 增加后的调用方式不变 在不改变源代码和调用方式的基础上增加额外的新功能 【二】装饰器的用途 对上线后的程序进行新功能的增加和修改 给一个功能增加新的需求或者改变原来的程序运行逻辑 【…

Day08React——第八天

useEffect 概念&#xff1a;useEffect 是一个 React Hook 函数&#xff0c;用于在React组件中创建不是由事件引起而是由渲染本身引起的操作&#xff0c;比如发送AJAx请求&#xff0c;更改daom等等 需求&#xff1a;在组件渲染完毕后&#xff0c;立刻从服务器获取频道列表数据…

iOS RACScheduler 使用详解

RACScheduler 是 ReactiveCocoa 框架中的一个关键组件&#xff0c;用于在 iOS 开发中管理任务的并发执行。以下是如何详细使用 RACScheduler 的指南&#xff0c;以 Markdown 格式展示。 主要调度器 主线程调度器 用于在主线程上执行任务&#xff0c;通常用于 UI 更新操作。 …

Java:二叉树(1)

从现在开始&#xff0c;我们进入二叉树的学习&#xff0c;二叉树是数据结构的重点部分&#xff0c;在了解这个结构之前&#xff0c;我们先来了解一下什么是树型结构吧&#xff01; 一、树型结构 1、树型结构简介 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>…

通用类的中文实体命名识别

中文命名实体识别&#xff08;Chinese NER&#xff09;调研&#xff1a;https://github.com/taishan1994/awesome-chinese-ner 论文&#xff1a;Unified Structure Generation for Universal Information Extraction &#xff08;一统实体识别、关系抽取、事件抽取、情感分析&a…

TCP协议学习记录

TCP协议学习记录 简述 对TCP有诸多疑惑的地方&#xff1a; 1、TCP和socket的关系 2、TCP客户端和服务端如何区分 3、TCP连接的两端&#xff0c;端口号需要一致吗 什么是socket 一种编程抽象 编写程序时&#xff0c;利用socket可以使用TCP&#xff1b;假设现在已经将TCP协议…

Matlab无基础快速上手1(遗传算法框架)

本文用经典遗传算法框架模板&#xff0c;对matlab新手友好&#xff0c;快速上手看懂matlab代码&#xff0c;快速应用实践&#xff0c;源代码在文末给出。 基本原理&#xff1a; 遗传算法&#xff08;Genetic Algorithm&#xff0c;GA&#xff09;是一种受生物学启发的优化算法…

在Gtiee搭建仓库传代码/多人开发/个人代码备份---git同步---TortoiseGit+TortoiseSVN

文章目录 前言1.安装必要软件2. Gitee建立新仓库git同步2.1 Gitee建立新仓库2.2 Gitee仓库基本配置2.3 Git方式进行同步 3. TortoiseGitTortoiseSVN常用开发方式3.1 秘钥相关3.2 TortoiseGit拉取代码TortoiseGit提交代码 4. 其他功能探索总结 前言 正常企业的大型项目都会使用…