list的讲解及模拟实现(c++)

与介绍vector时相同为了方便大家理解, 我们采用边模拟实现边讲解使用的方式, 展开对list容器的讲解.

目录

一.list的介绍

二.list中的经常使用的一些方法

1.构造函数

 2.迭代器的使用

3.容量的相关函数 

 4.容器中元素的访问

5.容器中元素的修改 

 三.list实现中的相关声明(后面对其进行具体实现)

四.list中的节点类

五.list中迭代器的封装

1.一个友元外加两个重命名

2.构造函数 

3.拷贝构造函数

4.*运算符重载

5.->运算符重载

6.前置++和后置++

7.前置--和后置--

8.==和!=的重载

9.注意

六.list类的成员函数

1.六个重命名

2.创建头结点

3.list的构造

4.迭代器的使用

 5.list容量相关操作

6.list中元素的访问

 7.list中元素的修改

8.list的迭代器失效问题

七.list和vector的对比


一.list的介绍

使用时注意包含头文件: #include<list>

list相关特性介绍:

1.list的底层是一个带头结点的双向循环链表.

2. list是可以在常数范围内在任意位置进行插入和删除的序列式容器, 并且该容器可以前后双向迭代.

3. list的底层是双向链表结构, 双向链表中每个元素存储在互不相关的独立节点中, 在节点中通过指针指向其前一个元素和后一个元素.

4. list与forward_list非常相似:最主要的不同在于forward_list是单链表, 只能朝前迭代, 而list让其更简单高效.

5. 与其他的序列式容器相比(array,vector,deque), list通常在任意位置进行插入、移除元素的执行效率更好.

6. 与其他序列式容器相比, list和forward_list最大的缺陷是不支持任意位置的随机访问, 比如: 要访问list的第6个元素, 必须从已知的位置(比如头部或者尾部)迭代到该位置, 在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间, 以保存每个节点的相关联信息.(对于存储类型较小元素的大list来说这可能是一个重要的因素)

7.在类和对象的位置, 知道静态成员变量, 也可以通过:  类名: :静态成员变量 进行访问, 而类中包含的其他类型, 也可以通过:  类名: :类内部定义类型  使用, 但要注意访问的是类型时要以"typename"为前缀来表示类型, 要不然编译器无法区分是类型还是静态成员变量.

前面看不懂没关系, 跟随讲解后面便会一一了解.

二.list中的经常使用的一些方法

1.构造函数

 2.迭代器的使用

 我们这里要注意, 前面介绍过的string和vector中因为底层使用的是连续空间的原因, 他们的迭代器都是原生态的指针, 但到list就不同了, list的底层是以双向链表的方式组织起来的不同空间位置的节点, 此时我们必须在list的类外重新封装一个迭代器的类, 该迭代器类中必须对++,--,==,!=等运算符进行重载, 与其他容器中的迭代器的某些功能保持一致, 之所以这样要求是因为有一些常用的算法例如: sort, find等等, 我们只用实现一份代码然后用不同容器的迭代器进行传参, 就能使用该算法, 不仅节省了资源, 还能防止了出现大量的重复代码.

3.容量的相关函数 

 4.容器中元素的访问

5.容器中元素的修改 

 三.list实现中的相关声明(后面对其进行具体实现)

我们要实现的list, c++系统已经给出了, 所以我们这里的实现将其放到自已定义的一个命名空间中.

namespace lz {

    // List的节点类
    template<class T> class list;

    template<class T>
    struct ListNode {
        ListNode(const T& val = T());

        ListNode<T>* _pPre;
        ListNode<T>* _pNext;
        T _val;
    };


    //List的正向迭代器类

    template<class T, class Ref, class Ptr>
    class ListIterator {
        friend class list<T>;
        typedef ListNode<T>* PNode;
        typedef ListIterator<T, Ref, Ptr> Self;

    public:

        ListIterator(PNode pNode = nullptr);

        ListIterator(const Self& l);

        T& operator*();

        T* operator->();

        Self& operator++();

        Self& operator++(int);

        Self& operator--();

        Self& operator--(int);

        bool operator!=(const Self& l);

        bool operator==(const Self& l);

    private:

        PNode _pNode;

    };

    //list类

    template<class T>
    class list {

        typedef ListNode<T> Node;
        typedef Node* PNode;

    public:

        typedef ListIterator<T, T&, T*> iterator;
        typedef ListIterator<T, const T&, const T&> const_iterator;

    public:

        ///
        // List的构造

        list();

        list(int n, const T& value = T());

        template <class Iterator>
        list(Iterator first, Iterator last);

        list(const list<T>& l);

        list<T>& operator=(list<T> l);

        ~list();

        ///
        // List Iterator

        iterator begin();

        iterator end();

        const_iterator begin() const;

        const_iterator end() const;

        ///
        // List Capacity

        size_t size()const;

        bool empty()const;

        
        // List Access

        T& front();

        const T& front()const;

        T& back();

        const T& back()const;

        
        // List Modify

        void push_back(const T& val);

        void pop_back();

        void push_front(const T& val);

        void pop_front();

        // 在pos位置前插入值为val的节点

        iterator insert(iterator pos, const T& val);

        // 删除pos位置的节点,返回该节点的下一个位置

        iterator erase(iterator pos);

        void clear();

        void swap(list<T>& l);

    private:

        void CreateHead();

        PNode _pHead;

    };

};

四.list中的节点类

说明:

因为在list底层是一个带头结点的双向循环链表, 所以我们要对其节点进行封装, 具体形式和C语言中的相同, 两个类类型的指针, 一个指向前面的节点类, 一个指向后面的节点类, 再加一个数据域来存储数据, 我们这里采用模板类的方式进行实现, 与c++系统的list保持同步, 并且这种模板类的方式, 可以针对于各种类型的数据, 只用使用者使用时将类型传入即可, 避免了同类代码的冗余.

代码说明:

根据上面说明的, 我们类中的数据成员有三个, 类类型的两个指针, 外加一个数据域, 再自定义一个传入数据用来初始化的构造函数即可, 至于节点的组织则再list类中进行实现.

    // List的节点类
    template<class T> class list;

    template<class T>
    struct ListNode {
        ListNode(const T& val = T()) {//构造函数
            _pPre = nullptr;
            _pNext = nullptr;
            _val = val;
        }
        
        //数据成员

        ListNode<T>* _pPre;
        ListNode<T>* _pNext;
        T _val;
    };

五.list中迭代器的封装

1.一个友元外加两个重命名

        friend class list<T>;
        typedef ListNode<T>* PNode;
        typedef ListIterator<T, Ref, Ptr> Self;

①.我们需要在迭代器的类中将list类声明为其的友元类, 因为我们实现迭代器类就是为了在list中使用, 所以在list中需要访问到迭代器中的所有成员.

②.对节点类的类类型指针进行重命名, 方便我们后续的使用.

③.对自身的类类型进行重命名, 方便后续的使用.

  • 29
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 20
    评论
C++中栈的链表实现可以使用链表模拟栈的结构。在这种实现中,我们可以使用std::list来作为底层数据结构。\[1\]栈的结构可以定义为一个类,其中包含一个std::list<int>类型的成员变量stk来存储栈中的元素。此外,我们可以使用两个迭代器bottom和top来表示栈的栈底和栈顶。 入栈操作可以通过在链表的头部插入元素来实现。具体而言,我们可以使用std::list的push_front()函数将元素插入到链表的头部。在入栈操作中,我们需要将新元素插入到栈顶位置,并更新栈顶指针top。 下面是一个简单的C++代码示例,展示了如何使用链表实现栈的入栈操作: ```cpp void Stack::push(int value) { stk.push_front(value); top = stk.begin(); } ``` 在这个示例中,我们使用std::list的push_front()函数将新元素插入到链表的头部,然后将top指针更新为链表的第一个元素。 需要注意的是,由于链表没有固定的大小限制,因此在链表实现的栈中不会出现栈满的情况。\[1\]因此,我们可以根据需要随时向链表中添加新的元素。 希望这个回答能够帮助到你! #### 引用[.reference_title] - *1* [C++实现栈(链表模拟)【每一步详细深入讲解,代码清晰、简单、易懂】](https://blog.csdn.net/weixin_55755506/article/details/128356314)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陆陆陆ovo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值