【C++】STL---list基本用法介绍

个人主页:平行线也会相交💪
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 平行线也会相交 原创
收录于专栏【C++之路】💌
本专栏旨在记录C++的学习路线,望对大家有所帮助🙇‍
希望我们一起努力、成长,共同进步。🍓
在这里插入图片描述

list是STL中的一种容器,底层其实就是一个双向链表

既然底层实现是双向链表,所以list重要的一点就是插入和删除操作的时间复杂度为常数时间O(1),这是因为链表的结构不需要像数组一样进行内存重排。

当然,如果要频繁访问链表中的元素,需要沿着链表进行遍历,这导致list容器访问操作的时间复杂度为O(n)。
在这里插入图片描述

下面将对list中的常见的用法进行一一介绍。

一、创建变量

下面链表变量的创建:

list<int> it1;//创建一个空的list
list<int> it2 = { 1,2,3,4,5 };//创建一个带有初始元素的list

二、增删查改

1️⃣插入元素

插入元素总共分为三种:尾插、头插、任意位置插入
尾插(push_back()):

list<int> lt1;
lt1.push_back(1);
lt1.push_back(2);

头插(push_front()):

list<int> lt1;
lt1.push_front(1);
lt1.push_front(2);

任意位置插入删除(insert()):

//在第三个位置进行插入10
list<int> lt1;
lt1.push_back(1);
lt1.push_back(2);
lt1.push_back(3);
lt1.push_back(4);
list<int>::iterator it = lt1.begin();
for (size_t i = 0; i < 2; i++)
{
	it++;
}
lt1.insert(it, 10);
for (auto e : lt1)
{
	cout << e << " ";
}
cout << endl;

在这里插入图片描述

2️⃣删除

//删除的节点中数据为3的节点
list<int> lt1;
lt1.push_back(1);
lt1.push_back(2);
lt1.push_back(3);
lt1.push_back(4);
for (auto e : lt1)
{
	cout << e << " ";
}
cout << endl;

list<int>::iterator it = lt1.begin();
it = find(lt1.begin(), lt1.end(), 3);
//判断是否查找成功
if (it != lt1.end())
{
	lt1.erase(it);
	//节点此时已经被删除了,当前的迭代器失效
	//*it *= 10;
}
for (auto e : lt1)
{
	cout << e << " ";
}
cout << endl;

在这里插入图片描述

下面来删除list中的偶数元素:

list<int> lt1;
	lt1.push_back(1);
	lt1.push_back(2);
	lt1.push_back(3);
	lt1.push_back(4);

	list<int>::iterator it = lt1.begin();
	while (it != lt1.end())
	{
		if (*it % 2 == 0)
		{
			it = lt1.erase(it);
		}
		else
		{
			it++;
		}
	}
	for (auto e : lt1)
	{
		cout << e << " ";
	}
	cout << endl;

在这里插入图片描述

3️⃣查找和修改

这里我们需要注意一点:std::list没有提供内置的find()函数

原因主要有两点:
第一遍历代替索引访问:由于std::list是一个双向链表,不支持通过索引来直接访问元素。相反,要访问或查找元素,需要使用迭代器进行迭代操作

第二:链表的特点是插入和删除元素的时间复杂度为O(1),但访问元素的时间复杂度为O(n),其中n是链表长度。相比之下,std::vector的访问时间复杂度为O(1)。由于链表不支持快速随机访问,使用线性搜索遍历整个链表可能是更高效的操作

举例:

//在链表中查找节点中数据为3的节点,如果查找成功,将该数据的数据*10
list<int> lt1;
lt1.push_back(1);
lt1.push_back(2);
lt1.push_back(3);
lt1.push_back(4);
for (auto e : lt1)
{
	cout << e << " ";
}
cout << endl;

list<int>::iterator it = lt1.begin();
it = find(lt1.begin(), lt1.end(), 3);
//判断是否查找成功
if (it != lt1.end())
{
	//查找成功则将迭代器位置的数据*10
	*it *= 10;
}
for (auto e : lt1)
{
	cout << e << " ";
}
cout << endl;
}

在这里插入图片描述
可以看到查找成功并将该节点的数据*10。

三、list大小计算

std::list提供了size()函数,用于返回链表中元素的数量
在这里插入图片描述

四、迭代器遍历

list<int> lt1 = { 1,2,3,4,5 };

for (auto it = lt1.begin(); it != lt1.end(); it++)
{
	(*it)++;
	cout << *it << " ";
}
cout << endl;

在这里插入图片描述

五、常见算法

1️⃣排序

list<int> lt = { 12,56,2,95,35,78,47 };
lt.sort();
for (auto e : lt)
{
	cout << e << " ";
}
cout << endl;

在这里插入图片描述

2️⃣删除指定值-remove()

这里需要注意的是:remove()函数会移除链表中所有与指定值相等的元素,而不仅仅是第一个匹配项。这与erase()函数的行为不同,erase()函数通常只移除第一个匹配项。

list<int> lt1 = { 12,56,2,95,35,78,47 };
lt1.remove(56);
for (auto e : lt1)
{
	cout << e << " ";
}
cout << endl;

在这里插入图片描述
如果链表中没有想要删除的指定值,则remove()函数不会作任何事情,也不会报错。请看举例:
在这里插入图片描述

3️⃣链表元素转移-splice()

在STL中,std::list提供了splice()函数,用于将一个链表的元素或者一个元素范围转移到另一个链表中。

splice()函数共有3个版本:

void splice(const_iterator position, list& x);
void splice(const_iterator position, list& x, const_iterator i);
void splice(const_iterator position, list& x, const_iterator first, const_iterator last);

下面对splice()函数的3个版本进行举例,请看:

list<int> lt1 = { 1,2,3,4 };
list<int> lt2 = { 10,20,30,40 };

list<int> lt3 = { 1,2,3,4 };
list<int> lt4 = { 10,20,30,40 };

list<int> lt5 = { 1,2,3,4 };
list<int> lt6 = { 10,20,30,40 };

// 将lt2中的所有元素移动到lt1的末尾
lt1.splice(lt1.end(), lt2);
for (auto e : lt1)
	cout << e << " ";
cout << endl;

// 将lt4中的第二个元素移动到lt3的开头
auto it3 = lt3.begin();
auto it4 = lt4.begin();
it4++;
lt3.splice(it3, lt4, it4);
for (auto e : lt3)
	cout << e << " ";
cout << endl;

//将lt6中的20 30 40移动到lt5的最后位置
auto first = ++lt6.begin();
auto end = lt6.end();
lt5.splice(lt5.end(), lt6, first, end);
for (auto e : lt5)
	cout << e << " ";
cout << endl;

运行结果如下:
在这里插入图片描述

好了,以上就是本文的全部内容,主要对list的一些基本语法和用法进行了介绍。
就到这里吧,再见啦友友们!!!

### C语言中 `list_init` 的实现与用法 在 Linux 内核开发中,`list_init` 并不是一个标准的函数名称,但它通常可以被理解为初始化一个双向链表的操作。Linux 内核提供了丰富的链表管理工具,其中最常用的是基于 `struct list_head` 定义的双向链表结构。 #### 双向链表的基础定义 以下是 `struct list_head` 的典型定义[^2]: ```c struct list_head { struct list_head *next, *prev; }; ``` 这个简单的结构体包含了两个指针成员变量:`next` 和 `prev`,分别指向下一个节点和上一个节点。通过这种设计,实现了双向链表的功能。 #### 初始化链表的方法 虽然没有直接命名为 `list_init` 的函数,但在实际应用中可以通过以下方式完成链表的初始化: 1. **静态初始化** 使用宏 `LIST_HEAD_INIT` 或者直接赋值的方式进行初始化。 ```c #define LIST_HEAD_INIT(name) { &(name), &(name) } struct list_head my_list = LIST_HEAD_INIT(my_list); ``` 这里将 `my_list.next` 和 `my_list.prev` 都指向自己,表示这是一个空的环形链表。 2. **动态初始化** 如果是在运行时创建并初始化链表,可以使用如下方法: ```c void INIT_LIST_HEAD(struct list_head *list) { list->next = list; list->prev = list; } ``` 调用该函数即可完成链表的初始化操作。例如: ```c struct list_head my_list; INIT_LIST_HEAD(&my_list); ``` 上述两种方法都可以使链表成为一个自闭环的形式,即 `next` 和 `prev` 均指向自身,这标志着链表为空的状态。 #### 添加节点到链表 一旦链表被成功初始化,就可以利用内核提供的接口来添加新的节点。常用的有以下几个函数: - `list_add`: 将新节点插入到指定位置之前。 - `list_add_tail`: 将新节点追加到链表尾部。 具体实现在引用中有提及[^4],核心逻辑是调整前后节点之间的连接关系以保持双向特性。 #### 示例代码 下面展示如何结合以上知识点构建一个完整的例子程序: ```c #include <stdio.h> #include <stdlib.h> // 定义链表头结构 struct list_head { struct list_head *next, *prev; }; // 动态初始化链表 #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); \ (ptr)->prev = (ptr); \ } while (0) int main() { // 创建链表头节点 struct list_head head; INIT_LIST_HEAD(&head); printf("List initialized.\n"); return 0; } ``` 此段代码展示了如何手动初始化一个名为 `head` 的链表实例,并打印确认消息。 --- ###
评论 111
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

兜里有颗棉花糖

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

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

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

打赏作者

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

抵扣说明:

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

余额充值