• C++——list


    目录

    list介绍

    list的函数接口

    构造函数

    push_front和pop_front

    push_back和pop_back

    insert

    erase

    迭代器

    front和back

    size

    resize

    empty

    clear

    list::sort

    unique

    reverse

    迭代器的实现


    list介绍

    1. list是一种可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
    2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立结点当中,在结点中通过指针指向其前一个元素和后一个元素。
    3. list与forward_list非常相似,最主要的不同在于forward_list是单链表,只能进行单方向迭代。
    4. 与其他容器相比,list通常在任意位置进行插入、删除元素的执行效率更高。
    5. list和forward_list最大的缺陷是不支持在任意位置的随机访问,其次,list还需要一些额外的空间,以保存每个结点之间的关联信息(对于存储的类型较小元素来说这可能是一个重要的因素)。

            总的来说,list就是一个带头双向循环链表。


    list的函数接口

    构造函数

    1. list<int> l1; //构造int类型的空容器
    2. list<int> l2(10, 2); //构造含有10个2的int类型容器
    3. list<int> l3(l2); //拷贝构造int类型的l2容器的复制品

    push_front和pop_front

    1. // push_front函数用于头插,pop_front函数用于头删
    2. list<int> l;
    3. l.push_front(1);
    4. l.push_front(0);
    5. l.pop_front()

    push_back和pop_back

    1. // push_back函数用于尾插,pop_back函数用于尾删
    2. list<int> l;
    3. l.push_back(0);
    4. l.push_back(1);
    5. l.pop_back();

    insert

    1. list<int> l;
    2. l.push_back(0);
    3. l.push_back(1);
    4. l.push_back(2);
    5. list<int>::iterator pos = find(l.begin(), l.end(), 2); // 要包含algorithm库
    6. l.insert(pos, 10); // 在pos=2位置之前插入10
    7. l.insert(pos, 2, 9); // 在pos=2位置之前插入2个9

    erase

    1. list<int> l;
    2. l.push_back(0);
    3. l.push_back(1);
    4. list<int>::iterator pos = find(l.begin(), l.end(), 1);
    5. if (pos != l.end()) // 一定要找到了再删除
    6. {
    7. l.erase(pos); // 删除指定迭代器位置的元素
    8. // pos = l.erase(pos) // 也可以接受返回值,返回的是删除位置的下一个节点
    9. }

            要注意的是list也有迭代器失效的问题,既然放到erase这里才讲,那就是erase的时候会导致迭代器失效。

    迭代器

    1. list<int> l(5, 2);
    2. // 正向迭代器遍历容器
    3. list<int>::iterator it = lt.begin();
    4. while (it != lt.end())
    5. {
    6. cout << *it << " ";
    7. it++;
    8. }
    9. // 反向迭代器遍历容器
    10. list<int>::reverse_iterator rit = lt.rbegin();
    11. while (rit != lt.rend())
    12. {
    13. cout << *rit << " ";
    14. rit++;
    15. }

    front和back

    1. // size函数用于获取当前容器当中的元素个数
    2. list<int> l;
    3. l.push_back(0);
    4. l.push_back(1);
    5. l.push_back(2);
    6. cout << l.size() << endl;

    size

    1. // front函数用于获取list容器当中的第一个元素,back函数用于获取list容器当中的最后一个元素
    2. list<int> l;
    3. l.push_back(0);
    4. l.push_back(1);
    5. l.push_back(2);
    6. cout << l.front() << endl;
    7. cout << l.back() << endl;

    resize

    1. // 当所给值大于当前的size时,将size扩大到该值,扩大的数据为第二个所给值,若未给出,则默认为容器所存储类型的默认构造函数所构造出来的值。
    2. // 当所给值小于当前的size时,将size缩小到该值。
    3. list<int> l(5, 1);
    4. l.resize(10, 2);
    5. l.resize(2);

    empty

    1. list<int> l;
    2. cout << l.empty() << endl; // 判断容器是否为空,返回的是bool类型,就是0或1

    clear

    1. // clear函数用于清空容器,清空后容器的size为0。
    2. list<int> l(5, 1);
    3. l.clear();

    list::sort

    1. list<int> l;
    2. l.push_back(4);
    3. l.push_back(7);
    4. l.push_back(5);
    5. l.push_back(9);
    6. l.sort(); // 默认将容器内数据排为升序
    7. // 既然algorithm中已经有了一个sort函数,那为什么还有在list中加入这个函数呢
    8. // 那是因为,算法库中的sort让list使用是会报错的
    9. // 算法库中的sort要求物理空间必须是连续的
    10. // 注意:一般也不会使用list中的sort函数
    11. // 在数据量大的时候,效率较低,不如直接把数据插入到vector中使用algorithm中的sort函数

    unique

    1. // unique函数用于删除容器当中连续的重复元素,使用这个函数必须要先排序
    2. list<int> l;
    3. l.push_back(0);
    4. l.push_back(0);
    5. l.push_back(1);
    6. l.push_back(1);
    7. l.push_back(2);
    8. l.push_back(3);
    9. l.unique();

    reverse

    1. // reverse函数用于将容器当中元素的位置进行逆置
    2. list<int> l();
    3. l.push_back(0);
    4. l.push_back(1);
    5. l.push_back(2);
    6. l.push_back(3);
    7. l.reverse();

    迭代器的实现

    1. // 简单定义一下list的结构
    2. template<class T>
    3. class list_node // 结点
    4. {
    5. T _data;
    6. list_node* _next;
    7. list_node* _prev;
    8. list_node(const T& x = T())
    9. :_data(x)
    10. ,_next(nullptr)
    11. ,_prev(nullptr)
    12. {}
    13. };
    14. template<class T>
    15. struct __list_iterator // 这里使用struct不用考虑class的权限
    16. {
    17. typedef list_node Node;
    18. typedef __list_iterator iterator;
    19. Node* _node; // 迭代器还是结点的指针
    20. __list_iterator(Node* node) // 用指针初始化迭代器
    21. :_node(node)
    22. {}
    23. bool operator!=(const iterator& it) const
    24. {
    25. return _node != it._node; // 判断两个迭代器指针不等就可以了
    26. }
    27. T& operator*()
    28. {
    29. return _node->_data; // 解引用就是拿到结点指向的值
    30. }
    31. T* operator->() // ->运算符是使用指针来访问成员
    32. {
    33. return &(operator*()); // 拿到指针指向的数据再取地址返回这个指针类型
    34. }
    35. iterator& operator++() // ++就把下一个结点的指针赋值给_node,注意这是前置++
    36. {
    37. _node = _node->_next;
    38. return *this;
    39. }
    40. };
    41. template<class T>
    42. class list
    43. {
    44. typedef list_node Node;
    45. public:
    46. typedef __list_iterator iterator;
    47. void push_back(const T& x)
    48. {
    49. Node* tail = _head->_prev;
    50. Node* newnode = new Node(x);
    51. tail->_next = newnode;
    52. newnode->_prev = tail;
    53. newnode->_next = _head;
    54. _head->_prev = newnode;
    55. }
    56. iterator begin() // begin就是头结点的下一个
    57. {
    58. return iterator(_head->_next); // 使用头结点的下一个的指针构造迭代器
    59. }
    60. iterator end() // end就是头结点
    61. {
    62. return iterator(_head->_prev);
    63. }
    64. list() // 构造函数
    65. {
    66. _head = new Node;
    67. _head->_next = _head;
    68. _head->_prev = _head;
    69. }
    70. public:
    71. Node* _head;
    72. };
    73. void test01()
    74. {
    75. list<int> l;
    76. l.push_back(1);
    77. l.push_back(2);
    78. list<int>::iterator it = l.begin();
    79. while (it != l.end())
    80. {
    81. cout << it->_data << endl;
    82. // 使用->运算符重载,有的时候,list中存放的不是int类型的数据,只使用一次it->是不够的
    83. // 比如一个坐标类型,有两个成员变量,it->只能拿到这个坐标类型的指针
    84. // 但是还不能访问到这个类型的成员,所以需要使用it->->
    85. // 但这样又会显得很奇怪,所以编译器为了增加可读性进行了特殊处理,所以用一个->就可以了
    86. ++it;
    87. }
    88. }
    1. // 既然有了正常的迭代器,那如果是const对象呢
    2. // const对象使用正常迭代器时会出现权限的放大,不可以被修改
    3. // 那有一个返回const类型的函数就可以了,例如下面这个函数
    4. T& opeartor*()
    5. {}
    6. // 这里可以添加一个返回const类型的函数
    7. // 但是只有返回类型不一样不能构成重载
    8. // 所以笨方法就是再写一个const迭代器类,这个类除了返回值不一样,其他的基本都一样
    9. // 但是很忌讳这样写
    10. // 可以这样该一下模板参数
    11. template<class T, class Ref, class Ptr> // Ref就是引用,Ptr就是指针
    12. struct __list_iterator
    13. {
    14. typedef __list_iterator iterator;
    15. // ...
    16. }
    17. // operator*()就可以修改为
    18. Ref opeartor*()
    19. {}
    20. // operator->()可以修改为
    21. Ptr operator->()
    22. {}
    23. template<class T>
    24. class list
    25. {
    26. typedef list_node Node;
    27. public:
    28. typedef __list_iterator iterator;
    29. typedef __list_iteratorconst T&, const T*> const_iterator;
    30. const_iterator cbegin() const
    31. {
    32. return cosnt_iterator(_head->_next);
    33. }
    34. const_iterator cend() const
    35. {
    36. return cosnt_iterator(_head);
    37. }
    38. // ...
    39. }
  • 相关阅读:
    Django 表单处理:从前端到后台的全流程指南
    Python机器视觉--OpenCV进阶(核心)--图像的开,闭运算,形态学梯度,顶帽,黑帽运算
    如何编写基本的Java程序
    MS2401隔离式调制器可pin对pin兼容AD7401/AMC1305
    C# 结构型设计模式----适配器模式
    SpringBoot+POI方式导出excel【加水印】
    Proxy代理数据拦截方法
    IDEA 2022.3 发布,终于支持 redis 了
    Linux安装Jenkins
    【机器学习】线性回归【上】朴素最小二乘估计
  • 原文地址:https://blog.csdn.net/m0_64607843/article/details/134043183