文章目录
前言
是的,我们又来学一个新容器,list!
它跟前面的相比,难在迭代器的理解
请加油,正文开始!
一、list介绍
- list是可以在常数范围内在任意位置插入和删除的序列式容器,并且该容器可以前后双向迭代
- list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前面一个元素和后一个元素
- list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效
- 与其他的序列式容器相比,list通常在任意位置进行插入,移除元素的执行效率更好
- 与其他序列式容器相比,list和forward_list最大的缺陷式不支持任意位置的随机访问。
其实就是之前学C语言的时候数组和链表的区别
二、list的构造函数
构造函数( (constructor)) | 接口说明 |
---|---|
list (size_type n, const value_type& val =value_type()) | 构造的list中包含n个值为val的元素 |
list() | 构造空的list |
list (const list& x) | 拷贝构造函数 |
list (InputIterator first, InputIterator last) | 用[first, last)区间中的元素构造list |
下面给出具体使用场景:
void TestList1()
{
list<int> l1; // 构造空的l1
list<int> l2(4, 100); // l2中放4个值为100的元素
list<int> l3(l2.begin(), l2.end()); // 用l2的[begin(), end())左闭右开的区间构造l3
list<int> l4(l3); // 用l3拷贝构造l4
// 以数组为迭代器区间构造l5
int array[] = { 16,2,77,29 };
list<int> l5(array, array + sizeof(array) / sizeof(int));
// 列表格式初始化C++11
list<int> l6{ 1,2,3,4,5 };
// 用迭代器方式打印l5中的元素
list<int>::iterator it = l5.begin();
while (it != l5.end())
{
cout << *it << " ";
++it;
}
cout << endl;
// C++11范围for的方式遍历
for (const auto& e : l5)
cout << e << " ";
cout << endl;
}
三、list的迭代器函数
通过begin函数可以得到容器中第一个元素的正向迭代器,通过end函数可以得到容器中最后一个元素的后一个位置的正向迭代器
通过rbegin函数可以得到容器中最后一个元素的反向迭代器,通过rend函数可以得到容器中第一个元素的前一个位置的反向迭代器
以下给出具体使用场景:
void TestList2()
{
int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
list<int> l(array, array + sizeof(array) / sizeof(array[0]));
// 使用正向迭代器正向list中的元素
// list<int>::iterator it = l.begin(); // C++98中语法
auto it = l.begin(); // C++11之后推荐写法
while (it != l.end())
{
cout << *it << " ";
++it;
}
cout << endl;
// 使用反向迭代器逆向打印list中的元素
// list<int>::reverse_iterator rit = l.rbegin();
auto rit = l.rbegin();
while (rit != l.rend())
{
cout << *rit << " ";
++rit;
}
cout << endl;
}
四、list的插入和删除函数
push_front & pop_front
push_front函数用于头插一个数据,pop_front函数用于头删一个数据
push_back & pop_back
push_back函数用于尾插一个数据,pop_back函数用于尾删一个数据
void TestList3()
{
int array[] = { 1, 2, 3 };
list<int> L(array, array + sizeof(array) / sizeof(array[0]));
// 在list的尾部插入4,头部插入0
L.push_back(4);
L.push_front(0);
PrintList(L);
// 删除list尾部节点和头部节点
L.pop_back();
L.pop_front();
PrintList(L);
}
insert
list当中的insert函数支持三种插入方式:
- 在指定迭代器位置插入一个数
- 在指定迭代器位置插入n个值为val的数
- 在指定迭代器位置插入一段迭代器区间(左闭右开)
erase
list当中的erase函数支持两种删除方式:
- 删除指定迭代器位置的元素
- 删除指定迭代器区间(左闭右开)的所有元素
void TestList4()
{
int array1[] = { 1, 2, 3 };
list<int> L(array1, array1 + sizeof(array1) / sizeof(array1[0]));
// 获取链表中第二个节点
auto pos = ++L.begin();
cout << *pos << endl;
// 在pos前插入值为4的元素
L.insert(pos, 4);
PrintList(L);
// 在pos前插入5个值为5的元素
L.insert(pos, 5, 5);
PrintList(L);
// 在pos前插入[v.begin(), v.end)区间中的元素
vector<int> v{ 7, 8, 9 };
L.insert(pos, v.begin(), v.end());
PrintList(L);
// 删除pos位置上的元素
L.erase(pos);
PrintList(L);
// 删除list中[begin, end)区间中的元素,即删除list中的所有元素
L.erase(L.begin(), L.end());
PrintList(L);
}
五、list的元素获取和大小控制
front & back
front函数用于获取list容器当中的第一个元素,back函数用于获取list容器当中的最后一个元素
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> lt;
lt.push_back(0);
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
cout << lt.front() << endl; // 0
cout << lt.back() << endl; // 4
return 0;
}
empty
empty函数用于判断当前容器是否为空
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> lt;
cout << lt.empty() << endl; // 1
return 0;
}
clear
clear函数用于清空容器,清空后容器为空
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> lt(5, 2);
for (const auto& e : lt)
{
cout << e << " ";
}
cout << endl; // 2 2 2 2 2
lt.clear(); // 清空容器
for (const auto& e : lt)
{
cout << e << " ";
}
cout << endl; // (无数据)
return 0;
}
六、list的操作函数
sort
sort函数可以将容器当中的数据默认排为升序,可以接收一个比较函数
#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);
lt.push_back(6);
lt.push_back(7);
for (const auto& e : lt)
{
cout << e << " ";
}
cout << endl; // 1 2 3 4 5 6 7
lt.sort(); // 默认将容器内数据排为升序
for (const auto& e : lt)
{
cout << e << " ";
}
cout << endl; // 1 2 3 4 5 6 7
lt.sort(greater<int>()); // 重新排成降序
for (const auto& e : lt)
{
cout << e << " ";
}
cout << endl; // 7 6 5 4 3 2 1
return 0;
}
splice
splice函数用于两个list容器之间的拼接,其有三种拼接方式:
- 将整个容器拼接到另一个容器的指定迭代器位置
- 将容器当中的某一个数据拼接到另一个容器的指定迭代器位置
- 将容器指定迭代器区间的数据拼接到另一个容器的指定迭代器位置
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> lt1(4, 2);
list<int> lt2(4, 6);
lt1.splice(lt1.begin(), lt2); // 将容器lt2拼接到容器lt1的开头
for (const 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 (const 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 (const auto& e : lt5)
{
cout << e << " ";
}
cout << endl; // 6 6 6 6 2 2 2 2
return 0;
}
请注意, 容器当中被拼接到另一个容器的数据在原容器当中就不存在了。(实际上就是将链表当中的指定结点拼接到了另一个容器当中)!
remove
remove函数用于删除容器当中特定值的元素
#include <iostream>
#include <list>
using namespace std;
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 (const auto& e : lt)
{
cout << e << " ";
}
cout << endl; // 1 4 3 3 2 2 3
lt.remove(3); // 删除容器当中值为3的元素
for (const auto& e : lt)
{
cout << e << " ";
}
cout << endl; // 1 4 2 2
return 0;
}
unique
unique函数用于删除容器当中连续的重复元素
#include <iostream>
#include <list>
using namespace std;
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 (const auto& e : lt)
{
cout << e << " ";
}
cout << endl; // 1 4 3 3 2 2 3
lt.sort(); // 将容器当中的元素排为升序
lt.unique(); // 删除容器当中连续的重复元素
for (const auto& e : lt)
{
cout << e << " ";
}
cout << endl; //1 2 3 4
return 0;
}
请注意,若想使用unique函数做到真正的去重,还需在去重前对容器内元素进行排序!
swap
swap函数用于交换两个容器的内容
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> lt1(4, 2); // 2 2 2 2
list<int> lt2(4, 6); // 6 6 6 6
lt1.swap(lt2); // 交换两个容器的内容
for (const auto& e : lt1)
{
cout << e << " ";
}
cout << endl; // 6 6 6 6
for (const auto& e : lt2)
{
cout << e << " ";
}
cout << endl; // 2 2 2 2
return 0;
}
总结
接下来马上就要迎来list的实现了,请注意,这还是蛮困难的!
可以提前心里准备一下!