当前位置:   article > 正文

list容器

list容器

目录

1.介绍(cv的,网上太多了)

 2.使用

2.1构造函数

2.2迭代器

begin(),end(),rbegin(),rend(),cbegin(),cend(),crbegin(),crend()

遍历方式

2.3 容量

empty()

size()

max_size()

2.4元素遍历

 front()

back()

2.5修改

assign()

push_front()

pop_front()

push_back()

pop_back()

insert()

erase()

swap()

resize()

clear()

2.6操作

 reverse()

sort()

merge()

unique()

remove()

remove_if()

splice()

3.模拟


使用这一块我会写的比较简洁,实在是前面vector和string已经写了很多相同的了

1.介绍(cv的,网上太多了)

1. list 是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
2. list 的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向
其前一个元素和后一个元素。
3. list forward_list 非常相似:最主要的不同在于 forward_list 是单链表,只能朝前迭代,已让其更简单高
效。
4. 与其他的序列式容器相比 (array vector deque) list 通常在任意位置进行插入、移除元素的执行效率
更好。
5. 与其他序列式容器相比, list forward_list 最大的缺陷是不支持任意位置的随机访问,比如:要访问 list
的第 6 个元素,必须从已知的位置 ( 比如头部或者尾部 ) 迭代到该位置,在这段位置上迭代需要线性的时间
开销; list 还需要一些额外的空间,以保存每个节点的相关联信息 ( 对于存储类型较小元素的大 list 来说这
可能是一个重要的因素 )

 2.使用

2.1构造函数

  1. //第一种常用
  2. //第一个参数给数量,第二个参数给初始化的值,比如2个3
  3. list<int>i(2,3);
  4. //3 3
  5. //第二种常用
  6. //给一个迭代器区间(可以是指针)
  7. list<int>i1(i.begin(), i.end());
  8. //3 3
  9. //第三种常用
  10. //拷贝构造
  11. list<int>l3(i1);
  12. //3 3
  13. 当然还有就是无参构造
  14. list()

另外,还有一个就是=的运算符重载

也就是说,可以这样写

  1. list<int>i1(3,6);
  2. list<int>l3 = i1;//注意,这其实还是拷贝构造,赋值是两个都已存在的实体类之间
  3. list<int>j;
  4. j = l3;//这才是赋值

2.2迭代器

begin(),end(),rbegin(),rend(),cbegin(),cend(),crbegin(),crend()

我们要知道,list是个双向循环的链表,也就是说,每个节点,都存在着前驱和后驱。

begin()和rend(),指向的就是第一个节点(有效节点),rbegin()和end()指向的就是头结点(不存有效数据)

begin(),end()如果++,是向后移动,rbegin(),rend()是向前移动

至于加个c,纯粹就是const修饰的迭代器罢了

遍历方式

list没有[]的重载,因为list的每个元素,物理空间不一定连续,那么就不能用指针+-的方式来随机访问,因此这里只能用迭代器遍历的方式

  1. list<int>i = { 2,3,4,5,6,6,7,8 };
  2. list<int>::iterator it = i.begin();
  3. while (it != i.end())
  4. {
  5. cout << (*it) << " ";
  6. it++;
  7. }
  8. 或者范围for(但范围for本质还是迭代器)

2.3 容量

empty()

很经典的判断容器里面是否还未空,是返回true,否返回false

size()

返回有效数据个数

max_size()

返回最大能存的数据个数(取决于编译环境)

2.4元素遍历

 front()

返回容器第一个元素,分两种重载,即非const和const(避免权限放大)

back()

返回容器最后一个元素,同样两种重载,非const和const(原因同理)

2.5修改

assign()

  1. list<int>i = { 2,3,4,5,6,6,7,8 };
  2. //void assign (size_type n, const value_type& val);
  3. list<int>c;
  4. c.assign(4, 5);
  5. //c : 5555
  6. //注意是替换,不是追加
  7. //template <class InputIterator>
  8. //void assign(InputIterator first, InputIterator last);
  9. c.assign(i.begin(), i.end());
  10. //c:2,3,4,5,6,6,7,8
  11. //注意,是替换,不是追加

push_front()

就是在第一个元素前插入一个数据

pop_front()

删除第一个元素

push_back()

在最后插入一个数据

pop_back()

删除最后一个数据

insert()

  1. list<int>i = { 2,3,4,5,6,6,7,8 };
  2. //第一种
  3. //iterator insert (iterator position, const value_type& val);
  4. //在迭代器指定位置插入一个数据
  5. list<int>::iterator it = i.begin();
  6. while (it != i.end())
  7. {
  8. if ((*it) == 4)break;
  9. it++;
  10. }
  11. it=i.insert(it, 4);
  12. cout << (*it);
  13. cout << endl;
  14. //这个重载还会返回迭代器,比如插入了一个4,那返回的就是指向这个4的迭代器
  15. //i: 2,3,4,4,5,6,6,7,8
  16. //第二种
  17. //void insert (iterator position, size_type n, const value_type& val);
  18. //注意,这个重载是插入多个相同值的元素
  19. i.insert(it, 4,6);
  20. //i:2 3 4 6 6 6 6 4 5 6 6 7 8
  21. it = i.begin();
  22. list<int>c;
  23. //第三种,迭代器区间
  24. //template <class InputIterator>
  25. //void insert(iterator position, InputIterator first, InputIterator last);
  26. c.insert(c.begin(),i.begin(), i.end());
  27. //c::2 3 6 6 6 6 4 4 5 6 6 7 8

erase()

  1. list<int>i = { 2,3,4,5,6,6,7,8 };
  2. list<int>::iterator it = i.begin();
  3. while ((*it) != 4)it++;
  4. //第一种
  5. //iterator erase (iterator position);
  6. //删除迭代器指向数据,返回删除之后,新的原相对位置的数据的迭代器
  7. it=i.erase(it);
  8. //i:2 3 5 6 6 7 8
  9. //第二种,删除迭代器区间的元素
  10. //iterator erase (iterator first, iterator last);
  11. while ((*it) != 6)it++;
  12. i.erase(it, i.end());
  13. //i:2 3 5

swap()

简单的交换

resize()

扩容

  1. void resize (size_type n, value_type val = value_type());
  2. 给n个空间大小,初始化值val
  3. 这里是调用无参构造

clear()

清空容器

2.6操作

 reverse()

经典的逆置函数

  1. list<int>i = { 2,3,4,5,6,6,7,8 };
  2. i.reverse();
  3. //i:8 7 6 6 5 4 3 2

sort()

排序,默认从小到大排,你可以自己写个规则,用法跟algorithm的sort()一样

之所以单独提供,是因为链表的物理空间不连续,而算法库的sort是针对连续物理空间的

而且算法库用的是快排,而链表这里用的是归并

平时用的话,速度还不如直接拷贝到vector,用算法库的sort之后,再拷贝回链表快

merge()

合并两个有序的链表,注意一定要有序,然后根据规则(默认从小到大),跟algorithm的sort()一样.

unique()

去重,注意,相同的必须紧邻,所以你可以先sort(),如果为了适配自定义类型,可以自己写个规则,跟algorithm的sort()一样

remove()

跟erase的最大区别就是,不用给迭代器,只用给值,相当于容器自己找


remove_if()

满足条件才删除

splice()

  1. list<int>i = { 2,3,4,5,6,6,7,8 };
  2. list<int>b = { 6,7,8,9,0,34,12,1 };
  3. //第一种
  4. //void splice (iterator position, list& x);
  5. //直接把x的所有节点都转移过去,x变空
  6. i.splice(i.begin(), b);
  7. //i:6 7 8 9 0 34 12 1 2 3 4 5 6 6 7 8
  8. //void splice (iterator position, list& x, iterator i);
  9. list<int>c= { 6,7,8,9,0,34,12,1 };
  10. i.splice(i.begin(), c, c.begin());
  11. //i:6 6 7 8 9 0 34 12 1 2 3 4 5 6 6 7 8
  12. //是将第二个参数指向的链表中的最后一个参数的位置的值转移过去
  13. //c的第一个6会没掉
  14. //void splice (iterator position, list& x, iterator first, iterator last);
  15. //这个就不示范了,就是把一个区间的节点转移过去

注意,可以自己转移自己,就是在一个链表里面转移

3.模拟

反向迭代器,我在这里先不实现,等后面另一篇文章的时候再补充上去

  1. #pragma once
  2. #include<assert.h>
  3. namespace manba {
  4. //注意,这个是节点的类,我们实现的是双向带头链表,所以要有前驱和后驱
  5. template<class T>
  6. struct ListNode {
  7. ListNode<T>* _next;//前驱
  8. ListNode<T>* _prev;//后驱
  9. //注意<>在类里面,是可以忽略的,但我们严谨一些
  10. T _data;//存数据
  11. ListNode(const T& x=T())//默认构造函数,方便后面调用,因为不确定参数类型,所以默认值
  12. //直接用T类型的默认构造即可,不管是内置还是自定义都可以符合
  13. :_next(nullptr)
  14. , _prev(nullptr)
  15. , _data(x)
  16. {}
  17. };
  18. //这里通过Ref,第二个模板参数,简约的实现了const迭代器和正常迭代器的转换
  19. //ptr控制的是配合重载->,区分const迭代器和普通迭代器
  20. template<class T,class Ref,class Ptr>
  21. struct _list_iterator {
  22. typedef ListNode<T> Node;
  23. //注意,类名<T>才是一个链表节点类型,这里重命名一下,方便后面少写点
  24. typedef _list_iterator<T,Ref,Ptr> self;
  25. //跟上面一个同理,这里是一个链表迭代器类型
  26. Node* _node;
  27. //迭代器本质还是指针,只是因为原始指针针对物理空间不连续的容器,无法实现++
  28. //而我们无法重载指针,所以我们采用封装的方式,将指针封装起来,将封装起来的迭代器
  29. //重载前后置++,前后置--,解引用*等运算符,理由前驱后驱指针,实现向前向后走
  30. _list_iterator(Node* node)//这里是默认构造,方便各种调用
  31. :_node(node)
  32. {}
  33. //前置++
  34. self& operator++()
  35. {
  36. _node = _node->_next;
  37. return *this;
  38. }
  39. //后置++
  40. //注意,这里我们返回的时候不用&,因为tmp是局部变量,函数结束后会销毁,所以直接返回迭代器类型
  41. self operator++(int)
  42. {
  43. self tmp(*this);
  44. _node = _node->_next;
  45. return tmp;
  46. }
  47. //前置--
  48. self& operator--() {
  49. _node = _node->_prev;
  50. return *this;
  51. }
  52. //后置--,理由同理
  53. self operator--(int)
  54. {
  55. self tmp(*this);
  56. _node = _node->_prev;
  57. return tmp;
  58. }
  59. //解引用,利用ref,实现const迭代器和普通迭代器
  60. Ref operator*()
  61. {
  62. return _node->_data;
  63. }
  64. //不等于
  65. bool operator !=(const self& s)
  66. {
  67. return _node != s._node;
  68. }
  69. //等于
  70. bool operator==(const self& s)
  71. {
  72. return _node == s._node;
  73. }
  74. Ptr operator->()
  75. {
  76. return &_node->_data;
  77. }
  78. /*struct AA {
  79. int b;
  80. AA(int a = 1)
  81. :b(a)
  82. {
  83. }
  84. };*/
  85. /*manba::List<manba::AA> l1;
  86. l1.push_back(3);
  87. l1.push_back(7);
  88. l1.push_back(9);
  89. l1.push_back(12);
  90. manba::List<manba::AA>::iterator it = l1.begin();
  91. while (it != l1.end())
  92. {
  93. cout << it.operator->()->b << endl;
  94. cout<<it->b<<endl;
  95. it++;
  96. }
  97. cout << endl;*/
  98. };
  99. //const迭代器
  100. //template<class T>
  101. //struct _list_const_iterator {
  102. // typedef ListNode<T> Node;
  103. // //注意,类名<T>才是一个链表节点类型,这里重命名一下,方便后面少写点
  104. // typedef _list_const_iterator<T> self;
  105. // //跟上面一个同理,这里是一个链表迭代器类型
  106. // Node* _node;
  107. // //迭代器本质还是指针,只是因为原始指针针对物理空间不连续的容器,无法实现++
  108. // //而我们无法重载指针,所以我们采用封装的方式,将指针封装起来,将封装起来的迭代器
  109. // //重载前后置++,前后置--,解引用*等运算符,理由前驱后驱指针,实现向前向后走
  110. // _list_const_iterator(Node* node)//这里是默认构造,方便各种调用
  111. // :_node(node)
  112. // {}
  113. // //前置++
  114. // self& operator++()
  115. // {
  116. // _node = _node->_next;
  117. // return *this;
  118. // }
  119. // //后置++
  120. // //注意,这里我们返回的时候不用&,因为tmp是局部变量,函数结束后会销毁,所以直接返回迭代器类型
  121. // self operator++(int)
  122. // {
  123. // self tmp(*this);
  124. // _node = _node->_next;
  125. // return tmp;
  126. // }
  127. // //前置--
  128. // self& operator--() {
  129. // _node = _node->_prev;
  130. // return *this;
  131. // }
  132. // //后置--,理由同理
  133. // self operator--(int)
  134. // {
  135. // self tmp(*this);
  136. // _node = _node->_prev;
  137. // return tmp;
  138. // }
  139. // //解引用
  140. // const T& operator*()
  141. // {
  142. // return _node->_data;
  143. // }
  144. // //不等于
  145. // bool operator !=(const self& s)
  146. // {
  147. // return _node != s._node;
  148. // }
  149. // //等于
  150. // bool operator==(const self& s)
  151. // {
  152. // return _node == s._node;
  153. // }
  154. //};
  155. template<class T>
  156. class List {
  157. typedef ListNode<T> Node;
  158. public:
  159. typedef _list_iterator<T,T&,T*> iterator;
  160. //typedef _list_const_iterator<T> const_iterator;
  161. //这里迭代器直接传const类型的模板参数
  162. typedef _list_iterator<T, const T&,const T*> const_iterator;
  163. //跟上面一样
  164. //空链表初始化
  165. void empty_init() {
  166. _head = new Node;
  167. _head->_next = _head;
  168. _head->_prev = _head;
  169. }
  170. //默认构造函数
  171. List() {
  172. empty_init();
  173. }
  174. //拷贝构造函数
  175. //List(const List<T> &cp)
  176. List(List<T>& cp)
  177. {
  178. empty_init();
  179. for (const auto& e : cp)
  180. {
  181. push_back(e);
  182. }
  183. }
  184. //交换函数,交换物理地址
  185. void swap(List<T>&t)
  186. {
  187. std::swap(_head, t._head);
  188. }
  189. //赋值
  190. //第一种
  191. //List<T>& operator=(const List<T>& t2)
  192. /*List<T>& operator=( List<T>& t2)
  193. {
  194. if (this != &t2)
  195. {
  196. clear();
  197. for (const auto& e : t2)
  198. {
  199. push_back(e);
  200. }
  201. }
  202. return *this;
  203. }*/
  204. //第二种
  205. //这个方法的妙处在于,因为外面要赋值的链表是直接通过拷贝构造直接给了t2链表
  206. // ,也就是传值。
  207. //而拷贝构造我们用的是深拷贝,这样t2的空间和外面的类是不一样的,如此,直接
  208. //让this链表和t2交换,函数结束之后,t2作为局部变量,t2会自动调用析构函数。
  209. List<T>& operator=(List<T> t2)
  210. {
  211. swap(t2);
  212. return *this;
  213. }
  214. //返回第一个有效节点的迭代器
  215. iterator begin()
  216. {
  217. return _head->_next;
  218. }
  219. iterator end()
  220. {
  221. return _head;
  222. }
  223. //返回第一个有效节点的const迭代器
  224. const_iterator begin() const
  225. {
  226. return _head->_next;
  227. }
  228. const_iterator end() const
  229. {
  230. return _head;
  231. }
  232. //尾插
  233. void push_back(const T& x)
  234. {
  235. insert(iterator(_head), x);
  236. }
  237. //头插
  238. void push_front(const T& x)
  239. {
  240. insert(begin(), x);
  241. }
  242. //尾删
  243. void pop_back()
  244. {
  245. erase(--end());
  246. }
  247. //头删
  248. void pop_front()
  249. {
  250. erase(begin());
  251. }
  252. //在指定位置前面插入
  253. //vector 迭代器在insert会失效,list不会
  254. iterator insert(iterator pos, const T& x)
  255. {
  256. Node* cur = pos._node;
  257. Node* prev = cur->_prev;
  258. Node* newnode = new Node(x);
  259. prev->_next = newnode;
  260. newnode->_prev = prev;
  261. newnode->_next = cur;
  262. cur->_prev = newnode;
  263. return iterator(newnode);
  264. }
  265. //删除指定位置
  266. iterator erase(iterator pos)
  267. {
  268. assert(pos != end());
  269. Node* cur = pos._node;
  270. Node* prev = cur->_prev;
  271. Node* next = cur->_next;
  272. prev->_next = next;
  273. next->_prev = prev;
  274. delete cur;
  275. return next;
  276. }
  277. //注意,vector在insert和erase处都会有迭代器失效的风险,所以要注意返回值。
  278. //而list在insert处不会,不用担心,但是erase处就要考虑这个问题了,要注意接收返回值。
  279. //更详细的,可以参考我在vector文章里,vector的模拟中对insert和erase的模拟。
  280. //另外补充一下,vector处的erase会出现迭代器失效,主要是针对vs的强制检查和如果删除了最后一个
  281. //元素,会导致迭代器超出了数组有效范围。
  282. //而对于list的erase,因为链表底层物理不一定是连续的,这样原迭代器封装的指针指向的空间是非法空间,
  283. //所以要注意返回值。
  284. //清空链表
  285. void clear()
  286. {
  287. iterator it = begin();
  288. while (it != end())
  289. {
  290. it = erase(it);
  291. }
  292. }
  293. //析构函数
  294. ~List()
  295. {
  296. clear();
  297. delete _head;
  298. _head = nullptr;
  299. }
  300. private:
  301. Node* _head;
  302. //头结点,注意,整个list是左闭右开,head是也是end()返回的
  303. };
  304. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/860603
推荐阅读
相关标签
  

闽ICP备14008679号