C++笔记---list

news2024/11/25 11:05:00

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),插入时有可能需要增容

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

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

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

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

相关文章

【目标检测数据集】工具钳子、剪刀、螺丝刀检测数据集3668张3类VOC+YOLO格式

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;3668 标注数量(xml文件个数)&#xff1a;3668 标注数量(txt文件个数)&#xff1a;3668 标注…

安卓13系统导航方式分析以及安卓13修改默认方式为手势导航 android13修改导航方式

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改5.彩蛋1.前言 系统导航方式默认一般是按键的,如果要改成手势的话,我们来看看用户怎么修改的: 设置=>系统=>手势=>系统导航,在这里进行修改。我们来分析下这个流程,并且将其修改为…

Stage模型UIAbility组件【单任务列表/多任务列表】

什么是多线程 比如你在微信中聊天 突然打开一个小程序 然后查看手机进程 如果一个软件有多个那就是多进程 &#xff08;目前小编看下来 只有安卓有 苹果看不出来&#xff09; 进程之间是可以相互跳转的 UIAbility组件可以是单个任务列表或多个任务列表 启动页想用哪个就设置哪个…

《python语言程序设计》2018版第8章第14题金融:信用卡号合法性 利用6.29题

一、之前6.29题我做的代码 这是用数字来进行分辨的 is_txt 4383576018402626 #合法def split_the_data_even(vis_n):current_a1 vis_n // 10000a_t1 vis_n % 10000# print("1th", a_t1)a_t2 current_a1 % 10000# print("2th", a_t2)current_a3 curre…

【自用23.】C++-const数据成员及const成员函数

const数据成员 const数据成员的初始化方式&#xff1a; 使用类内值&#xff08;C11支持&#xff09;使用构造函数的初始化列表 &#xff08;如果同时使用这两种方式&#xff0c;以初始化列表中的值为最终初始化结果&#xff09; 注意&#xff1a; 不能在构造函数或其他成员…

全新的训练算法:Reflection 70B进入大众的视野

在2024年9月6日&#xff0c;大模型的圈子迎来了一位新成员——Reflection 70B&#xff0c;它横扫了MMLU、MATH、IFEval、GSM8K等知名的模型基准测试&#xff0c;完美超越了GPT-4o&#xff0c;同时也超越了Claude3.5 Sonnet成为了新的大模型之王&#xff0c;Reflection 70B到底是…

Linux: network: esp:收到了重复的包

最近遇到一个问题,是说收到了dup的ESP包。 这个目前还是未解的谜题,不知道到底是谁发的重复包。 但是从wireshark里确实可以看到在相同SPI下,收到了两个序号相同的ESP包。 这个时候,就会触发防火墙的防御机制。下面是一个大模型给出的一个解答(主要介绍的是anti-replay的…

10款企业图纸加密软件大盘点|2024企业图纸加密软件推荐

在数字化时代&#xff0c;企业图纸数据的安全性显得尤为重要。图纸数据往往包含企业的核心技术、设计方案和知识产权&#xff0c;一旦泄露&#xff0c;将对企业造成不可估量的损失。因此&#xff0c;选择一款合适的图纸加密软件&#xff0c;成为企业保护核心资产的重要手段。以…

ComsolMatlab 互阻抗法计算多孔材料吸声(背腔无反射)

互阻抗法是一种用于计算多孔材料吸声性能的方法。它基于材料的声学参数来预测其吸声特性。互阻抗法的基本原理是考虑多孔材料中孔隙和固体相之间的相互作用&#xff0c;通过定义互阻抗来描述声波在材料中传播时的复杂情况。 在互阻抗法中&#xff0c;孔隙和固体相的声波反射和透…

第十五节:学习Springboot 的响应结果封装(自学Spring boot 3.x的第四天)

这节记录下如何使用枚举类和响应封装类实现响应结果封装。 第一步&#xff1a;新建立一个枚举类。枚举类的要求有两个变量&#xff0c;响应码code&#xff0c;响应信息desc。响应码需要跟前端约定好。 public enum ResponseCode {SUCCESS("success",101),ERROR(&qu…

2024年好用的10款图纸加密软件排行榜|图纸加密的最佳选择

随着企业对知识产权和数据安全需求的日益增加&#xff0c;图纸加密软件已经成为各行业保护敏感设计文档的关键工具。2024年&#xff0c;市场上涌现了多款优质的图纸加密软件&#xff0c;帮助企业有效防范数据泄露、维护信息安全。本文将为您介绍2024年好用的10款图纸加密软件&a…

入驻国际数字影像产业园有哪些优势?

在数字文创产业蓬勃发展的今天&#xff0c;选择入驻国际数字影像产业园&#xff0c;意味着您已踏上了一条通往行业前沿的快车道。这里&#xff0c;不仅是数字影像产业的聚集地&#xff0c;更是创新与梦想的孵化器。那么&#xff0c;入驻国际数字影像产业园究竟有哪些优势呢&…

vmware虚拟机 windows下查看进程id(pid)

在日常运维过程中, 发现宿主机cpu占用高, 经常要看是那一个虚拟机占用内存或cpu高. 但是在windows资源管理器中,所有的虚拟机都显示的是vmware-vmx.exe. 目前手动情况下就需要一个挨着一个去看. 有没有直接查看虚拟机进程id虚拟机运行目录的方法?? 实现步骤 1. 通过 vm…

【Python进阶】一篇文章教你如何使用PyCharm的调试功能?

要使用 PyCharm 的调试功能&#xff0c;可以按照以下步骤进行操作&#xff1a; 1、打开 PyCharm 启动 PyCharm&#xff0c;并打开你的 Python 项目。 2、设置断点 在你想要调试的代码行上&#xff0c;点击行号左侧的空白处&#xff0c;或者使用快捷键 Ctrl F8。这将在该行…

【C++ 高频面试题】指针和引用、关于内存泄漏和野指针问题

文章目录 1. 静态变量、全局变量、局部变量2. 指针和引用的区别3. 内存泄漏4. 野指针 1. 静态变量、全局变量、局部变量 ①局部变量&#xff1a; 作用范围&#xff1a;局部变量只在定义它的函数或代码块内有效&#xff0c;函数执行结束后&#xff0c;局部变量即失效。 生命周…

点亮第一盏LED灯,认识stm32最小系统板

嵌入式初学者&#xff0c;直接上手stm32开发&#xff0c;首先就是买块stm32开发板&#xff0c;选择最基础的系列&#xff0c;F103C8t6&#xff0c;先不管里面的数字代表什么意思&#xff0c;先弄明白F103代表什么意思&#xff0c;F103表示F1系列里的03子系列&#xff0c;OK&…

Http中get与post的区别,99%的人都理解错了吧

Get和Post是HTTP请求的两种基本方法&#xff0c;要说它们的区别&#xff0c;接触过WEB开发的人都能说出一二。 最直观的区别就是Get把参数包含在URL中&#xff0c;Post通过request body传递参数。 你可能自己写过无数个Get和Post请求&#xff0c;或者已经看过很多权威网站总结…

ICMAN触摸感应芯片方案

ICMAN触摸感应芯片 ICMAN触摸感应芯片采用先进的电容感应技术&#xff0c;能够精确检测和识别触摸动作。这一技术通过感应人体与传感器之间的微小电容变化来实现触控功能。相比传统的电阻式触控技术&#xff0c;电容感应技术具有更高的灵敏度和响应速度&#xff0c;能够提供更…

智能交通(四)——CMC特刊推荐

特刊征稿 01 期刊名称&#xff1a; Advanced Trends in Vehicular Ad hoc Networks (VANETs) 截止时间&#xff1a; 提交截止日期:2025年5月31日 目标及范围&#xff1a; 涵盖但不限于以下关键领域的提交&#xff1a; - 下一代 V2X 通信&#xff1a; 本主题探讨了如何将…

预防式编程——避免空值

文章目录 1. 输入验证2. 使用可选类型&#xff08;Optional Types&#xff09;3. 非空断言4. 安全调用运算符5. 提供默认值6. 设计模式7. 文档说明8. 数据结构的选择9. 逻辑判断10. 构造函数和初始化11. 使用工具类12. 枚举类型13. 编码规范14. 测试15. 重构16. 教育与培训 案例…