一看就懂!保姆级实例详解 STL list 容器【万字整理】

  • Post author:
  • Post category:其他




🎈 作者:


Linux猿


🎈 简介:

CSDN博客专家🏆,C/C++、面试、刷题、算法尽管咨询我,关注我,有问题私聊!


🎈 关注专栏:

C/C++面试通关集锦


(优质好文持续更新中……)🚀



目录


一、什么是 list ?


二、List 的定义


2.1 头文件


2.2 定义


2.3 常用方法


三、实例讲解


3.1 size()、clear()、empty() 方法


3.2 push_front()、push_back() 方法


3.3 pop_front()、pop_back() 方法


3.4 begin()、end() 方法


3.5 rbegin()、rend() 方法


3.6 insert 方法


3.6.1 插入单个元素


3.6.2 插入 n 个相同的元素


3.6.3 在 postion 位置插入 [first, last) 区间的元素


3.7 erase 方法


3.7.1 删除单个元素


3.7.2 删除区间 [first, last) 的元素


四、总结


list 是 STL 最长使用的容器之一,是在日常使用以及面试中经常遇到的知识点,下面来详细讲解下 list。

一、什么是 list ?

list 是一个双向链表封装起来的容器,可以实现 O(1) 的时间复杂度插入和删除元素,但是,和链表一样,不能通过下标(例如:g[i])快速访问元素,只能通过遍历获取。

图1 list 示意图

二、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 竟然是它,Ubuntu 排第六 ?


零基础都能看懂的 STL map 详解


一文掌握C/C++内存泄漏,防止内存泄漏以及检测工具!


【万字整理】❤️8大排序算法❤️【建议收藏】



🎈

欢迎小伙伴们点赞👍、收藏⭐、留言💬




版权声明:本文为u011074149原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。