【C++】STL——list的使用

简介: 【C++】STL——list的使用

一. list的介绍


list即带头双向循环链表,支持在任意位置O(1)的插入和删除


0a2653c851af460fa595bd959398a8f1.png


二. list的构造函数



2d65d23f6d4748949b924e4057485923.png


方法一:无参构造


list<int> lt1; //构造int类型的空容器


方法二:n个val值构造


list<int> lt2(10,2)//构造10个值为2的int容器


方法三:拷贝构造


list<int> lt3(lt2)//lt2拷贝构造给lt3


方法四:迭代器区间构造


string s ("hello world")
list<int> lt4(s.begin(), s.end());


三. list的插入 & 删除


🎨push_front和pop_front


头插 头删


int main()
{
  list<int> lt;
  lt.push_front(0);
  lt.push_front(1);
  lt.push_front(2);
  for (auto e : lt)
  {
  cout << e << " ";
  }
  cout << endl;
  lt.pop_front();
  for (auto e : lt)
  {
  cout << e << " ";
  }
  cout << endl;
}


0a2653c851af460fa595bd959398a8f1.png


🎨push_back和pop_back


尾插尾删


int main()
{
  list<int> lt;
  lt.push_back(0);
  lt.push_back(1);
  lt.push_back(2);
  for (auto e : lt)
  {
  cout << e << " ";
  }
  cout << endl;
  lt.pop_back();
  lt.pop_back();
  for (auto e : lt)
  {
  cout << e << " ";
  }
  cout << endl;
}


0a2653c851af460fa595bd959398a8f1.png


🎨insert


list当中的insert函数支持三种插入方式:


指定位置插入一个数

指定位置插入n个val值的数

指定迭代器位置插入一段迭代器区间(左闭右开)

int main()
{
  list<int> lt;
  lt.push_back(1);
  lt.push_back(2);
  lt.push_back(3);
  list<int>::iterator pos = find(lt.begin(), lt.end(), 3);
  if(pos != lt.end())
  {
  lt.insert(pos, 20);
  }
  for (auto e : lt)
  {
  cout << endl;
  }
  cout << endl;// 1 2 20 3
  pos = find(lt.begin(), lt.end(), 3);
  if(pos != lt.end())
  {
  lt.insert(pos, 5, 30);//在三的位置插入5个30
  }
  for (auto e : lt)
  {
  cout << endl;
  }
  cout << endl;// 1 2 20 30 30 30 30 30 3
  vector<int> v(2, 7);
  list<int>::iterator pos = find(lt.begin(), lt.end(), 1);
  if(pos != lt.end())
  {
     lt.insert(pos, v.begin(), v.end()); //在1的位置插入2个7
  }
  for (auto e : lt)
  {
  cout << e << " ";
  }
  cout << endl; //7 7 1 2 20 30 30 30 30 30 3
  return 0;
}


💢问:这里的pos会失效吗?

2d65d23f6d4748949b924e4057485923.png

不会,在pos位置前增加一个节点,并改变链接关系,但pos还是指向原本的位置,也不存在野指针


ps:由于find是stl各个容器都需要的,所以实现在了头文件algorithm.h中


🎨erase


4cebaac233b3433da32a72337a77fc60.png

int main()
{
  list<int> lt;
  lt.push_back(1);
  lt.push_back(2);
  lt.push_back(3);
  list<int>::iterator pos = find(lt.begin(), lt.end(), 2);
  if (pos != lt.end())
  {
  lt.erase(pos); //删除2
  }
  for (auto e : lt)
  {
  cout << e << " ";
  }
  cout << endl; //1 3 4 5
  pos = find(lt.begin(), lt.end(), 4);
  if (pos != lt.end())
  {
  lt.erase(pos, lt.end()); //删除4及其之后的元素
  }
  for (auto e : lt)
  {
  cout << e << " ";
  }
  cout << endl; //1 3
  return 0;
}


💢灵魂发问:此处的pos会失效吗?


打印崩溃了,显而易见,删除后,再访问就是野指针了


0a2653c851af460fa595bd959398a8f1.png


四. list的迭代器的使用


🌊正向迭代器


话不多说,上代码


#include <iostream>
#include <list>
using namespace std;
int main()
{
  list<int> lt(10, 2);
  //正向遍历
  list<int>::iterator it = lt.begin();
  while (it != lt.end())
  {
  cout << *it << " ";
  it++;
  }
  cout << endl;
  return 0;
}


🌊反向迭代器


#include <iostream>
#include <list>
using namespace std;
int main()
{
  list<int> lt(10, 5);
  //反向迭代器遍历容器
  list<int>::reverse_iterator rit = lt.rbegin();
  while (rit != lt.rend())
  {
  cout << *rit << " ";
  rit++;
  }
  cout << endl;
  return 0;
}


五. list元素的获取


🌍fornt & back


int main()
{
  list<int> lt;
  lt.push_back(1);
  lt.push_back(2);
  lt.push_back(3);
  cout << lt.front() << endl; 
  cout << lt.back() << endl; 
  return 0;
}


六. list的操作函数


⚡splice 转移


0a2653c851af460fa595bd959398a8f1.png

int main()
{
  list<int> lt1(4, 2);
  list<int> lt2(4, 6);
  lt1.splice(lt1.begin(), lt2); //将容器lt2拼接到容器lt1的开头
  for (auto e : lt1)
  {
  cout << e << " ";
  }
  cout << endl; //6 6 6 6 2 2 2 2 
  list<int> lt3(4, 2);
  list<int> lt4(4, 6);
  lt3.splice(lt3.begin(), lt4, lt4.begin()); //将容器lt4的第一个数据拼接到容器lt3的开头
  for (auto e : lt3)
  {
  cout << e << " ";
  }
  cout << endl; //6 2 2 2 2 
  list<int> lt5(4, 2);
  list<int> lt6(4, 6);
  lt5.splice(lt5.begin(), lt6, lt6.begin(), lt6.end()); //将容器lt6的指定迭代器区间内的数据拼接到容器lt5的开头
  for (auto e : lt5)
  {
  cout << e << " ";
  }
  cout << endl; //6 6 6 6 2 2 2 2
  return 0;
}


ps: 容器当中被拼接到另一个容器的数据在原容器当中就不存在了。(移植技术)


⚡remove


remove函数用于删除容器当中特定元素


int main()
{
  list<int> lt;
  lt.push_back(1);
  lt.push_back(1);
  lt.push_back(1);
  lt.push_back(3);
  lt.push_back(2);
  lt.push_back(2);
  lt.push_back(1);
  lt.push_back(3);
  for (auto e : lt)
  {
  cout << e << " ";
  }
  cout << endl; //1 1 1 3 2 2 1 3
  lt.remove(1); //删除容器当中值为1的元素
  for (auto e : lt)
  {
  cout << e << " ";
  }
  cout << endl; 
  return 0;
}


⚡remove_if


remove_if函数用于删除容器当中满足条件的元素


bool single_digit(const int& val)
{
  return val < 10;
}
int main()
{
  list<int> lt;
  lt.push_back(10);
  lt.push_back(4);
  lt.push_back(7);
  lt.push_back(18);
  lt.push_back(2);
  lt.push_back(5);
  lt.push_back(9);
  for (auto e : lt)
  {
  cout << e << " ";
  }
  cout << endl; //10 4 7 18 2 5 9
  lt.remove_if(single_digit); //删除容器当中值小于10的元素
  for (auto e : lt)
  {
  cout << e << " ";
  }
  cout << endl; //10 18
  return 0;
}


⚡unique


unique函数用于删除容器当中连续的重复元素(前提是先排序)


int main()
{
  list<int> lt;
  lt.push_back(1);
  lt.push_back(4);
  lt.push_back(3);
  lt.push_back(3);
  lt.push_back(2);
  lt.push_back(2);
  lt.push_back(3);
  for (auto e : lt)
  {
  cout << e << " ";
  }
  cout << endl; //1 4 3 3 2 2 3
  lt.sort(); //升序
  lt.unique(); 
  for (auto e : lt)
  {
  cout << e << " ";
  }
  cout << endl;
  return 0;
}


⚡sort


算法库里面已经有一个sort 了,为什么list里也实现了一个sort?


list的空间不是连续的,算法库中的sort要求物理空间要是连续的,所以vector和string才用,快排实现的参数取中,在链表实现不了


链表的sort是归并排序实现的


int main()
{
  list<int> lt;
  lt.push_back(4);
  lt.push_back(7);
  lt.push_back(5);
  lt.push_back(9);
  lt.push_back(6);
  lt.push_back(0);
  lt.push_back(3);
  for (auto e : lt)
  {
  cout << e << " ";
  }
  cout << endl; //4 7 5 9 6 0 3
  lt.sort(); //默认升序
  for (auto e : lt)
  {
  cout << e << " ";
  }
  cout << endl; //0 3 4 5 6 7 9
  return 0;
}
相关文章
|
5天前
|
编译器 C语言 C++
【c++丨STL】list模拟实现(附源码)
本文介绍了如何模拟实现C++中的`list`容器。`list`底层采用双向带头循环链表结构,相较于`vector`和`string`更为复杂。文章首先回顾了`list`的基本结构和常用接口,然后详细讲解了节点、迭代器及容器的实现过程。 最终,通过这些步骤,我们成功模拟实现了`list`容器的功能。文章最后提供了完整的代码实现,并简要总结了实现过程中的关键点。 如果你对双向链表或`list`的底层实现感兴趣,建议先掌握相关基础知识后再阅读本文,以便更好地理解内容。
14 1
|
17天前
|
算法 C语言 C++
【c++丨STL】list的使用
本文介绍了STL容器`list`的使用方法及其主要功能。`list`是一种双向链表结构,适用于频繁的插入和删除操作。文章详细讲解了`list`的构造函数、析构函数、赋值重载、迭代器、容量接口、元素访问接口、增删查改操作以及一些特有的操作接口如`splice`、`remove_if`、`unique`、`merge`、`sort`和`reverse`。通过示例代码,读者可以更好地理解如何使用这些接口。最后,作者总结了`list`的特点和适用场景,并预告了后续关于`list`模拟实现的文章。
33 7
|
26天前
|
存储 编译器 C++
C++ initializer_list&&类型推导
在 C++ 中,`initializer_list` 提供了一种方便的方式来初始化容器和传递参数,而右值引用则是实现高效资源管理和移动语义的关键特性。尽管在实际应用中 `initializer_list&&` 并不常见,但理解其类型推导和使用方式有助于深入掌握现代 C++ 的高级特性。
17 4
|
2月前
|
存储 编译器 C语言
【c++丨STL】vector的使用
本文介绍了C++ STL中的`vector`容器,包括其基本概念、主要接口及其使用方法。`vector`是一种动态数组,能够根据需要自动调整大小,提供了丰富的操作接口,如增删查改等。文章详细解释了`vector`的构造函数、赋值运算符、容量接口、迭代器接口、元素访问接口以及一些常用的增删操作函数。最后,还展示了如何使用`vector`创建字符串数组,体现了`vector`在实际编程中的灵活性和实用性。
64 4
|
2月前
|
C语言 C++ 容器
【c++丨STL】string模拟实现(附源码)
本文详细介绍了如何模拟实现C++ STL中的`string`类,包括其构造函数、拷贝构造、赋值重载、析构函数等基本功能,以及字符串的插入、删除、查找、比较等操作。文章还展示了如何实现输入输出流操作符,使自定义的`string`类能够方便地与`cin`和`cout`配合使用。通过这些实现,读者不仅能加深对`string`类的理解,还能提升对C++编程技巧的掌握。
77 5
|
2月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
60 2
|
2月前
|
存储 算法 Linux
【c++】STL简介
本文介绍了C++标准模板库(STL)的基本概念、组成部分及学习方法,强调了STL在提高编程效率和代码复用性方面的重要性。文章详细解析了STL的六大组件:容器、算法、迭代器、仿函数、配接器和空间配置器,并提出了学习STL的三个层次,旨在帮助读者深入理解和掌握STL。
55 0
|
20天前
|
存储 编译器 C语言
【c++丨STL】vector模拟实现
本文深入探讨了 `vector` 的底层实现原理,并尝试模拟实现其结构及常用接口。首先介绍了 `vector` 的底层是动态顺序表,使用三个迭代器(指针)来维护数组,分别为 `start`、`finish` 和 `end_of_storage`。接着详细讲解了如何实现 `vector` 的各种构造函数、析构函数、容量接口、迭代器接口、插入和删除操作等。最后提供了完整的模拟实现代码,帮助读者更好地理解和掌握 `vector` 的实现细节。
30 0
|
7月前
|
安全 Java
java线程之List集合并发安全问题及解决方案
java线程之List集合并发安全问题及解决方案
1064 1
|
6月前
|
Java API Apache
怎么在在 Java 中对List进行分区
本文介绍了如何将列表拆分为给定大小的子列表。尽管标准Java集合API未直接支持此功能,但Guava和Apache Commons Collections提供了相关API。