C++必修:STL之forward_list与list的使用

✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:C++学习
贝蒂的主页:Betty’s blog

1. forward_list与list

forward_list 是 C++ 11 引入的一种容器,它是一种带有头节点的单向链表。使用时需要包含头文件#include<forward_list>,其特点为:

  • 内存使用高效,因为只维护单向的链接。
  • 擅长在头部进行插入和删除操作,时间复杂度为 O(1)。

img

下面是forward_list用法的简单示例:

#include <iostream>
using namespace std;
#include <forward_list>
int main() 
{
    forward_list<int> fl = {1, 2, 3};
    // 在头部插入元素
    fl.push_front(0);

    // 遍历并输出
    for (int num : fl) 
    {
        cout << num << " ";
    }
    cout << endl;

    return 0;
}

list 是 C++ 标准库中基于带头双向循环链表实现的容器。使用时需要包含头文件#include<list>,其特点为:

  • 动态大小:可以根据需要自动增长或收缩。
  • 高效的插入和删除操作:在任何位置进行插入和删除操作的时间复杂度都是近似 O(1)。
  • 双向遍历:支持双向迭代器,可以从前往后和从后往前遍历。

img

下面是list用法的简单示例:

#include <iostream>
#include <list>
using namespace std;
int main() 
{
    list<int> myList = {10, 20, 30, 40, 50};
    // 在头部插入元素
    myList.push_front(5);
    // 在尾部插入元素
    myList.push_back(60);
    // 遍历并输出
    for (int num : myList) {
        cout << num << " ";
    }
    cout << endl;

    // 删除指定元素
    myList.remove(30);

    // 再次遍历输出
    for (int num : myList) {
        cout << num << " ";
    }
    cout << endl;
    return 0;
}

forward_listlist 都是用于存储序列的数据结构,但有一些明显的区别。

  • 结构:forward_list 是单向链表,list 是双向链表。
  • 迭代器:forward_list 只提供前向迭代器,而 list 提供双向迭代器,使用更灵活。
  • 内存占用:一般情况下,forward_listlist 内存占用更少。

在实际应用中,如果只需要前向遍历且对内存使用要求较高,优先选择 forward_list;如果需要双向遍历和更灵活的操作,list 则更合适。

因为forward_list日常中并不常用,所以这里就不在详细介绍,如果有需求可以直接去查官方文档即可。下面就让我们来介绍一下list的一些基本用法。

2. list的接口

list的接口也有很多,我们学习时也需要借助相关的文档——list的用法

img

2.1. list的迭代器

2.1.1. begin()与end()函数

list 迭代器的begin()rend() 的使用方法具体如下:

  1. 函数声明
  • iterator begin();
  • const_iterator begin() const;
  1. 作用:返回指向列表第一个元素的迭代器。
  2. 返回值:普通对象返回 iterator 迭代器,const 对象返回 const_iterator 迭代器。
  1. 函数声明:
  • iterator end();
  • const_iterator end() const;
  1. 作用:返回指向列表最后一个元素下一个位置(头节点)的迭代器。
  2. 返回值:普通对象返回 iterator 迭代器,const 对象返回 const_iterator 迭代器。

img

示例:

void Test1() 
{
	list<int> numbers = { 1, 2, 3, 4, 5 };
	list<int>::iterator it = numbers.begin();
	cout << "First element: " << *it << endl;
	while (it != numbers.end())
	{
		cout << *it <<" ";
		++it;
	}
	// 注意:这里不能直接解引用it,因为此时它指向的是头节点
	cout << endl;
}

img

2.1.2. rbegin()与rend()函数

list迭代器的rbegin()rend() 的使用方法具体如下:

  1. 函数声明:
  • reverse_iterator rbegin();
  • const_reverse_iterator rbegin() const;
  1. 作用:返回指向列表最后一个元素的反向迭代器。
  2. 返回值:普通对象返回 reverse_iterator 迭代器,const 对象返回 const_reverse_iterator 迭代器。
  1. 函数声明:
  • reverse_iterator rend();
  • const_reverse_iterator rend() const;
  1. 作用:返回指向列表第一个元素前一个位置(头节点)的反向迭代器。
  2. 返回值:普通对象返回 reverse_iterator 迭代器,const 对象返回 const_reverse_iterator 迭代器。

img

示例:

void Test2() 
{
    list<int> numbers = {1, 2, 3, 4, 5};
    list<int>::reverse_iterator rit = numbers.rbegin();
    cout << "Last element: " << *rit << endl;
    while (rit!= numbers.rend()) 
    {
        cout << *rit << " ";
        ++rit;
    }
    cout << endl;
}

img

2.2. list的初始化与销毁

因为list是一个类,所以我们在初始化时肯定调用其构造函数初始化。以下就是我们常见初始化的接口:

img

img

下面是具体的代码示例:

void Test3() 
{
    // 默认构造函数
    list<int> numbers1; 
    cout << "默认构造: ";
    for (const auto& num : numbers1) {
        cout << num << " ";
    }
    cout << endl;
    // n个val构造
    list<int> numbers2(5, 10); 
    cout << "n个val构造: ";
    for (const auto& num : numbers2) {
        cout << num << " ";
    }
    cout << endl;

    int arr[] = {1, 2, 3};
    // 通过vector的迭代器初始化
    list<int> numbers3(arr, arr + sizeof(arr) / sizeof(arr[0])); 
    cout << "迭代器区间构造: ";
    for (const auto& num : numbers3) {
        cout << num << " ";
    }
    cout << endl;

    list<int> numbers4 = {4, 5, 6};
    // 拷贝构造
    list<int> numbers5(numbers4); 
    cout << "拷贝构造: ";
    for (const auto& num : numbers5) {
        cout << num << " ";
    }
    cout << endl;

    numbers1 = numbers2;
    // 赋值重载
    cout << "赋值重载: ";
    for (const auto& num : numbers1) {
        cout << num << " ";
    }
    cout << endl;
}

img

而由于list是一个类,出了作用域会自动调用它的析构函数,所以不用显示调用。

2.3. list的容量操作

接下来我们将学习关于list类常见的容量操作:

函数名称功能
size返回列表中元素的数量
max_size返回列表可容纳的最大元素数量
empty检查列表是否为空,是则返回 true,否则返回 false
resize重新设置列表中元素的数量,超过原来数量则用默认值填充
clear清空列表中的所有元素
2.3.1. size()与max_size()

size 用于获取列表中当前元素的数量。max_size 返回列表能够容纳的最大元素数量。

void Test4() 
{
    list<int> numbers = {1, 2, 3, 4, 5};
    cout << "Size of list: " << numbers.size() << endl;
    cout << "Max size of list: " << numbers.max_size() << endl;
}

img

2.3.2. empty()和clear()

empty 用于检查列表是否为空。clear 用于清空列表中的所有元素。

void Test5() 
{
    list<int> numbers;
    if (numbers.empty()) 
    {
       cout << "List is empty" << endl;
    }
    numbers = {1, 2, 3};
    numbers.clear();
    if (numbers.empty()) 
    {
        cout << "List is cleared and now empty" << endl;
    }
}

img

2.3.3. resize()的用法

resize 用于重新设置列表中元素的数量。

当使用 resize(n) 时,如果 n 大于当前列表的大小,那么会在列表末尾添加足够数量的默认值元素,使列表大小达到 n 。如果 n 小于当前列表的大小,那么会从列表末尾删除一些元素,使列表大小变为 n

void Test6() 
{
    list<int> numbers = {1, 2, 3};
    numbers.resize(5);
    cout << "After resizing to 5: ";
    for (auto& num : numbers) 
    {
        cout << num << " ";
    }
    cout << endl;

    numbers.resize(2);
    cout << "After resizing to 2: ";
    for (auto& num : numbers) 
    {
        cout << num << " ";
    }
    cout << endl;
}

img

2.4. list的访问操作

接下来我们就来介绍list常见的访问函数:

函数名称功能
back返回列表最后一个元素
front返回列表第一个元素
void Test7() 
{
    list<int> myList = {10, 20, 30};  // 创建一个包含元素的列表

    // 输出列表的第一个元素
    cout << "The front element is: " << myList.front() << endl; 

    // 输出列表的最后一个元素
    cout << "The back element is: " << myList.back() << endl; 
}

img

2.5. list的修改操作

list关于修改的函数的接口都比较多,一一列举比较麻烦,这里我们只重点介绍常用的接口,剩下的大家具体使用时查官方文档即可。下面是常见的关于list修改的函数接口:

函数名称功能
push_back在列表尾部添加元素
push_front在列表头部添加元素
pop_back删除列表最后一个元素
pop_front删除列表第一个元素
insert在指定位置插入元素
erase删除指定位置或区间的元素
swap交换两个列表
2.5.1. push_back()与pop_back()

这两个函数就是简单的尾插与尾删。

void Test8() 
{
    list<int> myList;  // 创建一个空列表

    myList.push_back(10);  // 在列表尾部添加元素 10
    myList.push_back(20);  // 在列表尾部添加元素 20

    cout << "列表元素: ";
    for (auto& num : myList) {
        cout << num << " ";
    }
    cout << endl;

    myList.pop_back();  // 删除列表尾部的元素

    cout << "删除尾部元素后列表元素: ";
    for (auto& num : myList) {
        cout << num << " ";
    }
    cout << endl;
}

img

2.5.2. push_front与pop_front()

这两个函数就是简单的头插与头删。

void Test9() 
{
    list<int> myList;  // 创建一个空列表
    myList.push_front(5);  // 在列表头部添加元素 5
    myList.push_front(3);  // 在列表头部添加元素 3
    cout << "列表元素: ";
    for (auto& num : myList) {
        cout << num << " ";
    }
    cout << endl;
    myList.pop_front();  // 删除列表头部的元素
    cout << "删除头部元素后列表元素: ";
    for (auto& num : myList) 
    {
        cout << num << " ";
    }
    cout << endl;
}

img

2.5.3. insert()与erase()

当在列表中进行插入操作时,如果在一个迭代器指向的位置之前或之后插入元素,那么这个迭代器就会失效。所以我们需要接受insert的返回值,该返回值是一个指向新插入元素的迭代器。

void Test10()
{
	list<int> myList = { 1, 2, 3 };
	list<int>::iterator it = myList.begin();
	it = myList.insert(it, 4);  // 这里迭代器 it 失效
	it = myList.insert(it, 5);  // 这里迭代器 it 失效
	for (auto& num : myList)
	{
		cout << num << " ";
	}
	cout << endl;
}

img

使用erase函数删除元素后,指向被删除元素的迭代器也会失效,同样需要接受返回值,该返回值是一个指向删除元素后一个元素的迭代器。

void Test11()
{
	list<int> myList = { 1, 2, 3, 4, 5 };
	list<int>::iterator it = myList.begin();
	it = myList.erase(it);  // 迭代器 it 失效
	it = myList.erase(it);  // 迭代器 it 失效
	for (auto& num : myList)
	{
		cout << num << " ";
	}
	cout << endl;
}

img

2.5.4. swap()交换

listswap交换也与之前vectorstring中的swap一样只是指针交换,效率很高。

void Test12()
{
	list<int> list1 = { 1, 2, 3 };
	list<int> list2 = { 4, 5, 6 };
	cout << "交换之前:" << endl;
	for (auto& num : list1)
	{
		cout << num << " ";
	}
	cout << endl;

	for (auto& num : list2)
	{
		cout << num << " ";
	}
	cout << endl;
	list1.swap(list2);
	cout << "交换之前:" << endl;
	for (auto& num : list1)
	{
		cout << num << " ";
	}
	cout << endl;

	for (auto& num : list2)
	{
		cout << num << " ";
	}
	cout << endl;

}

img

2.6. list的其他操作

下面我们接受一些关于list常见的其他操作。

函数名称功能描述
splice将元素从一个列表转移到另一个列表
remove移除具有特定值的元素
remove_if移除满足条件的元素
unique移除重复的值
merge合并已排序的列表
sort对容器中的元素进行排序
reverse反转元素的顺序

splice() 函数主要用于在列表中进行元素的转移操作。 它可以将一个列表中的部分或全部元素转移到另一个列表中。可以指定要转移的元素范围以及目标插入位置等,实现了高效灵活的元素移动和重组。

void Test13() 
{
    list<int> list1 = {1, 2, 3};
    list<int> list2 = {4, 5};

    // 将 list2 的元素转移到 list1 中
    list1.splice(list1.end(), list2);

    for (auto num : list1) {
        cout << num << " ";
    }
    cout << endl;
}

img

remove函数相当于一直遍历列表,然后erase删除指定元素。

void Test14() 
{
    list<int> myList = {1, 2, 2, 3, 2};

    // 移除值为 2 的元素
    myList.remove(2);

    for (auto num : myList) {
        cout << num << " ";
    }
    cout << endl;
}

img

remove_if函数相当于remove的补充,它支持传参函数或者仿函数。

bool isEven(int num) {
    return num % 2 == 0;
}

void Test15() {
    list<int> myList = {1, 2, 3, 4, 5, 6};

    // 移除满足偶数条件的元素
    myList.remove_if(isEven);

    for (auto num : myList) {
        cout << num << " ";
    }
    cout << endl;
}

img

unique 函数主要用于移除列表中相邻的重复元素。它使得容器中只保留不重复的元素序列,但需要注意的是,它并不保证完全去除所有重复元素,只是处理相邻的重复项,通常也需要结合其他操作来实现完全去重。

void Test16() 
{
    list<int> myList = {1, 2, 2, 3, 3, 3};

    // 移除相邻的重复元素
    myList.unique();
    for (auto num : myList) {
        cout << num << " ";
    }
    cout << endl;
}

img

merge()函数主要用于将两个已排序的序列合并成一个新的已排序序列。 它会按照排序顺序将一个序列中的元素与另一个序列中的元素合理地组合在一起,形成一个合并后的有序序列。需要注意的是,在合并之前,两个源序列本身需要是已经排序好的。

void Test17()
{
    list<int> list1 = {1, 3, 5};
    list<int> list2 = {2, 4, 6};

    list1.sort();
    list2.sort();

    // 合并两个已排序的列表
    list1.merge(list2);

    for (auto num : list1) {
        cout << num << " ";
    }
    cout << endl;
}

img

list中的 sort() 函数用于对列表进行排序。它会按照指定的排序规则(默认为升序)对列表中的元素进行重新排列,使得元素按有序的方式呈现。

需要注意的是算法库中也有一个sort()函数,但是其支持的是随机迭代器,而list是一种双向迭代器,所以list无法调用算法库中的sort()

void Test18() {
    list<int> myList = {3, 1, 4, 1, 5, 9, 2, 6, 5};

    // 对列表进行排序
    myList.sort();

    for (auto num : myList) {
        cout << num << " ";
    }
    cout << endl;
}

最后还有一个reverse()函数,这个函数在算法库中也有,也可以用于实现list的逆置。

void Test19()
{
	list<int> l2 = { 1,2,4,5 };
	l2.reverse();//list中的reverse
	reverse(l2.begin(), l2.end());//算法库中的reverse
	for (auto& num : l2)
	{
		cout << num << " ";
	}
}

img

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Betty’s Sweet

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值