C++笔记---list

1. list的介绍

list其实就是就是我们所熟知的链表(双向循环带头结点),但其是作为STL中的一个类模板而存在。

也就是说,list是可以用来存储任意类型数据的顺序表,既可以是内置类型,也可以是自定义类型,或是STL中的其他容器。

除了底层的实现不同以外,用法与vector基本相同,但不支持随机访问,以及与随机访问有关的接口。

具体以list - C++ Reference为准,本文为该文档的一个简单总结与标注。

2. list的重要接口

以下表格中的函数都不是官方库中的函数原型,而是为了方便学习和理解而进行了简化的。

2.1 默认成员函数

2.1.1 构造函数

四种构造方式
(1) list();默认构造
(2) list(size_t n, const T& val = T());用val初始化list前n个数据

(3) template<class InputIterator>

list(InputIterator first, InputIterator last);

用迭代器区间进行初始化
(4) list(const list& x);拷贝构造
 2.1.2 赋值重载

 2.2 迭代器相关

begin返回开始位置的迭代器
end返回最后一个数据的下一个位置的迭代器
rbegin用于逆向迭代
rend用于逆向迭代
cbegin用于const修饰的容器的迭代
cend用于const修饰的容器的迭代
crbegin用于const修饰的容器的逆向迭代
crend用于const修饰的容器的逆向迭代

2.3 大小容量相关

bool empty() const;判断list是否为空
size_t size() const;返回list中的数据个数
size_t max_size() const;返回由于系统或数据库限制,list能够存储数据的最大容量(并不一定能达到)

2.4 访问相关

(1) T& front();
(2) constT& front() const;
返回第一个元素的引用
(1) T& back();
(2) constT& back() const;
返回最后一个元素的引用

2.5 元素修改相关

(1) template<class InputIterator>

     void assign(InputIterator first, InputIterator last);

(2) void assign(size_t n, const T& val);

给list赋新的值,效果类似于重新构造这个list
void push_front(const T& val); 在list头部插入一个元素
void push_back(const T& val); 在list尾部插入一个元素
void pop_front();删除list头部的一个元素
void pop_back();删除list尾部的一个元素
(1) iterator insert(iterator pos, const T& val);
(2) void insert(iterator pos, size_t n, const T& val);
(3) template <class InputIterator>
     void insert(iterator position, InputIterator first,InputIterator last);
在指定位置插入元素,常数时间O(1),效率很高,不会导致迭代器失效
(1) iterator erase(iterator pos);
(2) iterator erase(iterator first, iterator last);
删除指定位置的数据,常数时间O(1),效率很高,会导致当前位置迭代器失效
void swap(list<T>& x);交换两个list的数据
void resize(size_t n, T val = T());改变list的元素个数,使其容纳n个元素,n<size则删除多余的,n>size则加入n个值与val相同的元素
void clear();清空list

2.6 list结点移动

(1) void splice(iterator position, list& x);
(2) void splice(iterator position, list& x, iterator i);
(3) void splice(iterator position, list& x, iterator first, iterator last);
(1) 将x的结点全部移动到position位置
(2) 将x中i指向的结点移动到position位置
(3) 将x中first到last的结点移动到position位置
void remove(const T& val);移除list中与val值相同的结点
template <class Predicate>
void remove_if(Predicate pred);
按照pred(*it)函数的返回值(true移除/false不移除)来移除结点
(1) void unique();
(2) template <class BinaryPredicate>
     void unique(BinaryPredicate binary_pred);

(1) 移除值连续相等(operator==)的几个结点,只留下这组结点中的第一个(在处理经过排序的list时,能确保各种值得结点只留下一个)

(2) 按照binary_pred(*it, *(it-1))给出的逻辑判断结点的值是否相等,进而删除结点

(1) void merge(list& x);
(2) template <class Compare>
     void merge(list& x, Compare comp);

(1) 将x合并到调用函数的list中(x的结点全部移动到list中,并确保合并后的list中的结点也是有序(operator<)的,但要求两个list事先都是有序的。如果list==*this,该函数无行为)

(2) 按照comp函数给出的比较方式来进行有序的合并

(1) void sort();
(2) template <class Compare>
     void sort(Compare comp);

(1) 按照operator<进行排序

(2) 按照comp给出的比较方式排序

void reverse();逆置list

3. 迭代器失效

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

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

4. list不完全模拟实现示例

#pragma once
#include<iostream>
using namespace std;

namespace lbz
{
    // List的节点类
    template<class T>
    struct ListNode
    {
        ListNode(const T& val = T())
            :_val(val)
            ,_pPre(nullptr)
            ,_pNext(nullptr)
        {}
        ListNode<T>* _pPre;
        ListNode<T>* _pNext;
        T _val;
    };


    //List的迭代器类
    template<class T, class Ref, class Ptr>
    struct ListIterator
    {
        typedef ListNode<T>* PNode;
        typedef ListIterator<T, Ref, Ptr> Self;
        typedef Ref _Ref;
        typedef Ptr _Ptr;
    public:
        ListIterator(PNode pNode = nullptr)
            :_pNode(pNode)
        {}
        ListIterator(const Self& l)
            :_pNode(l._pNode)
        {}

        T& operator*()
        {
            return _pNode->_val;
        }
        // 按理来说在使用时需要两个->,但编译器为了可读性做了优化
        T* operator->()
        {
            return &(_pNode->_val);
        }
        Self& operator++()
        {
            _pNode = _pNode->_pNext;
            return (*this);
        }
        Self operator++(int)
        {
            Self tmp = (*this);
            _pNode = _pNode->_pNext;
            return tmp;
        }
        Self& operator--()
        {
            _pNode = _pNode->_pPre;
            return (*this);
        }
        Self& operator--(int)
        {
            Self tmp = (*this);
            _pNode = _pNode->_pPre;
            return tmp;
        }
        bool operator!=(const Self& l) const
        {
            return (this->_pNode != l._pNode);
        }
        bool operator==(const Self& l) const
        {
            return (this->_pNode == l._pNode);
        }

        PNode _pNode;
    };

    template<class iterator>
    struct reverseListIterator
    {
        typedef typename iterator::_Ref Ref;
        typedef typename iterator::_Ptr Ptr;
        typedef reverseListIterator<iterator> Self;

        reverseListIterator(iterator it)
            :_it(it)
        {}

        Ref operator*()
        {
            iterator tmp(_it);
            --tmp;
            return *tmp;
        }
        Ptr operator->()
        {
            return &(operator*());
        }
        Self& operator++()
        {
            --_it;
            return *this;
        }
        Self& operator++(int)
        {
            Self tmp(*this);
            --_it;
            return tmp;
        }
        Self& operator--()
        {
            ++_it;
            return *this;
        }
        Self& operator--(int)
        {
            Self tmp(*this);
            ++_it;
            return tmp;
        }

        bool operator==(const Self& rit) const
        {
            return _it == rit._it;
        }
        bool operator!=(const Self& rit) const
        {
            return _it != rit._it;
        }

        iterator _it;
    };


    //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;
        typedef reverseListIterator<iterator> reverse_iterator;
        typedef reverseListIterator<const_iterator> const_reverse_iterator;
    public:
        ///
        // List的构造
        list()
        {
            CreateHead();
        }

        list(int n, const T& value = T())
        {
            CreateHead();
            while (n--)
            {
                push_back(value);
            }
        }

        template <class Iterator>
        list(Iterator first, Iterator last)
        {
            CreateHead();
            while (first != last)
            {
                push_back(*first);
                ++first;
            }
        }
        list(const list<T>& l)
        {
            CreateHead();
            list tmp(l.begin(), l.end());
            swap(tmp);
        }
        // 支持用大括号参数列表构造/隐式类型转换
        list(initializer_list<T> il)
        {
            CreateHead();
            for (auto& e : il)
            {
                push_back(e);
            }
        }
        list<T>& operator=(const list<T> l)
        {
            list tmp(l.begin(), l.end());
            swap(tmp);
            return *this;
        }
        ~list()
        {
            clear();
            delete _pHead;
            _pHead = nullptr;
        }


        ///
        // List Iterator
        iterator begin()
        {
            return iterator(_pHead->_pNext);
        }
        iterator end()
        {
            return iterator(_pHead);
        }
        const_iterator begin() const
        {
            return const_iterator(_pHead->_pNext);
        }
        const_iterator end() const
        {
            return const_iterator(_pHead);
        }
        const_iterator cbegin() const
        {
            return const_iterator(_pHead->_pNext);
        }
        const_iterator cend() const
        {
            return const_iterator(_pHead);
        }

        reverse_iterator rbegin()
        {
            return reverse_iterator(end());
        }
        reverse_iterator rend()
        {
            return reverse_iterator(begin());
        }
        const_reverse_iterator crbegin() const
        {
            return const_reverse_iterator(cend());
        }
        const_reverse_iterator crend() const
        {
            return const_reverse_iterator(cbegin());
        }


        ///
        // List Capacity
        size_t size()const
        {
            return _size;
        }
        bool empty()const
        {
            return !_size;
        }


        
        // List Access
        T& front()
        {
            return _pHead->_pNext->_val;
        }
        const T& front()const
        {
            return _pHead->_pNext->_val;
        }
        T& back()
        {
            return _pHead->_pPre->_val;
        }
        const T& back()const
        {
            return _pHead->_pPre->_val;
        }


        
        // List Modify
        void push_back(const T& val) { insert(end(), val); }
        void pop_back() { erase(--end()); }
        void push_front(const T& val) { insert(begin(), val); }
        void pop_front() { erase(begin()); }
        // 在pos位置前插入值为val的节点
        iterator insert(iterator pos, const T& val)
        {
            PNode newnode = new Node(val);
            newnode->_pNext = pos._pNode;
            newnode->_pPre = pos._pNode->_pPre;
            pos._pNode->_pPre->_pNext = newnode;
            pos._pNode->_pPre = newnode;
            _size++;
            return pos;
        }
        // 删除pos位置的节点,返回该节点的下一个位置
        iterator erase(iterator pos)
        {
            pos._pNode->_pNext->_pPre = pos._pNode->_pPre;
            pos._pNode->_pPre->_pNext = pos._pNode->_pNext;
            iterator ret = pos._pNode->_pNext;
            delete pos._pNode;
            --_size;
            return ret;
        }
        void clear()
        {
            PNode cur = _pHead->_pNext->_pNext;
            while (cur != _pHead->_pNext)
            {
                delete cur->_pPre;
                cur = cur->_pNext;
            }
            _pHead->_pNext = _pHead;
            _pHead->_pPre = _pHead;
            _size = 0;
        }
        void swap(list<T>& l)
        {
            std::swap(_pHead, l._pHead);
            std::swap(_size, l._size);
        }
    private:
        void CreateHead()
        {
            _pHead = new Node;
            _pHead->_pNext = _pHead;
            _pHead->_pPre = _pHead;
        }
        PNode _pHead;
        size_t _size = 0;
    };
};

5. list的迭代器

由于底层结构的复杂性,list的迭代器不再像string和vector那样可以直接由指针代劳。

我们依然将结点指针作为list迭代器的底层,但是各操作符原本的逻辑已经无法满足我们的需要,均需要进行重载。

于是,我们用ListIterator类对结点的指针进行了包装,并对所需的操作符进行了相应的重载。

//List的迭代器类
template<class T, class Ref, class Ptr>
struct ListIterator
{
    typedef ListNode<T>* PNode;
    typedef ListIterator<T, Ref, Ptr> Self;
    typedef Ref _Ref;
    typedef Ptr _Ptr;
public:
    ListIterator(PNode pNode = nullptr)
        :_pNode(pNode)
    {}
    ListIterator(const Self& l)
        :_pNode(l._pNode)
    {}

    T& operator*()
    {
        return _pNode->_val;
    }
    // 按理来说在使用时需要两个->,但编译器为了可读性做了优化
    T* operator->()
    {
        return &(_pNode->_val);
    }
    Self& operator++()
    {
        _pNode = _pNode->_pNext;
        return (*this);
    }
    Self operator++(int)
    {
        Self tmp = (*this);
        _pNode = _pNode->_pNext;
        return tmp;
    }
    Self& operator--()
    {
        _pNode = _pNode->_pPre;
        return (*this);
    }
    Self& operator--(int)
    {
        Self tmp = (*this);
        _pNode = _pNode->_pPre;
        return tmp;
    }
    bool operator!=(const Self& l) const
    {
        return (this->_pNode != l._pNode);
    }
    bool operator==(const Self& l) const
    {
        return (this->_pNode == l._pNode);
    }

    PNode _pNode;
};

其中Ref代表T的引用,Ptr代表T的指针,根据这两个参数是否被const修饰,我们可以实例化出普通的迭代器和const迭代器:

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

6. list反向迭代器

与正向迭代器相比,反向迭代器只是在进行++或--的行为与其不同。

我们可以采用适配器模式(stack和queue也是采用该种模式对其他容器进行包装)来实现反向迭代器:

用迭代器作为反向迭代器的底层,通过对正向迭代器的接口进行包装,使其行为满足我们的需求。

template<class iterator>
struct reverseListIterator
{
    typedef typename iterator::_Ref Ref;
    typedef typename iterator::_Ptr Ptr;
    typedef reverseListIterator<iterator> Self;

    reverseListIterator(iterator it)
        :_it(it)
    {}

    Ref operator*()
    {
        iterator tmp(_it);
        --tmp;
        return *tmp;
    }
    Ptr operator->()
    {
        return &(operator*());
    }
    Self& operator++()
    {
        --_it;
        return *this;
    }
    Self& operator++(int)
    {
        Self tmp(*this);
        --_it;
        return tmp;
    }
    Self& operator--()
    {
        ++_it;
        return *this;
    }
    Self& operator--(int)
    {
        Self tmp(*this);
        ++_it;
        return tmp;
    }

    bool operator==(const Self& rit) const
    {
        return _it == rit._it;
    }
    bool operator!=(const Self& rit) const
    {
        return _it != rit._it;
    }

    iterator _it;
};

 同理,我们可以定义出普通反向迭代器和const反向迭代器:

typedef reverseListIterator<iterator> reverse_iterator;
typedef reverseListIterator<const_iterator> const_reverse_iterator;

7. list和vector的区别

容器listvector
底层结构带头结点的双向循环链表动态顺序表,一段连续空间
随机访问不支持随机访问,访问某个元素效率O(N)支持随机访问,访问某个元素效率O(N)
插入和删除任意位置插入和删除效率高,不需要搬移元素,时间复杂度为O(1)

任意位置插入和删除效率低,需要搬移元素,时间复杂度为O(N),插入时有可能需要增容

增容:开辟新空间,拷贝元素,释放旧空间,导致效率更低

空间利用率底层节点动态开辟,小节点容易造成内存碎片,空间利用率低,缓存利用率低底层为连续空间,不容易造成内存碎片,空间利用
率高,缓存利用率高
迭代器对原生态指针(节点指针)进行封装原生态指针
迭代器失效插入元素不会导致迭代器失效,删除元素时,只会导致当前迭代器失效,其他迭代器不受影响在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器需要重新赋值否则会失效
使用场景大量插入和删除操作,不关心随机访问需要高效存储,支持随机访问,不关心插入删除效率

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

单词排序C++实现

代码如下&#xff1a; #include<iostream> #include<string> #include<fstream> #include<map> #include<iomanip> #include<algorithm> #include<vector>int read_file(std::map<std::string,int> &map_words) {std::st…

大牛直播SDK最经典的一句

搜索引擎搜大牛直播SDK&#xff0c;居然提示我搜“大牛直播SDK最经典的一句”&#xff0c;闲来无事&#xff0c;点开看看&#xff0c;AI智能问答&#xff0c;给出了答案&#xff1a; ‌大牛直播SDK最经典的一句是&#xff1a;"我们只做最擅长的部分,我们不做的,提供对接接…

[进阶]面向对象之多态(二)

文章目录 多态调用成员的特点多态的优势和弊端 多态调用成员的特点 变量调用:编译看左边,运行也看左边方法调用:编译看左边,运行看右边 多态的优势和弊端 优势&#xff1a; 在多态形式下&#xff0c;右边对象可以实现解耦合&#xff0c;便于扩展和维护定义方法的时候&…

mysql高级sql

文章目录 一&#xff0c;查询1.按关键字排序1.1按关键字排序操作(1)按分数排序查询&#xff08;不加asc默认为升序&#xff09;(2)按分数降序查询&#xff08;DESC&#xff09;(3)使用where进行条件查询(4)使用ORDER BY语句对多个字段排序 1.2使用区间判断查询&#xff08;and/…

ESP32 TCP通信交换数据Mixly Arduino编程

TCP通信交换数据 在ESP32与ESP32或其它局域网络内主机间传输数据时&#xff0c;TCP是很方便的&#xff0c;特别当我们连接互联网后ESPnow不能用&#xff0c;MQTT又不稳定发送大量的数据&#xff0c;同时蓝牙有其它用途时&#xff0c;那么学会TCP通信协议就变得十分重要。 一、…

【开源分享】vsomeip 安装、编译、运行步骤笔记

文章目录 1. 摘要2. 安装、编译2.1 开发环境说明2.2 安装依赖2.3 获取代码2.4 编译代码2.5 安装 3. 测试验证参考 1. 摘要 本文主要描述 vsomeip 的安装、编译与运行步骤。下载源码&#xff0c;安装必要依赖&#xff0c;如Boost和CMake。通过CMake配置编译 vsomeip 库&#xf…

2024年10款好用的图纸加密软件推荐|企业图纸的守护神

在数字化时代&#xff0c;图纸数据的安全性是企业不可忽视的重要任务。随着技术的不断进步&#xff0c;图纸加密软件成为了保护企业知识产权和敏感数据的关键工具。以下是2024年推荐的10款好用的图纸加密软件&#xff0c;它们各具特色&#xff0c;能够满足不同企业的需求。 1.…

Java数组的定义及遍历

数组的声明 长度不能超过定义的长度。超过则会报错通过下标来访问 数组的遍历 最常用最简单的方法是增强for循环。

【解决bug之路】npm install node-sass(^4.14.1)连环报错解决!!!(Windows)

有关node-sass的深入分析可参考&#xff1a;又报gyp ERR&#xff01;为什么有那么多人被node-sass 坑过&#xff1f; 主要有如下三方面错误&#xff0c;请自查&#xff1a; 1.node&#xff0c;npm版本需与node-sass版本匹配&#xff0c;像node-sass&#xff08;^4.14.1&#x…

第三部分:4---进程地址空间

目录 数组的空间分配解析&#xff1a; 物理地址和虚拟地址&#xff1a; 虚拟地址空间&#xff1a; 进程地址空间的本质&#xff1a; 为什么要有进程地址空间&#xff1f; 页表对进程访问内存的检查&#xff1a; 进程地址空间和页表如何关联起来&#xff1f; 进程的独立…

保姆级离线+windows环境+私有化部署大模型

基于gis数据的高敏感高保密性要求&#xff0c;相信gis的小伙伴都有如下的需求&#xff1a;在内网&#xff0c;无外网环境下&#xff0c;部署自己的私有化大模型。 1.环境背景&#xff1a; 没有Linux环境&#xff0c;只是windows 无外网&#xff0c;内网环境 2.安装部署过程…

数据结构与算法1: 链表

基础知识 链表可以被想象为一系列的节点&#xff0c;每个节点至少有一个指针指向下一个节点&#xff0c;在最后一个节点&#xff0c;用null pointer来表示链表的结束。 链表的创建速度通常很快&#xff0c;在表头和表尾的插入也很快&#xff08;O(1)&#xff09;&#xff0c;…

HCIA--实验十三:VLAN间通信子接口实验/双单臂路由实验

一、实验内容 1.需求/要求&#xff1a; 将两个单臂路由通过两台交换机连接起来&#xff0c;成为双臂路由&#xff0c;并探讨这么做的原因。实现全网通&#xff0c;让任何一台主机之间都可以通信。 二、实验过程 1.拓扑图&#xff1a; 2.步骤&#xff1a; 1.给PC配置ip地址…

Oceanbase Restore Point实践

官网链接&#xff1a;Restore Point-V3.2.4-OceanBase 数据库文档-分布式数据库使用文档 在很多应用系统中&#xff0c;用户需要查询数据库中的某个时间点&#xff0c;或者特定版本的数据来完成一些数据分析或汇总之类的操作。 OceanBase 数据库在 V2.2.7x 版本中提供了 Restor…

大学生租房平台:SpringBoot框架的设计与实现

第4章 系统设计 一个成功设计的系统在内容上必定是丰富的&#xff0c;在系统外观或系统功能上必定是对用户友好的。所以为了提升系统的价值&#xff0c;吸引更多的访问者访问系统&#xff0c;以及让来访用户可以花费更多时间停留在系统上&#xff0c;则表明该系统设计得比较专业…

5.第二阶段x86游戏实战2-认识内存

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…

分享从零开始学习网络设备配置--任务6.3 使用基本ACL限制网络访问

任务描述 某公司构建了互联互通的办公网&#xff0c;为保护公司内网用户数据的安全&#xff0c;该公司实施内网安全防范措施。公司分为经理部、财务部和销售部&#xff0c;分属3个不同的网段&#xff0c;3个部门之间用路由器进行信息传递。为了安全起见&#xff0c;公司领导要求…

C语言——希尔排序

希尔排序是对于插入排序的一种优化 代码&#xff1a; #include <stdio.h> #include <stdlib.h> void shell_sort(int* p, int len) { int i; int j; int step; int tmp; for (step len / 2; step > 0; step step / 2) { fo…

JavaWeb【day14】--(SpingBoot原理)

SpingBoot原理 在前面十多天的课程当中&#xff0c;我们学习的都是web开发的技术使用&#xff0c;都是面向应用层面的&#xff0c;我们学会了怎么样去用。而我们今天所要学习的是web后端开发的最后一个篇章springboot原理篇&#xff0c;主要偏向于底层原理。 我们今天的课程安…

【达梦数据库】误删数据库目录问题复现解决方式

目录 1、环境搭建1.1、查询数据库版本1.2、创建表1.3、插入数据1.4、查询数据 2、故障重演2.1、服务器内直接删除整个库文件2.2、查询数据&#xff1a;数据可查2.3、查看进程&#xff1a;进程存在2.4、查看proc进程文件&#xff1a;deleted 3、数据恢复3.1、逻辑导出导入-(数据…