• 【C++干货铺】list的使用 | 模拟实现


    =========================================================================

    个人主页点击直达:小白不是程序媛

    C++专栏:C++干货铺

    代码仓库:Gitee

    =========================================================================

    目录

    list的介绍及使用

    list的介绍

    list的使用

    list的构造

    list迭代器的使用

    list的增删查改

    list的模拟实现

    结点的封装

    迭代器的封装

    list成员变量

    构造函数

    拷贝构造函数

    operator=

    析构函数和清理空间

    insert

    erase

    push_back、push_front、pop_back、pop_front

    begin+end、cbegin+cend

    list和vector的对比


    list的介绍及使用

    list的介绍

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

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

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

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

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

    list的使用

    list的构造

    函数名称       函数作用
    list(size_t type n,const value_type构造的list中包含n个值为val的元素
    list()构造空list
    list(const list&x)拷贝构造函数
    list(first,last)迭代器区间中的元素构造list
    1. list<int> lt1(10, 1);
    2. list<int> lt2;
    3. list<int> lt3(lt1);
    4. list<int> lt4(lt3.begin(), lt3.end());
    5. list<int>::iterator lt = lt4.begin();
    6. while (lt != lt4.end())
    7. {
    8. cout << *lt << " ";
    9. lt++;
    10. }
    11. cout << endl;
    12. for (auto e : lt4)
    13. {
    14. cout << e << " ";
    15. }
    16. cout << endl;

    list迭代器的使用

    函数名称函数作用
    begin+end返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
    rbegin+rend返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的
    reverse_iterator,即begin位置

    list的增删查改

    函数名称函数作用
    push_back在list尾部插入值为val的元素
    push_front在list首元素前插入值为val的元素
    pop_back删除list中最后一个元素
    pop_front删除list中第一个元素
    insert在list position 位置中插入值为val的元素
    erase删除list position位置的元素
    1. list<int> lt(5,9);
    2. //头插
    3. lt.push_front(1);
    4. //尾插
    5. lt.push_back(2);
    6. for (auto e : lt)
    7. {
    8. cout << e << " ";
    9. }
    10. cout << endl;
    11. //尾删
    12. lt.pop_back();
    13. //头删
    14. lt.pop_front();
    15. for (auto e : lt)
    16. {
    17. cout << e << " ";
    18. }
    19. cout << endl;
    20. lt.insert(lt.begin(), 30);
    21. for (auto e : lt)
    22. {
    23. cout << e << " ";
    24. }
    25. cout << endl;
    26. lt.erase(lt.begin());
    27. for (auto e : lt)
    28. {
    29. cout << e << " ";
    30. }

     


    list的模拟实现

    list通过一个一个结构体的结点实现,节点中包括指向下一个位置、指向前一个位置的指针和有效数值组成。

    结点的封装

    使用struct而不是class是因为默认为公开的

    1. template<class T>
    2. //封装结点
    3. struct list_node
    4. {
    5. list_node(const T& x=T())
    6. :_data(x)
    7. ,_next(nullptr)
    8. ,_prev(nullptr)
    9. {
    10. }
    11. T _data;
    12. list_node* _next;
    13. list_node* _prev;
    14. };

    迭代器的封装

    list和顺序表最大的不同是list物理上不连续,需要使用指针进行移动直线下一个或者指向其他的操作,而不像顺序表物理上是连续的,++、--都可以拿到有效数据;因此需要对迭代器单独封装。

    1. //迭代器封装
    2. template<class T, class Ref, class Ptr>
    3. struct __list_iterator
    4. {
    5. typedef list_node<T> Node;
    6. typedef __list_iterator<T, Ref, Ptr> self;
    7. Node* _node;
    8. __list_iterator(Node* node)
    9. :_node(node)
    10. {}
    11. self& operator++()
    12. {
    13. _node = _node->_next;
    14. return *this;
    15. }
    16. self& operator--()
    17. {
    18. _node = _node->_prev;
    19. return *this;
    20. }
    21. self operator++(int)
    22. {
    23. self tmp(*this);
    24. _node = _node->_next;
    25. return tmp;
    26. }
    27. self operator--(int)
    28. {
    29. self tmp(*this);
    30. _node = _node->_prev;
    31. return tmp;
    32. }
    33. Ref operator*()
    34. {
    35. return _node->_data;
    36. }
    37. Ptr operator->()
    38. {
    39. return &_node->_data;
    40. }
    41. bool operator!=(const self& s)
    42. {
    43. return _node != s._node;
    44. }
    45. bool operator==(const self& s)
    46. {
    47. return _node == s._node;
    48. }
    49. };

    list成员变量

    指向结构体节点的指针和有效数据的个数

    1. Node* _node;
    2. size_t _size;

    构造函数

    1. typedef list_node<T> Node;
    2. public:
    3. typedef __list_iterator<T, T&, T*> iterator;
    4. typedef __list_iterator<T, const T&, const T*> const_iterator;
    5. void empty_init()
    6. {
    7. _node = new Node;
    8. _node->_next = _node;
    9. _node->_prev = _node;
    10. _size = 0;
    11. }
    12. list()
    13. {
    14. empty_init();
    15. }

    拷贝构造函数

    1. list(list& x)
    2. {
    3. empty_init();
    4. for (auto e : x)
    5. {
    6. push_back(e);
    7. }
    8. }

    operator=

    1. void swap(list<T>& lt)
    2. {
    3. std::swap(_node, lt._node);
    4. std::swap(_size, lt._size);
    5. }
    6. list<int>& operator=(list<int> lt)
    7. {
    8. swap(lt);
    9. return *this;
    10. }

    析构函数和清理空间

    1. ~list()
    2. {
    3. clear();
    4. delete _node;
    5. _node = nullptr;
    6. }
    7. void clear()
    8. {
    9. iterator it = begin();
    10. while (it != end())
    11. {
    12. it = erase(it);
    13. }
    14. }

    insert

    1. void insert(iterator pos, const T& x)
    2. {
    3. Node* cur = pos._node;
    4. Node* newnode = new Node(x);
    5. Node* prev = cur->_prev;
    6. prev->_next = newnode;
    7. newnode->_next = cur;
    8. cur->_prev = newnode;
    9. newnode->_prev = prev;
    10. _size++;
    11. }

    erase

    1. iterator erase(iterator pos)
    2. {
    3. Node* cur = pos._node;
    4. Node* prev = cur->_prev;
    5. Node* next = cur->_next;
    6. prev->_next = next;
    7. next->_prev = prev;
    8. return next;
    9. }

    push_back、push_front、pop_back、pop_front

    1. void push_back(const T& x)
    2. {
    3. insert(end(), x);
    4. }
    5. void push_front(const T& x)
    6. {
    7. insert(begin(), x);
    8. }
    9. void pop_front()
    10. {
    11. erase(begin());
    12. }
    13. void pop_back()
    14. {
    15. erase(--end());
    16. }

    begin+end、cbegin+cend

    1. const_iterator begin() const
    2. {
    3. return const_iterator(_node->_next);
    4. }
    5. const_iterator end() const
    6. {
    7. return const_iterator(_node);
    8. }
    9. iterator end()
    10. {
    11. return _node;
    12. }
    13. iterator begin()
    14. {
    15. return _node->_next;
    16. }

    list和vector的对比

    vectorlist



    动态顺序表,一段连续空间带头结点的双向循环链表


    访
    支持随机访问,访问某个元素效率O(1)不支持随机访问,访问某个元素
    效率O(N)




    任意位置插入和删除效率低,需要搬移元素,时间复杂
    度为O(N),插入时有可能需要增容,增容:开辟新空
    间,拷贝元素,释放旧空间,导致效率更低
    任意位置插入和删除效率高,不
    需要搬移元素,时间复杂度为
    O(1)




    底层为连续空间,不容易造成内存碎片,空间利用率
    高,缓存利用率高
    底层节点动态开辟,小节点容易
    造成内存碎片,空间利用率低,
    缓存利用率低


    原生态指针对原生态指针(节点指针)进行封装




    在插入元素时,要给所有的迭代器重新赋值,因为插入
    元素有可能会导致重新扩容,致使原来迭代器失效,删
    除时,当前迭代器需要重新赋值否则会失效
    插入元素不会导致迭代器失效,
    删除元素时,只会导致当前迭代
    器失效,其他迭代器不受影响
    使


    需要高效存储,支持随机访问,不关心插入删除效率大量插入和删除操作,不关心随
    机访问

    今天对list的介绍和底层模拟实现的分享到这就结束了,希望大家读完后有很大的收获,也可以在评论区点评文章中的内容和分享自己的看法。您三连的支持就是我前进的动力,感谢大家的支持!! !

  • 相关阅读:
    2021 年的 Flutter 状态管理:如何选择?
    Unity开发者——编辑器技巧
    MFC基础-选项卡控件
    基于SSM的高校共享单车管理系统【数据库设计、源码、开题报告】
    Java基础(二十六):正则表达式
    揭秘 Task.Wait
    面试高频问题----2
    【Harmony3.1/4.0】笔记六-对话框
    JAVASE语法零基础——继承1
    BUUCTF 刮开有奖 1
  • 原文地址:https://blog.csdn.net/qq_55119554/article/details/134563249