C++ List (带你一篇文章搞定C++中的List类)

打印 上一主题 下一主题

主题 826|帖子 826|积分 2493


  感谢大佬的光临各位,希望和各人一起进步,望得到你的三连,互三支持,一起进步
  数据结构习题_LaNzikinh篮子的博客-CSDN博客
  初阶数据结构_LaNzikinh篮子的博客-CSDN博客
  收入专栏:C++_LaNzikinh篮子的博客-CSDN博客
  其他专栏:c语言基础_LaNzikinh篮子的博客-CSDN博客
  个人主页:LaNzikinh-CSDN博客
    文章目录

  

  • 前言
  • 一.迭代器
  • 二.list的底层实现
  • 三.list利用和细节
  • 总结
  
前言

颠末前面两个容器的解说,实在已经对许多接口的利用都了解的差不多了,容器之间接口的利用真的都是大体相同的,但是底层的实现是不同的,本日我们来看看列表是怎样实现这个功能的,在解说列表的实现之前,我们先要再次来解说这个迭代器

一.迭代器

   迭代器的功能分为四种迭代器,反向迭代器,常数迭代器,反向常数迭代器,他的性子有三种,单向迭代器,双向迭代器和随机迭代器,性子的意思就是底层结构,底层结构可以决定可以用哪些算法
  

   举个例子来说,好比说我们之前学过的排序算法,他在库内里就一个专门这样子的函数sort,他支持的就是随机的代替其他不支持,所以说在list创建的类就不能用这个库内里的算法,只能自己实现一个,又好比说逆置算法reverse(需要++/--)他支持双向迭代器,所以说随机的也可以,但是单向的就不行
  

我们的list就是双向迭代器,我们之前学过的vector和string就是随机迭代器,那我们后面的栈和队列是什么呢?
   答案是根本不支持迭代,栈的特性是先进后出,队列的特性是先进先出,如果都满意了迭代器的遍历,那这些特性就不存在了,你支持了迭代器,那你的特性的意义在什么地方呢
  
二.list的底层实现

   我们之前讲过了库函数的利用,直接看文档即可,在这里就不做多的了解了,和之前的利用string,vector是大抵相同的,主要照旧来看list的底层,他是一个带头双向循环列表,不是我们以前C语言学过的单链表
  

   接下来了,我们先要来定义两个结构体,一个就是节点本身,另有一个就是指向节点的指针,即便它是一个带头双向循环列表,这两个也是必不可少的
  2.1定义两个结构体

结点
   const T& data = T();利用缺省参数来进行初始化
  1. template<class T>
  2. struct list_node
  3. {
  4.         T _data;
  5.         list_node<T>* _next;
  6.         list_node<T>* _prev;
  7.         list_node(const T& data = T())
  8.                 :_data(data)
  9.                 , _next(nullptr)
  10.                 , _prev(nullptr)
  11.         {}
  12. };
复制代码
指向节点的指针
   留意:这里获取的迭代器,就是一个节点的指针
  由于他是一个指针,所以说我们要用函数重载来定义它的加加减减和判断即是,另有解引用
  1. template<class T>
  2. struct list_iterator
  3. {
  4.         typedef list_node<T> Node;
  5.         typedef list_iterator<T> Self;
  6.         Node* _node;
  7.         list_iterator(Node* node)
  8.                 :_node(node)
  9.         {}
  10.         T& operator*()
  11.         {
  12.                 return _node->_data;
  13.         }
  14.         Self& operator++()
  15.         {
  16.                 _node = _node->_next;
  17.                 return *this;
  18.         }
  19.         Self& operator--()
  20.         {
  21.                 _node = _node->_prev;
  22.                 return *this;
  23.         }
  24.         bool operator!=(const Self& s) const
  25.         {
  26.                 return _node != s._node;
  27.         }
  28.         bool operator==(const Self& s) const
  29.         {
  30.                 return _node == s._node;
  31.         }
  32. };
复制代码
2.2insert()和 erase()

迭代器失效标题

   前面说过,此处各人可将迭代器临时理解成类似于指针,迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。由于list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。
  insert()
  1. void insert(iterator pos, const T& x)
  2. {
  3.         Node* cur = pos._node;
  4.         Node* prev = cur->_prev;
  5.         Node* newnode = new Node(x);
  6.         // prev newnode cur
  7.         newnode->_next = cur;
  8.         cur->_prev = newnode;
  9.         newnode->_prev = prev;
  10.         prev->_next = newnode;
  11.         ++_size;
  12. }
复制代码
erase()
   只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响
  1. void erase(iterator pos)
  2. {
  3.         assert(pos != end());
  4.         Node* prev = pos._node->_prev;
  5.         Node* next = pos._node->_next;
  6.         prev->_next = next;
  7.         next->_prev = prev;
  8.         delete pos._node;
  9.         --_size;
  10. }
复制代码
  所以要用返回值来改正,防止迭代器失效
  1. int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
  2. list<int> l(array, array+sizeof(array)/sizeof(array[0]));
  3. auto it = l.begin();
  4. while (it != l.end())
  5. {
  6. l.erase(it++); // it = l.erase(it);
  7. }
复制代码
2.3头插尾插一个数据

   直接复用就可以了
  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. }
复制代码
2.4析构函数和迭代器函数

  1. iterator begin()
  2. {
  3.         return _head->_next;
  4. }
  5. iterator end()
  6. {
  7.         return _head;
  8. }
  9. list()
  10. {
  11.         _head = new Node;
  12.         _head->_next = _head;
  13.         _head->_prev = _head;
  14.         _size = 0;
  15. }
复制代码
2.4成员对象

  1. private:
  2.         Node* _head;
  3.         size_t _size;
复制代码
三.list利用和细节

   照旧增补几个list一些独特的函数利用方式和一些利用它的细节
  emplace:构造和插入元素

   他是支持直接传构造函数对象的参数的
  1. list<A> lt;
  2. A a1(1,1);
  3. lt.push_back(3,3);//不合理,不存在
  4. lt.emplace_back(3,3);//可以
复制代码
unique:删除重复值

   留意:前提要有序不然删不完全
  merge:归并排序列表

   留意:归并排序列表,v1会被滞空
  1. it.merge(v1);
复制代码
补:

   如果要在pos的位置插入一个30大小的元素
  1. auto it = lt.begin();
  2. int k = 3;
  3. while (k--)
  4. {
  5.         ++*it;
  6. }
  7. it.insert(it, 30);
复制代码
由于他的迭代器只支持++

总结

链表容器结构到这里就结束了,下一章,我们将引入一个适配器的概念去完成栈与队列的知识

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

大连全瓷种植牙齿制作中心

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表