一看就懂!保姆级实例详解 STL list 容器【万字整理】
🎈 作者:Linux猿
🎈 简介:CSDN博客专家🏆,华为云享专家🏆,Linux、C/C++、面试、刷题、算法尽管咨询我,关注我,有问题私聊!
list 是 STL 最长使用的容器之一,是在日常使用以及面试中经常遇到的知识点,下面来详细讲解下 list。
一、什么是 list ?
list 是一个双向链表封装起来的容器,可以实现 O(1) 的时间复杂度插入和删除元素,但是,和链表一样,不能通过下标(例如:g[i])快速访问元素,只能通过遍历获取。
二、List 的定义
2.1 头文件
使用 List 需要引入头文件:
#include <list>
2.2 定义
list<T>变量名称;
其中,T 是数据类型,例如:int、char、String、类等。
下面来看一下定义的例子:
#include <iostream>
#include <list> // list 头文件
using namespace std; // 命名空间,如果没有,则使用 std::list
int main() {
list<int> t1; // 定义一个名为 t1 的 int 类型的 list
cout<<"t1 size = "<<t1.size()<<endl; // size = 0
list<int> t2(5); // 定义一个长度为 5,初始值都为 0 的 int 类型的 list
cout<<"t2 size = "<<t2.size()<<endl; // size = 5
list<int> t3(5, 10); // 定义一个长度为 5,初始值都为 10 的 int 类型的 list
cout<<"t3 size = "<<t3.size()<<endl; // size = 5
int idx = 0;
list<int>::iterator iter;
for(iter = t3.begin(); iter != t3.end(); ++iter) {
cout<<*iter<<" "; // *iter = 10
}
cout<<endl;
// 输出为:10 10 10 10 10
list<int> t4(t3); // 使用 t3 初始化 t4,需要是一样的类型
list<int> t5(t3.begin(), t3.end()); // 使用 t3 初始化 t5
}
输出为:
linuxy@linuxy:~/defineDir$ g++ -o main main.cpp
linuxy@linuxy:~/defineDir$ ./main
t1 size = 0
t2 size = 5
t3 size = 5
10 10 10 10 10
linuxy@linuxy:~/defineDir$
还可以使用花括号的形式初始化:
#include <iostream>
#include <list> // list 头文件
using namespace std; // 命名空间,如果没有,则使用 std::list
int main() {
list<int> t1{1,2,3,4,5,6}; // 定义一个名为 t1 的 int 类型的 list
list<int>::iterator iter;
for(iter = t1.begin(); iter != t1.end(); ++iter) {
cout<<*iter<<" ";
}
cout<<endl;
}
输出为:
linuxy@linuxy:~/defineDir$ g++ -o main main.cpp
linuxy@linuxy:~/defineDir$ ./main
1 2 3 4 5 6
linuxy@linuxy:~/defineDir$
2.3 常用方法
size() : 返回链表中的元素个数;
clear(): 清空所有元素;
empty(): 判断是否为空;
push_front(): 在链表开头添加元素;
push_back(): 在链表结尾添加元素;
pop_front(): 删除链表开头的元素;
pop_back(): 删除链表结尾元素;
begin(): 返回指向链表第一个元素的迭代器;
end(): 返回指向链表最后一个元素的迭代器;
rbegin(): 返回指向链表最后一个元素的迭代器;(用于逆向遍历元素)
rend(): 返回指向链表第一个元素的迭代器;(用于逆向遍历元素)
insert(position, val): 在链表 position 位置插入元素 val;
insert(position, n, val): 在链表 position 位置插入 n 个元素 val;
insert(position, first, last): 在链表 position 位置插入 [first, last) 区间的元素;
erase(position): 删除 position 位置的元素;
erase(first, last): 删除 区间[first, last) 位置的元素;
三、实例讲解
3.1 size()、clear()、empty() 方法
函数原型为:
size_type size() const // 返回链表存储的元素总数
void clear() // 清空链表
bool empty() const // 判断链表是否为空,空返回 true,否则,false
来看一个例子:
#include <iostream>
#include <list> // list 头文件
using namespace std; // 命名空间,如果没有,则使用 std::list
int main() {
list<int> t1; // 定义一个名为 t1 的 int 类型的 list
cout<<"t1.size() = "<<t1.size()<<endl; // size = 0
cout<<"t1.empty() = "<<t1.empty()<<endl; // 判断是否为空
cout<<"--------------------------"<<endl;
t1.push_back(1); // 在 t1 尾部添加一个元素
cout<<"t1 size = "<<t1.size()<<endl; // size = 1
cout<<"t1.empty() = "<<t1.empty()<<endl; // 判断是否为空
cout<<"--------------------------"<<endl;
t1.clear();
cout<<"t1 size = "<<t1.size()<<endl; // size = 1
cout<<"t1.empty() = "<<t1.empty()<<endl; // 判断是否为空
}
输出结果为:
linuxy@linuxy:~/defineDir$ g++ -o main main.cpp
linuxy@linuxy:~/defineDir$ ./main
t1.size() = 0
t1.empty() = 1
--------------------------
t1 size = 1
t1.empty() = 0
--------------------------
t1 size = 0
t1.empty() = 1
linuxy@linuxy:~/defineDir$
3.2 push_front()、push_back() 方法
函数原型如下:
void push_front (const value_type& val); // 将元素添加到链表的开头
void push_back (const value_type& val); // 将元素添加到链表的结尾
来看一个例子:
#include <iostream>
#include <list> // list 头文件
using namespace std; // 命名空间,如果没有,则使用 std::list
int main() {
list<int> t1; // 定义一个名为 t1 的 int 类型的 list
t1.push_front(10);
t1.push_back(20);
list<int>::iterator iter;
for(iter = t1.begin(); iter != t1.end(); ++iter) {
cout<<*iter<<endl;
}
}
输出结果为:
linuxy@linuxy:~/defineDir$ g++ -o main main.cpp
linuxy@linuxy:~/defineDir$ ./main
10
20
linuxy@linuxy:~/defineDir$
可以看到,输出的顺序为 10、20。
3.3 pop_front()、pop_back() 方法
函数原型如下:
void pop_front(); // 删除链表最前面一个(第一个)元素
void pop_back(); // 删除链表最后一个元素
来看一个例子:
#include <iostream>
#include <list> // list 头文件
using namespace std; // 命名空间,如果没有,则使用 std::list
int main() {
list<int> t1; // 定义一个名为 t1 的 int 类型的 list
for(int i = 1; i <= 6; ++i) { // 插入元素 1 2 3 4 5 6
t1.push_back(i);
}
list<int>::iterator iter; // 迭代器
for(iter = t1.begin(); iter != t1.end(); ++iter) {
cout<<*iter<<" ";
}
cout<<endl;
// 输出为:1 2 3 4 5 6
t1.pop_front(); // 删除链表第一个元素
t1.pop_back(); // 删除链表最后一个元素
cout<<"----------------------------"<<endl;
for(iter = t1.begin(); iter != t1.end(); ++iter) {
cout<<*iter<<" ";
}
cout<<endl;
// 输出为:2 3 4 5
}
输出结果为:
linuxy@linuxy:~/defineDir$ g++ -o main main.cpp
linuxy@linuxy:~/defineDir$ ./main
1 2 3 4 5 6
----------------------------
2 3 4 5
linuxy@linuxy:~/defineDir$
可以看到,链表第一个和最后一个元素被删除了。
3.4 begin()、end() 方法
函数原型如下:
iterator begin(); // 返回指向第一个元素的迭代器
iterator end(); // 返回指向最后一个元素的迭代器
来看一个例子:
#include <iostream>
#include <list> // list 头文件
using namespace std; // 命名空间,如果没有,则使用 std::list
int main() {
list<int> t1; // 定义一个名为 t1 的 int 类型的 list
for(int i = 1; i <= 6; ++i) { // 插入元素 1 2 3 4 5 6
t1.push_back(i);
}
list<int>::iterator iter; // 顺序遍历链表的迭代器
for(iter = t1.begin(); iter != t1.end(); ++iter) {
cout<<*iter<<" ";
}
cout<<endl;
// 输出为:1 2 3 4 5 6
}
输出结果为:
linuxy@linuxy:~/defineDir$ g++ -o main main.cpp
linuxy@linuxy:~/defineDir$ ./main
1 2 3 4 5 6
linuxy@linuxy:~/defineDir$
可以看到,顺序输出了链表中的元素。
3.5 rbegin()、rend() 方法
函数原型如下:
reverse_iterator rbegin() // 返回指向最后一个元素的反向迭代器
reverse_iterator rend() // 返回指向第一个元素的反向迭代器
来看一个例子:
#include <iostream>
#include <list> // list 头文件
using namespace std; // 命名空间,如果没有,则使用 std::list
int main() {
list<int> t1; // 定义一个名为 t1 的 int 类型的 list
for(int i = 1; i <= 6; ++i) { // 插入元素 1 2 3 4 5 6
t1.push_back(i);
}
list<int>::reverse_iterator iter; // 反向迭代器
for(iter = t1.rbegin(); iter != t1.rend(); ++iter) {
cout<<*iter<<" ";
}
cout<<endl;
//输出为: 6 5 4 3 2 1
}
输出为:
linuxy@linuxy:~/defineDir$ g++ -o main main.cpp
linuxy@linuxy:~/defineDir$ ./main
6 5 4 3 2 1
linuxy@linuxy:~/defineDir$
可以看到,逆序输出了链表元素。
注意:遍历链表的迭代器需要使用反向迭代器,例如上面的代码中 list<int>:: reverse_iterator。
3.6 insert 方法
insert 重载了多个方法,下面来看一下。
3.6.1 插入单个元素
函数原型如下:
iterator insert (const_iterator position, const value_type& val); // 在 position 的位置插入元素 val
注意:如果链表中 position 位置原先有元素,则新元素插入后,原先的元素在新元素后面。
来看一个例子:
#include <iostream>
#include <list> // list 头文件
using namespace std; // 命名空间,如果没有,则使用 std::list
int main() {
list<int> t1; // 定义一个名为 t1 的 int 类型的 list
for(int i = 1; i <= 6; ++i) { // 插入元素 1 2 3 4 5 6
t1.push_back(i);
}
list<int>::iterator iter;
for(iter = t1.begin(); iter != t1.end(); ++iter) {
if(*iter == 3) { // 在元素 3 的位置插入元素 7,故元素 3 就位于了 7 后面
t1.insert(iter, 7);
}
}
for(iter = t1.begin(); iter != t1.end(); ++iter) {
cout<<*iter<<" ";
}
cout<<endl;
// 输出为: 1 2 7 3 4 5 6
}
输出结果为:
linuxy@linuxy:~/defineDir$ g++ -o main main.cpp
linuxy@linuxy:~/defineDir$ ./main
1 2 7 3 4 5 6
linuxy@linuxy:~/defineDir$
可以看到,7 插入到了原先 3 的位置,插入后,3 在 7 后面。
3.6.2 插入 n 个相同的元素
iterator insert (const_iterator position, size_type n, const value_type& val); // 在 position 位置插入 n 个元素 val
来看一个例子:
#include <iostream>
#include <list> // list 头文件
using namespace std; // 命名空间,如果没有,则使用 std::list
int main() {
list<int> t1; // 定义一个名为 t1 的 int 类型的 list
for(int i = 1; i <= 6; ++i) { // 插入元素 1 2 3 4 5 6
t1.push_back(i);
}
list<int>::iterator iter;
for(iter = t1.begin(); iter != t1.end(); ++iter) {
if(*iter == 3) { // 在元素 3 后面插入 5 个 7
t1.insert(iter, 5, 7);
}
}
for(iter = t1.begin(); iter != t1.end(); ++iter) {
cout<<*iter<<" ";
}
cout<<endl;
// 输出为:1 2 7 7 7 7 7 3 4 5 6
}
输出结果为:
linuxy@linuxy:~/defineDir$ g++ -o main main.cpp
linuxy@linuxy:~/defineDir$ ./main
1 2 7 7 7 7 7 3 4 5 6
linuxy@linuxy:~/defineDir$
可以看到,插入了 5 个 7,插入后,3 排在了最后一个 7 后面。
3.6.3 在 postion 位置插入 [first, last) 区间的元素
iterator insert (const_iterator position, InputIterator first, InputIterator last); // 在 position 位置插入[first, last) 区间的元素
来看一个例子:
#include <iostream>
#include <list> // list 头文件
using namespace std; // 命名空间,如果没有,则使用 std::list
int main() {
list<int> t1; // 定义一个名为 t1 的 int 类型的 list
for(int i = 1; i <= 6; ++i) { // 插入元素 1 2 3 4 5 6
t1.push_back(i);
}
list<int> t2;
for(int i = 7; i <= 9; ++i) { // 插入元素 7 8 9
t2.push_back(i);
}
list<int>::iterator iter;
for(iter = t1.begin(); iter != t1.end(); ++iter) {
if(*iter == 3) { // 在元素 3 的位置插入元素 7 8 9
t1.insert(iter, t2.begin(), t2.end());
}
}
for(iter = t1.begin(); iter != t1.end(); ++iter) {
cout<<*iter<<" ";
}
cout<<endl;
// 输出为: 1 2 7 8 9 3 4 5 6
}
输出结果为:
linuxy@linuxy:~/defineDir$ g++ -o main main.cpp
linuxy@linuxy:~/defineDir$ ./main
1 2 7 8 9 3 4 5 6
linuxy@linuxy:~/defineDir$
可以看到 7,8,9 插入到了 t1 中原先 3 的位置。其中,迭代器 first 和 last 只要是一个区间就可以,不一定是 begin() 和 end()。
3.7 erase 方法
3.7.1 删除单个元素
函数原型如下所示:
iterator erase (const_iterator position); // 删除 position 位置的元素
来看一个例子:
#include <iostream>
#include <list> // list 头文件
using namespace std; // 命名空间,如果没有,则使用 std::list
int main() {
list<int> t1; // 定义一个名为 t1 的 int 类型的 list
for(int i = 1; i <= 6; ++i) { // 插入元素 1 2 3 4 5 6
t1.push_back(i);
}
list<int>::iterator iter = t1.begin();
++iter;
t1.erase(iter); // 删除 2
for(iter = t1.begin(); iter != t1.end(); ++iter) {
cout<<*iter<<" ";
}
cout<<endl;
// 输出为: 1 3 4 5 6
}
输出结果为:
linuxy@linuxy:~/defineDir$ g++ -o main main.cpp
linuxy@linuxy:~/defineDir$ ./main
1 3 4 5 6
linuxy@linuxy:~/defineDir$
可以看到删除了元素 2。
3.7.2 删除区间 [first, last) 的元素
函数原型如下所示:
iterator erase (const_iterator first, const_iterator last); // 删除链表中 [first, last) 区间的元素
来看一个例子:
#include <iostream>
#include <list> // list 头文件
using namespace std; // 命名空间,如果没有,则使用 std::list
int main() {
list<int> t1; // 定义一个名为 t1 的 int 类型的 list
for(int i = 1; i <= 6; ++i) { // 插入元素 1 2 3 4 5 6
t1.push_back(i);
}
list<int>::iterator iter1, iter2;
iter2 = t1.begin();
++iter2;
iter1 = iter2;
++iter2;
++iter2; // iter1 指向元素 2,iter2 指向元素 4
t1.erase(iter1, iter2); // 删除 [2, 4) 区间的元素,故删除链表中的 2,3 两个元素
for(iter1 = t1.begin(); iter1 != t1.end(); ++iter1) {
cout<<*iter1<<" ";
}
cout<<endl;
// 输出 1 4 5 6
}
输出结果为:
linuxy@linuxy:~/defineDir$ g++ -o main main.cpp
linuxy@linuxy:~/defineDir$ ./main
1 4 5 6
linuxy@linuxy:~/defineDir$
可以看到删除了区间 [2, 4) 的元素,即:2 和 3。
四、总结
以上就是 STL list 常用的方法讲解,在经常插入/删除元素操作的情况下可以考虑使用 list。
🎈 本文为博主原创,阅读更多优质好文可以关注公众号:Linux猿技术
🎈 欢迎小伙伴们点赞👍、收藏⭐、留言💬
- 点赞
- 收藏
- 关注作者
评论(0)