当前位置:   article > 正文

C++list_c++ list find

c++ list find

  list是个双向链表。      

底层是带头双向循环链表

元素访问没有[]了,意味着以后大部分需要使用迭代器进行访问。

 先试着写一下代码。

  1. #include<list>
  2. using namespace std;
  3. void test1()
  4. {
  5. list<int> lt;
  6. lt.push_back(1);
  7. lt.push_back(2);
  8. lt.push_back(3);
  9. lt.push_back(4);
  10. list<int>::iterator it = lt.begin();
  11. while(it != lt.end())
  12. {
  13. cout << *it << endl;
  14. it++;
  15. }
  16. for (auto c : lt)
  17. {
  18. cout << c << endl;
  19. }
  20. }
  21. int main()
  22. {
  23. test1();
  24. return 0;
  25. }

到着遍历可以用反向迭代器。

 const对象调用const迭代器。


没有reserve和resize了,因为链表是按需要申请和释放空间的。 


获取头和尾的数据 。


如果pop_back的数量大于结点数量,会报错。


list中没有find()模板,需要调用algorithm库中的find。

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<list>
  4. using namespace std;
  5. void test2()
  6. {
  7. list<int> lt;
  8. lt.push_back(1);
  9. lt.push_back(2);
  10. lt.push_back(3);
  11. lt.push_back(4);
  12. auto pos = find(lt.begin(), lt.end(), 3);
  13. if (pos != lt.end())
  14. {
  15. lt.insert(pos, 30);
  16. }
  17. for (auto i : lt)
  18. {
  19. cout << i <<" ";
  20. }
  21. cout << endl;
  22. }
  23. int main()
  24. {
  25. test2();
  26. return 0;
  27. }

 

不失效。 

 但erase后,pos会失效,因为此时pos位置结点已经没了。


底层是快速排序。 

链表sort底层是归并排序。

为什么链表有自己的sort? 

 vector和string属于随机。 

底层是快速排序。 

它俩能够相减,需要迭代器是连续的,而链表迭代器不连续。

链表list要排序只能使用list的sort了。

用库里面sort对vector排序要比list中的sort对链表排序快。

平时头插和头删最好不用vector 。


remove一个对象中没有的数,不会报错。


利用unique进行去重,必须先保证链表数据有序。


合并

但在合并前,需要保证两个列表数据都是有序的,否则会报错。 


  1. void test3()
  2. {
  3. list<int> lt;
  4. lt.push_back(1);
  5. lt.push_back(2);
  6. lt.push_back(3);
  7. lt.push_back(4);
  8. lt.push_front(5);
  9. lt.push_front(6);
  10. for (auto e : lt)
  11. {
  12. cout << e << " ";
  13. }
  14. cout << endl;
  15. list<int> lt1(lt);
  16. for (auto e : lt1)
  17. {
  18. cout << e << " ";
  19. }
  20. cout << endl;
  21. lt.clear();
  22. for (auto e : lt)
  23. {
  24. cout << e << " ";
  25. }
  26. cout << endl;
  27. }

这个代码,我们需要写个深拷贝构造函数,因为默认拷贝构造函数是个浅拷贝,lt被clear后,地址被释放了一次,lt1出函数时,地址又被释放了一次,它俩是相同的地址,被释放两次,所以会报错。


简单模拟实现下list 

  1. #include<iostream>
  2. #include<string>
  3. #include<list>
  4. #include<algorithm>
  5. #include<assert.h>
  6. namespace bit
  7. {
  8. using namespace std;
  9. template<class T>
  10. struct list_node
  11. {
  12. list_node* _next;
  13. list_node* _prev;
  14. T _data;
  15. list_node(const T& x)
  16. :_next(nullptr)
  17. , _prev(nullptr)
  18. , _data(x)
  19. {}
  20. };
  21. template<class T, class Ref>
  22. struct list_iterator
  23. {
  24. typedef list_node<T> node;
  25. typedef list_iterator<T, Ref> self;
  26. node* _pnode;
  27. list_iterator(node* p)
  28. :_pnode(p)
  29. {}
  30. Ref operator*()
  31. {
  32. return _pnode->_data;
  33. }
  34. self& operator++()
  35. {
  36. _pnode = _pnode->_next;
  37. return *this;
  38. }
  39. self& operator--()
  40. {
  41. _pnode = _pnode->_prev;
  42. return *this;
  43. }
  44. bool operator!=(const self& e)
  45. {
  46. return _pnode != e._pnode;
  47. }
  48. };
  49. template<class T>
  50. class list
  51. {
  52. public:
  53. typedef list_node<T> node;
  54. typedef list_iterator<T, T&> iterator;
  55. typedef list_iterator<T, const T&> const_iterator;
  56. list()
  57. {
  58. empty_initialize();
  59. }
  60. const_iterator begin()const
  61. {
  62. return const_iterator(_head->_next);
  63. }
  64. const_iterator end()const
  65. {
  66. return const_iterator(_head);
  67. }
  68. iterator begin()
  69. {
  70. return iterator(_head->_next);
  71. }
  72. iterator end()
  73. {
  74. return iterator(_head);
  75. }
  76. iterator insert(iterator pos, const T& n)
  77. {
  78. node* newnode = new node(n);
  79. node* cur = pos._pnode;
  80. node* prev = cur->_prev;
  81. newnode->_next = cur;
  82. newnode->_prev = prev;
  83. prev->_next = newnode;
  84. cur->_prev = newnode;
  85. return iterator(newnode);
  86. }
  87. iterator erase(iterator pos)
  88. {
  89. assert(pos != end());
  90. node* prev = pos._pnode->_prev;
  91. node* next = pos._pnode->_next;
  92. prev->_next = next;
  93. next->_prev = prev;
  94. delete pos._pnode;
  95. return iterator(next);
  96. }
  97. list<T>& operator=(const list<T>& lt)
  98. {
  99. if (this != &lt)
  100. {
  101. clear();
  102. for (const auto& e : lt)
  103. {
  104. push_back(e);
  105. }
  106. }
  107. return *this;
  108. }
  109. void push_front(const T& x)
  110. {
  111. insert(begin(), x);
  112. }
  113. void pop_front()
  114. {
  115. erase(begin());
  116. }
  117. void pop_back()
  118. {
  119. erase(--end());
  120. }
  121. void clear()
  122. {
  123. iterator it = begin();
  124. while (it != end())
  125. {
  126. it = erase(it);
  127. }
  128. }
  129. /*list(const list<T>& lt)
  130. {
  131. _head = new node(T());
  132. _head->_next = _head;
  133. _head->_prev = _head;
  134. for (const auto &e : lt)
  135. {
  136. push_back(e);
  137. }
  138. }*/
  139. void empty_initialize()
  140. {
  141. _head = new node(T());
  142. _head->_next = _head;
  143. _head->_prev = _head;
  144. }
  145. ~list()
  146. {
  147. clear();
  148. delete _head;
  149. _head = NULL;
  150. }
  151. void push_back(const T& e)
  152. {
  153. /*node* NEW = new node(e);
  154. node* tail = _head->_prev;
  155. tail->_next = NEW;
  156. NEW->_next = _head;
  157. _head->_prev = NEW;
  158. NEW->_prev = tail;*/
  159. insert(end(), e);
  160. }
  161. private:
  162. node* _head;
  163. };
  164. void test1_1(const list<int>& lt1)
  165. {
  166. list<int>::const_iterator it1 = lt1.begin();
  167. cout << "lt1::";
  168. while (it1 != lt1.end())
  169. {
  170. cout << *it1 << " ";
  171. ++it1;
  172. }
  173. cout << endl;
  174. }
  175. void test1()
  176. {
  177. list<int> lt1;
  178. lt1.push_back(1);
  179. lt1.push_back(2);
  180. lt1.push_back(3);
  181. lt1.push_back(4);
  182. lt1.push_back(5);
  183. list<int>::iterator it = lt1.begin();
  184. cout << "lt1:";
  185. while (it != lt1.end())
  186. {
  187. cout << (*it)++ << " ";
  188. ++it;
  189. }
  190. cout << endl;
  191. cout << "lt1:";
  192. for (auto e : lt1)
  193. {
  194. cout << e;
  195. }
  196. lt1.insert(lt1.end(), 7);
  197. cout << endl;
  198. cout << "lt1:";
  199. for (auto e : lt1)
  200. {
  201. cout << e;
  202. }
  203. cout << endl;
  204. list<int> lt2 = lt1;
  205. lt1.erase(--lt1.end());
  206. lt1.erase(--lt1.end());
  207. cout << "lt1::";
  208. for (auto e : lt1)
  209. {
  210. cout << e;
  211. }
  212. cout << endl;
  213. cout << "lt2::";
  214. for (auto e : lt2)
  215. {
  216. cout << e;
  217. }
  218. cout << endl;
  219. lt2 = lt1;
  220. cout << "lt1::";
  221. for (auto e : lt1)
  222. {
  223. cout << e;
  224. }
  225. cout << endl;
  226. cout << "lt2::";
  227. for (auto e : lt2)
  228. {
  229. cout << e;
  230. }
  231. cout << endl;
  232. test1_1(lt1);
  233. lt1.push_front(8);
  234. cout << "lt1::";
  235. for (auto e : lt1)
  236. {
  237. cout << e;
  238. }
  239. cout << endl;
  240. lt1.pop_front();
  241. cout << "lt1::";
  242. for (auto e : lt1)
  243. {
  244. cout << e;
  245. }
  246. cout << endl;
  247. lt1.pop_back();
  248. cout << "lt1::";
  249. for (auto e : lt1)
  250. {
  251. cout << e;
  252. }
  253. cout << endl;
  254. lt2.clear();
  255. cout << "lt2::";
  256. for (auto e : lt2)
  257. {
  258. cout << e;
  259. }
  260. cout << endl;
  261. }
  262. }
  263. int main()
  264. {
  265. bit::test3();
  266. return 0;
  267. }

 

构造函数名和类名相同,list是类名,所以不写list<int>,因为这是个类型。 

在类模板内部类型等同于类名,但了解就行,我们尽量不用。


 c++中的cout起到了针对自定义类型输出的作用。


因为这个,所以需要提供默认构造。


  

写两个箭头反而是错的。

 可以这样写


当我们用const迭代器对数据修改时,发现它可以修改。这就出现了问题。

 

 这是因为我们的->运算符重载返回的是T*,我们需要把返回值变为const T*,这样结构体内的数据就不能修改了。

注意T*const,时是指针指向不能修改,而指针指向的结构体中的数据能被修改。

我们再加个模板参数就能解决。

 

 

 从上述过程,我们也看出了typedef的优势。

来看下完整代码。

  1. #include<iostream>
  2. #include<string>
  3. #include<list>
  4. #include<algorithm>
  5. #include<assert.h>
  6. namespace bit
  7. {
  8. using namespace std;
  9. template<class T>
  10. struct list_node
  11. {
  12. list_node* _next;
  13. list_node* _prev;
  14. T _data;
  15. list_node(const T& x)
  16. :_next(nullptr)
  17. , _prev(nullptr)
  18. , _data(x)
  19. {}
  20. };
  21. template<class T, class Ref,class Ptr>
  22. struct list_iterator
  23. {
  24. typedef list_node<T> node;
  25. typedef list_iterator<T, Ref,Ptr> self;
  26. node* _pnode;
  27. list_iterator(node* p)
  28. :_pnode(p)
  29. {}
  30. Ref operator*()
  31. {
  32. return _pnode->_data;
  33. }
  34. self& operator++()
  35. {
  36. _pnode = _pnode->_next;
  37. return *this;
  38. }
  39. self& operator++(int)
  40. {
  41. self tmp(*this);
  42. _pnode = _pnode->_next;
  43. return tmp;
  44. }
  45. self& operator--()
  46. {
  47. _pnode = _pnode->_prev;
  48. return *this;
  49. }
  50. Ptr operator->()
  51. {
  52. return &_pnode->_data;
  53. }
  54. bool operator!=(const self& e)
  55. {
  56. return _pnode != e._pnode;
  57. }
  58. };
  59. template<class T>
  60. class list
  61. {
  62. public:
  63. typedef list_node<T> node;
  64. typedef list_iterator<T, T&,T*> iterator;
  65. typedef list_iterator<T, const T&,const T*> const_iterator;
  66. list()
  67. {
  68. empty_initialize();
  69. }
  70. const_iterator begin()const
  71. {
  72. return const_iterator(_head->_next);
  73. }
  74. const_iterator end()const
  75. {
  76. return const_iterator(_head);
  77. }
  78. iterator begin()
  79. {
  80. return iterator(_head->_next);
  81. }
  82. iterator end()
  83. {
  84. return iterator(_head);
  85. }
  86. iterator insert(iterator pos, const T& n)
  87. {
  88. node* newnode = new node(n);
  89. node* cur = pos._pnode;
  90. node* prev = cur->_prev;
  91. newnode->_next = cur;
  92. newnode->_prev = prev;
  93. prev->_next = newnode;
  94. cur->_prev = newnode;
  95. return iterator(newnode);
  96. }
  97. iterator erase(iterator pos)
  98. {
  99. assert(pos != end());
  100. node* prev = pos._pnode->_prev;
  101. node* next = pos._pnode->_next;
  102. prev->_next = next;
  103. next->_prev = prev;
  104. delete pos._pnode;
  105. pos._pnode;
  106. return iterator(next);
  107. }
  108. /*list<T>& operator=(const list<T>& lt)
  109. {
  110. if (this != &lt)
  111. {
  112. clear();
  113. for (const auto& e : lt)
  114. {
  115. push_back(e);
  116. }
  117. }
  118. return *this;
  119. }*/
  120. void push_front(const T& x)
  121. {
  122. insert(begin(), x);
  123. }
  124. void pop_front()
  125. {
  126. erase(begin());
  127. }
  128. void pop_back()
  129. {
  130. erase(--end());
  131. }
  132. void clear()
  133. {
  134. iterator it = begin();
  135. while (it != end())
  136. {
  137. it = erase(it);
  138. }
  139. }
  140. /*list(const list<T>& lt)
  141. {
  142. _head = new node(T());
  143. _head->_next = _head;
  144. _head->_prev = _head;
  145. for (const auto &e : lt)
  146. {
  147. push_back(e);
  148. }
  149. }*/
  150. template <class inter>
  151. list(inter begin, inter end)
  152. {
  153. _head = new node(T());
  154. _head->_next = _head;
  155. _head->_prev = _head;
  156. while (begin != end)
  157. {
  158. push_back(*begin);
  159. ++begin;
  160. }
  161. }
  162. void swap(list<T>& tmp)
  163. {
  164. std::swap(_head, tmp._head);
  165. }
  166. list(const list<T>& lt)
  167. {
  168. empty_initialize();
  169. list<T> tmp(lt.begin(), lt.end());
  170. swap(tmp);
  171. }
  172. list<T> operator=(list<T> lt)
  173. {
  174. swap(lt);
  175. return *this;
  176. }
  177. void empty_initialize()
  178. {
  179. _head = new node(T());
  180. _head->_next = _head;
  181. _head->_prev = _head;
  182. }
  183. ~list()
  184. {
  185. clear();
  186. delete _head;
  187. _head = NULL;
  188. }
  189. void push_back(const T& e)
  190. {
  191. /*node* NEW = new node(e);
  192. node* tail = _head->_prev;
  193. tail->_next = NEW;
  194. NEW->_next = _head;
  195. _head->_prev = NEW;
  196. NEW->_prev = tail;*/
  197. insert(end(), e);
  198. }
  199. private:
  200. node* _head;
  201. };
  202. struct Pos
  203. {
  204. Pos(int row = 0, int col = 0)
  205. :row(row)
  206. , col(col)
  207. {}
  208. int row;
  209. int col;
  210. };
  211. void test1_2(const list<Pos>& lt1)
  212. {
  213. list<Pos>::const_iterator it1 = lt1.begin();
  214. cout << "lt1::";
  215. while (it1 != lt1.end())
  216. {
  217. cout << it1->row << ":" << (*it1).col;
  218. cout << endl;
  219. ++it1;
  220. }
  221. }
  222. void testpos()
  223. {
  224. list<Pos> p1;
  225. p1.push_back(Pos(1, 1));
  226. p1.push_back(Pos(2, 2));
  227. p1.push_back(Pos(3, 3));
  228. p1.push_back(Pos(4, 4));
  229. list<Pos>::iterator it = p1.begin();
  230. while (it != p1.end())
  231. {
  232. //cout << (*it).row << ":" << (*it).col;
  233. cout << it->row << ":" << (*it).col;
  234. cout << endl;
  235. ++it;
  236. }
  237. test1_2(p1);
  238. }
  239. }
  240. int main()
  241. {
  242. bit::testpos();
  243. return 0;
  244. }

初始化列表如果不显示初始化, 自定义类型会调用默认构造函数,内置类型需要自己给缺省值,或在初始化列表显示初始化,否则会被初始化为随机值。

对于析构而言,编译器对自定义类型调用析构函数,对于内置类型不处理。编译器也不敢处理。

比如list的头指针。因为list它不仅有头结点。只处理头结点会出错。


迭代器我们没有写析构函数,_pnode也不会自动释放。 

另外之前我们自己模拟实现的迭代器的拷贝构造 其实是浅拷贝。

两个容器指向同一个空间。因为_pnode并不释放,所以浅拷贝不会出问题。

不需要显示写析构,就不需要显示写拷贝构造和赋值。

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

闽ICP备14008679号