30 0

[作业] C++ -- STL【list的使用】 [推广有奖]

  • 0关注
  • 0粉丝

等待验证会员

学前班

40%

还不是VIP/贵宾

-

威望
0
论坛币
0 个
通用积分
0
学术水平
0 点
热心指数
0 点
信用等级
0 点
经验
20 点
帖子
1
精华
0
在线时间
0 小时
注册时间
2018-8-8
最后登录
2018-8-8

楼主
朱女亭凡8943614 发表于 2025-12-3 18:33:25 |AI写论文

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币

1、forward_list与list

forward_list 是在 C++11 中新增的一种容器类型,其底层实现为带有一个头节点的单向链表。使用该容器需包含头文件 #include<forward_list>,主要特性包括:

  • 内存效率高,仅维护单向指针链接,占用空间较小。
  • 在链表头部进行插入或删除操作时性能优异,时间复杂度为 O(1)。

以下是一个简单的 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) 时间复杂度。
  • 提供双向迭代器,允许从前向后和从后向前两种遍历方式。

以下是 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_list 比 list 更节省内存空间。

实际开发中,若应用场景只需要单向遍历且对内存敏感,则推荐使用 forward_list;若需要频繁地双向访问或任意位置修改,list 是更优选择。

由于 forward_list 在日常编程中使用频率较低,本文不再深入展开,如有需要可查阅官方文档获取详细信息。接下来将重点介绍 list 容器的基本接口与使用方法。

2、list的接口

#

list 提供了丰富的成员函数接口,学习过程中建议结合标准文档参考 —— list的用法

2.1 list的初始化与销毁

作为类类型,list 对象通过调用构造函数完成初始化。常见的构造方式如下所示:

具体代码示例如下:

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;
}

由于 list 具备自动资源管理机制,在对象生命周期结束时会自动调用析构函数释放资源,因此无需手动显式调用销毁函数。

2.2 list的迭代器

list 支持多种类型的迭代器,可用于正向和反向遍历。

begin() 与 end() 的功能说明如下:

函数声明:

iterator begin();
const_iterator begin() const;

作用:返回指向列表首个元素的迭代器。

返回值:非 const 对象返回 iterator 类型,const 对象则返回 const_iterator 类型。

函数声明:

iterator end();
const_iterator end() const;

作用:返回指向最后一个元素后一个位置(即头节点)的迭代器。

返回值:普通对象返回 iterator,const 对象返回 const_iterator。

示例代码:

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;
}

rbegin() 与 rend() 用于反向遍历:

函数声明:

reverse_iterator rbegin();
const_reverse_iterator rbegin() const;

作用:返回指向最后一个元素的反向迭代器起始位置。

返回值:普通对象返回 reverse_iterator,const 对象返回 const_reverse_iterator。

函数声明:

reverse_iterator rend();
const_reverse_iterator rend() const;

作用:返回指向第一个元素前一位置(即头节点)的反向迭代器终点。

返回值:同上。

反向迭代器使用示例:

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;
}

2.3 list的容量操作

list 提供了一系列用于查询和控制容器容量的方法,常用接口如下表所示:

函数名称 功能描述
size 返回当前列表中元素的个数
max_size 获取容器理论上最多能容纳的元素数量
empty 判断列表是否为空,空则返回 true,否则返回 false
resize 重新设定列表中元素的数量,若新大小大于原大小,则用默认值填充新增部分
clear 移除所有元素,使列表变为空

size 函数用于获取当前 list 中实际存储的元素总数。

max_size 返回系统限制下该 list 最大可容纳的元素数目。

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;
}

empty 可快速判断容器是否为空。

clear 能够彻底清空 list 中的所有内容。

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;
    }
}

resize 方法可以调整 list 的元素数量。

当调用 resize(n) 时,如果 n 大于当前元素数量,则会在末尾添加默认构造的元素以补足至 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;
}

2.4 list 的访问操作

接下来我们介绍 list 中常用的访问函数:

函数名称 功能说明
back 获取并返回列表中最后一个元素
front 获取并返回列表中第一个元素

需要注意的是,list 不支持通过下标配合 [ ] 操作符进行元素访问。这是因为像 vector 这类容器底层采用连续内存空间,可以通过指针偏移快速定位元素;而 list 使用的是非连续的链式结构,访问第 n 个元素需要从头或尾逐个遍历,时间复杂度为 O(n)。尽管语法上可以实现 operator[ ],但由于性能代价过高,标准库并未提供该操作。

void Test7() 
{
    list<int> myList = {10, 20, 30};  // 创建一个包含元素的列表

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

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

2.5 list 的修改操作

list 提供了多种用于修改内容的接口,种类较多。此处仅重点讲解常用方法,其余可查阅官方文档获取详细信息。以下是常见的 list 修改相关函数:

函数名称 功能描述
push_back 在列表尾部插入一个新元素
emplace_back 在列表尾部就地构造一个新元素
push_front 在列表头部插入一个新元素
emplace_front 在列表头部就地构造一个新元素
pop_back 移除列表最后一个元素
pop_front 移除列表第一个元素
insert 在指定位置前插入元素或元素序列
erase 删除指定位置或区间内的元素
swap 交换两个 list 容器的内容

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;
}

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;
}

C++11 引入的 emplace 系列函数,其核心优势在于“就地构造”机制,避免了临时对象的生成以及后续的拷贝或移动开销,从而显著提升性能。

此外,emplace 系列还支持传递构造参数,适用于自定义类型的对象构建。

insert 与 erase 操作注意事项

在使用 insert() 向列表中插入元素时,若操作发生在某个迭代器所指向的位置之前或之后,原迭代器将失效。因此建议接收 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;
}

同理,在调用 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;
}

list 中的 swap() 函数与 vector、string 中的 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;

}

2.6 list 的其他常用操作

以下是一些 list 中常见的附加功能函数:

函数名称 功能描述
splice 将一个列表中的元素转移到另一个列表中
remove 删除所有等于特定值的元素
remove_if 根据条件函数删除满足条件的元素
unique 去除相邻的重复元素
merge 合并两个已排序的列表为一个有序列表
sort 对列表内元素进行排序
reverse 反转列表中元素的顺序

splice() 函数允许将一个 list 中的部分或全部元素直接迁移到另一个 list 中,支持指定转移范围和插入位置,是一种高效且灵活的元素重组方式。

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;
}

remove 函数的作用类似于遍历整个列表,并对每一个匹配目标值的元素调用 erase 进行删除。

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

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

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

remove_ifremove 的扩展版本,支持传入函数指针或仿函数作为判断条件,实现更复杂的筛选逻辑。

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;
}

unique() 函数用于清除相邻的重复元素,保留唯一的连续序列。需注意:它仅处理相邻重复项,并不会消除所有重复值(例如分散在不同位置的相同元素),如需完全去重,通常需先排序再调用 unique。

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

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

merge() 函数用于将两个已排序的 list 合并为一个新的有序列表。合并过程会按照升序(或其他指定顺序)整合两个源列表中的元素。使用前提要求两个原始列表均已排好序,否则结果不可预期。

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;
}

sort() 成员函数用于对 list 内部元素进行排序,默认按升序排列。值得注意的是,标准算法库中的 std::sort 要求随机访问迭代器,而 list 仅提供双向迭代器,因此无法使用 <algorithm> 中的全局 sort 函数,必须调用 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 自带的 reverse 成员函数更为高效,专为链表结构优化设计。

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 << " ";
	}
}

3. 迭代器分类

根据功能强弱和操作特性,C++ 中的迭代器可分为以下几类:

迭代器的类型决定了其所能支持的算法操作。例如,标准算法库中的 sort 需要随机访问迭代器才能实现高效的排序逻辑,而 list 使用的是双向迭代器,不具备随机访问能力,因此不能直接使用该算法。

由于 sort 函数的底层实现基于快速排序,因此要求所使用的迭代器具备随机访问能力,并支持通过下标进行加减操作。正因如此,list 容器无法直接使用标准库中的 sort 算法,因为它所提供的迭代器不满足随机访问的条件。为此,list 类内部自行实现了一个专属的 sort 成员函数。

算法的设计前提是必须采用随机迭代器。

此外,在 C++ 标准库中,迭代器的分类采用了继承机制来表达不同类型之间的关系,即更具体的迭代器类型通过继承自较通用的基类来实现。这种设计体现了“子类是一种特殊的父类”的面向对象原则。

二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:list IST STL forward include

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
jg-xs1
拉您进交流群
GMT+8, 2025-12-5 16:56