✨ Blog's 主页: 白乐天_ξ( ✿>◡❛)
🌈 个人Motto:他强任他强,清风拂山冈!
🔥 所属专栏:C++深入学习笔记
💫 欢迎来到我的学习笔记!
一、list的介绍
文档内容以及大致翻译如下图:
list
是一种允许在序列内的任何位置进行常数时间的插入删除的序列式容器,并且该容器可以前后双向的迭代;list
的底层是一个带头双向循环链表,双向链表中每个元素存储在不相关的独立结点中,在结点中通过指针指向前一个位置和后一个位置;list
和forward_list
非常相似,最主要的区别在于:forward_list
是单链表,只能进行单方向的迭代;- 与其他容器相比,
list
通常在任意位置进行插入、删除元素的执行效率更高;list
和forward_list
最大的缺点就是:不支持在任意位置的随机访问;其次list
还需要一些额外的空间来保存每个结点之间的关联信息(对于存储的类型较小的元素的链表来说这可能是一个很重要的因素)。
二、list的使用
2.1.list的定义方式
cpp
#include <list>
#include <string>
using namespace std;
int main()
{
// 1. 构造某个类型的空容器
list<int> lt1;// 构造int类型的空容器
// 2. 构造一个含有val的某类型容器
list<int> lt2(10, 2);// 构造含有10个2的int类型容器
// 3. 构造某类型容器的复制品
list<int> lt3(lt2);// 拷贝哦构造int类型的lt2容器的复制品
// 4. 使用迭代器构造某一段内容
string s("hello world");
list<char> lt4(s.begin(), s.end());// 构造string对象某段区间的复制品
// 5. 构造数组某段区间的复制品
int arr[] = { 1,2,3,4 };
int sz = sizeof(arr) / sizeof(int);
list<int> lt5(arr, arr + sz);// 构造数组某段区间的复制品
return 0;
}
2.2.list的插入删除
2.2.1.push_front(头插)和pop_front(头删)
push_front
函数用于头插一个数据,pop_front
函数用于头删一个数据。
cpp
#include <list>
#include <string>
#include <iostream>
using namespace std;
int main()
{
list<int> lt;
lt.push_front(0);
lt.push_front(1);
lt.push_front(2);
lt.push_front(3);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.pop_front();
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.pop_front();
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
return 0;
}
2.2.2.push_back(尾插)和pop_back(尾删)
push_back
函数用于尾插一个数据,pop_back
用于尾删一个数据。
cpp
#include <list>
#include <iostream>
using namespace std;
int main()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
cout << endl;
lt.pop_back();
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
return 0;
}
2.2.3.insert
insert的使用形式:
cpp
// 在指定迭代器位置插入一个数
iterator insert (iterator position, const value_type& val);
// 在指定迭代器位置插入n个值为val的数
void insert (iterator position, size_type n, const value_type& val);
// 在指定迭代器位置插入一段迭代器区间(左闭右开)
template <class InputIterator>
void insert (iterator position, InputIterator first, InputIterator last);
代码举例:
cpp
#include <iostream>
#include <algorithm>
#include <list>
#include <vector>
using namespace std;
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);// 找到数据2的位置,下标赋值给pos
lt.insert(pos, 10);// 在2的位置插入10
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
// 用法二:再制定迭代器位置插入n个值为val的数据
pos = find(lt.begin(), lt.end(), 3);// 找到数据的位置,下标赋值给pos
lt.insert(pos, 3, 9);// 在pos的位置插入3个9
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
// 用法三:在指定迭代器位置插入一段迭代器区间(左闭右开)
vector<int> v(2, 7);//
pos = find(lt.begin(), lt.end(), 1);// 找到1的位置,下标赋值给pos
lt.insert(pos, v.begin(), v.end());// 在1的位置插入一个迭代器区间的数据(2个7)
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
return 0;
}
补充:find
函数的头文件algorithm
,该函返回一个迭代器,指向<font style="color:rgb(42, 43, 46);">[first,last)</font>
范围内第一个与val相等的元素。如果没有找到这样的元素,则返回<font style="color:rgb(42, 43, 46);">last</font>
。
cpp
template <class InputIterator, class T>
InputIterator find (InputIterator first, InputIterator last, const T& val);
// val就是要查找的值
2.2.4.erase
erase的使用形式:
cpp
// 删除指定迭代器位置的元素
iterator erase (iterator position);
// 删除指定迭代器区间(左闭右开)的所有元素
iterator erase (iterator first, iterator last);
代码示例:
cpp
#include <iostream>
#include <algorithm>
#include <list>
#include <vector>
using namespace std;
int main()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;// 1 2 3 4 5
// erase的第一个用法:
list<int>::iterator pos = find(lt.begin(), lt.end(), 2);// 在迭代器区间查找2
lt.erase(pos);//删除pos位置的值------2
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;// 1 3 4 5
// erase的第二个用法:
pos = find(lt.begin(), lt.end(), 4);
lt.erase(pos, lt.end());// 删除pos之后的所有数据------删除4、5
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;// 1 3
return 0;
}
2.3.list的迭代器使用
2.3.1.begin和end
正向迭代器遍历容器
cpp
#include <vector>
#include <list>
#include <iostream>
using namespace std;
int main()
{
list<int> lt;// 定义初始化为10个2的lt链表
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
list<int>::iterator it = lt.begin();
while (it != lt.end())
{
cout << *it << " ";
++it;
}
cout << endl;
return 0;
}
2.3.2.rbegin和rend
反向迭代器遍历容器
cpp
#include <vector>
#include <list>
#include <iostream>
using namespace std;
int main()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
list<int>::reverse_iterator rit = lt.rbegin();
while (rit != lt.rend())
{
cout << *rit << " ";
rit++;
}
cout << endl;
return 0;
}
2.4.list元素的获取访问
2.4.1.front和back
front函数返回对链表第一个元素的引用(用于获取list容器当中的第一个元素),在空容器上调用此函数会有未定义的行为。
back函数用于获取list容器当中的最后一个元素。
cpp
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
lt.push_back(6);
cout << lt.front() << endl;// 1
cout << lt.back() << endl; // 6
return 0;
}
2.5.list的大小控制
2.5.1.size
size用于获取当前容器中的元素个数。
cpp
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> lt(6, 1);
cout << lt.size() << endl;
return 0;
}
2.5.2.resize
resize
的作用:调整容器的大小,使其包含n个元素(n是新容器的大小)。用法如下:
cpp
void resize (size_type n, value_type val = value_type());
- 如果n小于当前容器的大小(size),则内容将被减少到前n个元素,并删除超出的元素(并销毁它们)。
- 如果n大于当前容器的大小,则通过在末尾插入所需的元素来扩展内容,以达到n的大小。如果指定了val,则将新元素初始化为val的副本,否则将其进行值初始化。
- resize函数会通过插入或删除元素来改变容器的实际内容。
- val值是一个缺省值,如果未指定,就会使用默认构造函数。
- 成员类型
<font style="color:rgb(42, 43, 46);">value_type</font>
是容器中元素的类型,在<font style="color:rgb(42, 43, 46);">list</font>
中定义为第一个模板形参(T)的别名。
cpp
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> lt(6, 1);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.resize(8, 2);// 传了值的情况
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.resize(2);// 没传值,缩容size到3=2
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
return 0;
}
2.5.3.empty
empty
的作用:判断容器是否为空(即容器的size是否为0)。用法如下:
cpp
bool empty() const;
代码示例:
cpp
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> lt1(10, 2);
cout << lt1.empty() << endl;
list<int> lt2;
cout << lt2.empty() << endl;
return 0;
}
2.5.4.clear
clear
函数用于判断当前容器是否为空。用法如下:
cpp
void clear();
代码示例:
cpp
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> lt(5, 1);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.clear();
cout << lt.empty() << endl;
return 0;
}
2.6.list的操作函数
2.6.1.sort
sort
函数的作用:对list
中的元素进行排序, sort可以将容器中的数据默认排位升序。用法如下:
cpp
void sort();
template <class Compare>
void sort(Compare comp);
代码示例:
cpp
#include <list>
#include <iostream>
using namespace std;
int main()
{
list<int> lt;
lt.push_back(3);
lt.push_back(1);
lt.push_back(0);
lt.push_back(9);
lt.push_back(4);
lt.push_back(3);
lt.push_back(2);
lt.push_back(6);
lt.push_back(3);
lt.push_back(7);
lt.push_back(5);
lt.push_back(10);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.sort();
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
//3 1 0 9 4 3 2 6 3 7 5 10
//0 1 2 3 3 3 4 5 6 7 9 10
return 0;
}
2.6.2.splice
splice的作用(文档翻译过来):将元素从 "x" 转移到容器当中,并将这些元素插入到特定位置。这样做能够有效地把这些元素插入到容器里,同时从 "x" 中移除它们,进而改变两个容器的大小。此操作既不涉及任何元素的构造,也不涉及任何元素的破坏。无论 "x" 是左值还是右值,也无论元素的值类型是否支持移动构造,这些元素都会被转移。
用法如下:
splice
函数用于两个list容器之间的拼接,其有三种拼接方式:
- 将整个容器拼接到另一个容器的指定迭代器位置。
- 将容器当中的某一个数据拼接到另一个容器的指定迭代器位置。
- 将容器指定迭代器区间的数据拼接到另一个容器的指定迭代器位置。
cpp
// 将x的所有元素传输到容器中
void splice(iterator position, list& x);
// 只将x指向i的元素传输到容器中
void splice(iterator position, list& x, iterator i);
// 将范围[first,last]从x传输到容器中
void splice(iterator position, list& x, iterator first, iterator last);
// 其中x代表list链表
// position代表容器中插入x的元素的位置
// 指定x中的元素范围的迭代器。将[first,last)范围内的元素转移到position。注意:包括first指向的元素,但是不包括last指向的元素。
代码示例:
cpp
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> lt1(4, 1);
list<int> lt2(4, 2);
lt1.splice(lt1.begin(), lt2);// 在开始位置插入整个list容器
for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;
// 此时lt2已经变成空的list容器了
for (auto e : lt2)
{
cout << e << " ";
}
cout << endl;
list<int> lt3(4, 3);
list<int> lt4(4, 4);
lt3.splice(lt3.begin(), lt4, lt4.begin());// 将lt4的第一个位置的数据转移到lt3链表的靠头
for (auto e : lt3)
{
cout << e << " ";
}
cout << endl;
for (auto e : lt4)
{
cout << e << " ";
}
cout << endl;
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;
}
2.6.3.remove
从容器中移除值为<font style="color:rgb(42, 43, 46);">val</font>
的所有元素。调用这些对象的析构函数,并按移除的元素数量减少容器大小。
remove函数用于删除容器当中特定值的元素。用法如下:
cpp
void remove (const value_type& val);
代码示例:
cpp
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> lt;
lt.push_back(2);
lt.push_back(1);
lt.push_back(6);
lt.push_back(2);
lt.push_back(0);
lt.push_back(5);
lt.push_back(4);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.remove(2);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
return 0;
}
2.6.4.remove_if
<font style="color:rgb(42, 43, 46);">remove_if</font>
允许使用除相等比较之外的其他条件来确定元素是否被删除。(满足条件就可以使用删除:但是不能是比较像等条件)这将调用这些对象的析构函数,并根据删除的元素数量减少容器大小。用法如下:
cpp
template <class Predicate>
void remove_if (Predicate pred);
代码示例:
cpp
#include <iostream>
#include <list>
using namespace std;
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;
}
2.6.5.unique
unique
用于删除容器中连续的重复元素。注意:要想做到真正的驱虫,就需要在对去重前对容器内的元素进行排序。用法如下:
cpp
void unique();
template <class BinaryPredicate>
void unique (BinaryPredicate binary_pred);
代码示例:
cpp
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> lt;
lt.push_back(4);
lt.push_back(5);
lt.push_back(2);
lt.push_back(9);
lt.push_back(1);
lt.push_back(3);
lt.push_back(0);
lt.push_back(6);
lt.push_back(4);
lt.push_back(1);
lt.push_back(0);
lt.push_back(7);
lt.push_back(8);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.sort();// 先升序排序一下
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.unique();// 删除掉连续的重复元素
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;// 0 1 2 3 4 5 6 7 8 9
return 0;
}
2.6.6.merge
merge
函数的作用:将一个有序的list容器合并到另一个有序的list容器当中,使得合并后的list容器仍然有序。(类似于归并排序)
- 把列表中的各个有序位置上的所有元素转移到一个容器中。这里有一个前提条件,两个容器(原列表和目标容器)都必须已经是有序状态。
- 这个操作会有效地使列表 x 变为空,也就是删除了 x 中的所有元素,同时将这些元素插入到目标容器中的有序位置。目标容器的大小会根据传输过来的元素数量进行扩展。
- 该操作在执行过程中,不会构造或销毁任何元素。无论 x 是左值还是右值,也无论元素的类型(value_type)是否支持移动构造,元素都会被转移。
用法如下:
cpp
void merge(list& x);
template <class Compare>
void merge(list& x, Compare comp);
代码示例:
cpp
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> lt1;
lt1.push_back(3);
lt1.push_back(1);
lt1.push_back(8);
lt1.push_back(9);
lt1.push_back(6);
list<int> lt2;
lt2.push_back(2);
lt2.push_back(10);
lt2.push_back(7);
lt2.push_back(4);
lt1.push_back(5);
lt1.sort();
lt2.sort();
lt1.merge(lt2);
for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;
return 0;
}
2.6.7.reverse
reverse函数用于将容器中的元素进行逆置,即反转列表容器中元素的顺序。。用法如下:
cpp
void reverse();
代码如下:
cpp
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> lt1;
lt1.push_back(1);
lt1.push_back(3);
lt1.push_back(2);
lt1.push_back(4);
lt1.push_back(7);
for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;
lt1.reverse();
for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;
return 0;
}
2.6.8.assign
assign
函数的作用:用于将新内容分配给容器,替换其当前内容,新内容的赋予方式有两种:
- n个值为val的数据分配给容器;
- 将所给的迭代器区间当中的内容分配给容器。
其他:
- 为被分配的元素所需的任何存储空间,都会使用内部分配器来进行分配。
- 在进行调用之前,容器中原本保存的任何元素都将被销毁,然后被新构造的元素所替换(这里不是进行元素赋值操作)。
cpp
template <class InputIterator>
void assign(InputIterator first, InputIterator last);
void assign(size_type n, const value_type& val);
代码示例:
cpp
#include <iostream>
#include <string>
#include <list>
using namespace std;
int main()
{
list<char> lt(3, 'a');
lt.assign(3, 'b'); //将新内容分配给容器,替换其当前内容
for (auto e : lt)
{
cout << e << " ";
}
cout << endl; //b b b
string s("hello world");
lt.assign(s.begin(), s.end()); //将新内容分配给容器,替换其当前内容
for (auto e : lt)
{
cout << e << " ";
}
cout << endl; //h e l l o w o r l d
return 0;
}
2.6.9.swap
swap
函数用于交换两个容器的内容。