c++之list容器
1.前言
我们这里的List(链表)是带头双向循环链表,这一点在下一篇文章的对list的模拟实现是至关重要的,本篇文章主要讲解的是c++自带的list的相关接口!
2.接口及其应用
2.1 遍历数据(迭代器和范围for)
遍历list的方法和以往我们遍历vector,string的方式类似,一种是用迭代器,另外一种则是利用范围for,二者本质是一样的
相关代码如下:
void list_test1()
{
list<int> lt1;
lt1.push_back(1);
lt1.push_back(2);
lt1.push_back(3);
lt1.push_back(4);
lt1.push_back(5);
lt1.push_back(6);
list<int> lt2 = { 6,5,4,3,2,1 };
//两种遍历的方式
//法一)直接用迭代器,相当于指针
list<int>::iterator it1 = lt1.begin();
while (it1 != lt1.end())
{
cout << *it1 << " ";
++it1;
}
cout << endl;
//法二)用范围for,但是本质还是迭代器,能用迭代器就能用范围for
for (const auto e : lt2)
{
cout << e << " ";
}
cout << endl;
}
2.2 替换数据(assign函数)

简单来说,就是assign我们要传两个参数,lt.assign(x,y),结果就是输出x个y!
代码如下:
void list_test2() //验证assign的作用
{
list<int> lt1 = { 1,2,3,4,5,6,7,8,9 };
list<int>::iterator it = lt1.begin();
while (it != lt1.end())
{
cout << *it << " ";
it++;
}
cout << endl;
lt1.assign(3, 2);
for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;
lt1.assign(10, 1);
for (auto e : lt1)
{
cout << e << " ";
}
}
运行结果:

2.3 插入数据(push_back\emplace_back)
插入数据(以尾插为例)可以用push_back函数以及emplace_back函数

下面谈一下二者的异同点:
同:1.两者都用于在容器的末尾添加元素,扩展容器的大小。
2.它们都可以用于支持动态扩展的容器,如 std::vector、std::list、std::deque 等。
异:1.push_back接受一个已经构造好的对象作为参数,并将其复制或移动到中。需要先构造一个 对象,然后将其复制或移动到容器中。如果对象的构造和复制/移动操作代价较高,性能受 到影响。
2.emplace_back接受构造参数,并在容器的末尾原地构造对象,避免了额外的复制或移动操 作。 直接在容器分配的内存中构造对象,减少了不必要的复制或移动操作,通常性能更高。
代码如下:
struct A
{
A(int a1 = 1, int a2 = 2,int a3=3)
:_a1(a1)
, _a2(a2)
,_a3(a3)
{}
int _a1;
int _a2;
int _a3;
};
ostream& operator<<(ostream& out,A& lt) //你的范围for每个元素的类型是A, 所以要换成A&, 然后函数里面打印的时候,需要指定A里面的成员变量。
{
out << lt._a1<<lt._a2<<lt._a3;
return out;
}
void list_test3() //插入数据的方法
{
//法一)直接利用库函数
list<int> lt;
// 只能插入int
lt.push_back(1);
lt.push_back(2);
// 插入int
lt.emplace_back(3);
lt.emplace_back(4);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
//其余插入数据的方法:利用构造
list<A> lt2;
A aa1(2, 2, 1);
lt2.push_back(aa1);
for (auto e1 : lt2)
{
cout << e1 << " ";
}
cout << endl;
lt2.push_back(A(2, 6, 5));
lt2.push_back({ 2,9,1 });
lt2.emplace_back(aa1);
lt2.emplace_back(A(2, 2, 5));
//更高效
lt2.emplace_back(2, 2, 1);
for (auto e1 : lt2)
{
cout << e1 << " ";
}
cout << endl;
}
运行结果:

2.4 删除数据(remove函数)

简单来说,就是移除指定数字,即lt.remove(x),就是移除lt中所有值为x的数
代码如下:
void list_test4() //remove
{
list<int> lt = { 1,2,3,5,5,5,7,8,5 };
list<int>::iterator it = lt.begin();
while (it != lt.end())
{
cout << *it << " "; //1 2 3 5 5 5 7 8 5
++it;
}
cout << endl;
lt.remove(100); //没有要移动的数字就不动了
for (auto ch : lt)
{
cout << ch << " "; //1 2 3 5 5 5 7 8 5
}
cout << endl;
lt.remove(5); //删除所有的5
for (auto e : lt)
{
cout << e << " "; //1 2 3 7 8
}
cout << endl;
lt.remove(3);
for (auto e1 : lt)
{
cout << e1 << " "; //1 2 7 8
}
}
2.5 对数据的排序
说明:sort()函数是默认排升序的,如果想排降序,则需要使用greater
代码如下:
void list_test5() //排序
{
list<int> lt = { 10,6,2,8,3,1,5,95 };
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
// 不支持
// sort(lt1.begin(), lt1.end())
lt.sort(); //默认排升序
for (auto e1 : lt)
{
cout << e1 << " ";
}
cout << endl;
//排降序
//法一)
//greater<int> gt;
//lt.sort(gt);
//法二)对上述进行简化
lt.sort(greater<int>());
for (auto e1 : lt)
{
cout << e1 << " ";
}
cout << endl;
}
运行结果:

2.5 数据去重(unique函数)

有一点值得注意的是,其与remove(x)函数不同,remove函数是移除所有值为x的数,没有什么要求,但是unique函数,不会将所有值为x的数都移除走,实现的功能是去重,必须是当有n个x相连时,才可以把后面n-1个x移除掉,而且必须是在有序的情况下,其工作原理可以类比数据结构的双指针法
代码如下:
void list_test6() //unique,必须要在有序的情况下使用,升降序都行
{
list<int> lt = { 10,2,5,3,2,6,2,2,1,3 };
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.sort(greater<int>());
for (auto e2 : lt)
{
cout << e2 << " ";
}
cout << endl;
lt.unique();
for (auto e3 : lt)
{
cout << e3 << " ";
}
cout << endl;
}
运行结果:

2.6 数据移动(splice函数)

说明:splice函数有以下三种用法:
用法一)将一个列表的所有元素移动到另一个列表的指定位置

解释:将 other 中的所有元素移动到当前列表的 pos 指向的位置之前
示例:

用法二)将一个列表中的单个元素移动到另一个列表的指定位置
解释:将 other 中由 it 指向的单个元素移动到当前列表的 pos 指向的位置之前
示例:
用法三)将一个列表中指定范围的元素移动到另一个列表的指定位置

解释:将 other 中从 first 到 last(不包括 last)范围内的元素移动到当前列表的 pos 指向的位置之前
示例:
代码如下(补充)
void list_test7()
{
list<int> lt = { 1,3,5,7,9 };
list<int> lt2 = { 2,4,6,8,10 };
cout << "lt:";
for (auto e : lt)
{
cout << e << " "; //lt:1 3 5 7 9
}
cout << endl;
cout << "lt2:";
for (auto e : lt2)
{
cout << e << " "; //lt:2 4 6 8 10
}
cout << endl;
// 1移动到尾部
lt.splice(lt.end(), lt, lt.begin());
for (auto e3 : lt)
{
cout << e3 << " "; //3 5 7 9 1
}
cout << endl;
//将 lt2 中的所有元素移动到当前列表的 lt 指向的位置之前
lt.splice(lt.begin(), lt2);
for (auto e3 : lt)
{
cout << e3 << " "; //2 4 6 8 10 3 5 7 9 1
}
cout << endl;
}
最后,附上总的代码
#include<iostream>
#include<list>
using namespace std;
void list_test1()
{
list<int> lt1;
lt1.push_back(1);
lt1.push_back(2);
lt1.push_back(3);
lt1.push_back(4);
lt1.push_back(5);
lt1.push_back(6);
list<int> lt2 = { 6,5,4,3,2,1 };
//两种遍历的方式
//法一)直接用迭代器,相当于指针
list<int>::iterator it1 = lt1.begin();
while (it1 != lt1.end())
{
cout << *it1 << " ";
++it1;
}
cout << endl;
//法二)用范围for,但是本质还是迭代器,能用迭代器就能用范围for
for (const auto e : lt2)
{
cout << e << " ";
}
cout << endl;
}
void list_test2() //验证assign的作用
{
list<int> lt1 = { 1,2,3,4,5,6,7,8,9 };
list<int>::iterator it = lt1.begin();
while (it != lt1.end())
{
cout << *it << " ";
it++;
}
cout << endl;
lt1.assign(3, 2);
for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;
lt1.assign(10, 1);
for (auto e : lt1)
{
cout << e << " ";
}
}
struct A
{
A(int a1 = 1, int a2 = 2,int a3=3)
:_a1(a1)
, _a2(a2)
,_a3(a3)
{}
int _a1;
int _a2;
int _a3;
};
ostream& operator<<(ostream& out,A& lt) //你的范围for每个元素的类型是A, 所以要换成A&, 然后函数里面打印的时候,需要指定A里面的成员变量。
{
out << lt._a1<<lt._a2<<lt._a3;
return out;
}
void list_test3() //插入数据的方法
{
//法一)直接利用库函数
list<int> lt;
// 只能插入int
lt.push_back(1);
lt.push_back(2);
// 插入int
lt.emplace_back(3);
lt.emplace_back(4);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
//其余插入数据的方法:利用构造
list<A> lt2;
A aa1(2, 2, 1);
lt2.push_back(aa1);
for (auto e1 : lt2)
{
cout << e1 << " ";
}
cout << endl;
lt2.push_back(A(2, 6, 5));
lt2.push_back({ 2,9,1 });
lt2.emplace_back(aa1);
lt2.emplace_back(A(2, 2, 5));
//更高效
lt2.emplace_back(2, 2, 1);
for (auto e1 : lt2)
{
cout << e1 << " ";
}
cout << endl;
}
void list_test4() //remove
{
list<int> lt = { 1,2,3,5,5,5,7,8,5 };
list<int>::iterator it = lt.begin();
while (it != lt.end())
{
cout << *it << " "; //1 2 3 5 5 5 7 8 5
++it;
}
cout << endl;
lt.remove(100); //没有要移动的数字就不动了
for (auto ch : lt)
{
cout << ch << " "; //1 2 3 5 5 5 7 8 5
}
cout << endl;
lt.remove(5); //删除所有的5
for (auto e : lt)
{
cout << e << " "; //1 2 3 7 8
}
cout << endl;
lt.remove(3);
for (auto e1 : lt)
{
cout << e1 << " "; //1 2 7 8
}
}
void list_test5() //排序
{
list<int> lt = { 10,6,2,8,3,1,5,95 };
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
// 不支持
// sort(lt1.begin(), lt1.end())
lt.sort(); //默认排升序
for (auto e1 : lt)
{
cout << e1 << " ";
}
cout << endl;
//排降序
//法一)
//greater<int> gt;
//lt.sort(gt);
//法二)对上述进行简化
lt.sort(greater<int>());
for (auto e1 : lt)
{
cout << e1 << " ";
}
cout << endl;
}
void list_test6() //unique,必须要在有序的情况下使用,升降序都行
{
list<int> lt = { 10,2,5,3,2,6,2,2,1,3 };
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.sort(greater<int>());
for (auto e2 : lt)
{
cout << e2 << " ";
}
cout << endl;
lt.unique();
for (auto e3 : lt)
{
cout << e3 << " ";
}
cout << endl;
}
void list_test7()
{
list<int> lt = { 1,3,5,7,9 };
list<int> lt2 = { 2,4,6,8,10 };
cout << "lt:";
for (auto e : lt)
{
cout << e << " "; //lt:1 3 5 7 9
}
cout << endl;
cout << "lt2:";
for (auto e : lt2)
{
cout << e << " "; //lt:2 4 6 8 10
}
cout << endl;
// 1移动到尾部
lt.splice(lt.end(), lt, lt.begin());
for (auto e3 : lt)
{
cout << e3 << " "; //3 5 7 9 1
}
cout << endl;
//将 lt2 中的所有元素移动到当前列表的 lt 指向的位置之前
lt.splice(lt.begin(), lt2);
for (auto e3 : lt)
{
cout << e3 << " "; //2 4 6 8 10 3 5 7 9 1
}
cout << endl;
}
int main()
{
//list_test1();
//list_test2();
//list_test3();
//list_test4();
//list_test5();
//list_test6();
list_test7();
return 0;
}
本文到此结束,如有不恰当指出,欢迎指正!下一篇我们将讲解如何自己实现list类!
更多推荐
所有评论(0)