【C++】_list常用方法解析及模拟实现

news2024/11/17 1:55:10

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓

目录

  ✨说在前面

🍋知识点一:什么是list?

•🌰1.list的定义

•🌰2.list的基本特性

•🌰3.常用接口介绍

🍋知识点二:list常用接口

•🌰1.默认成员函数

🔥构造函数(⭐)

🔥析构函数

•🌰2.list对象的访问和遍历操作

🔥front、back

🔥迭代器(⭐)

•🌰3.list对象的容量操作

🔥empty

🔥size

•🌰4.list对象的修改操作

🔥push_back、pop_back

🔥push_front、pop_front

🔥insert(⭐)

🔥reverse

🔥merge

🔥sort(⭐)

🔥unique

🔥remove、remove_if

🔥splice

•🌰5.list的模拟实现

 • ✨SumUp结语


  ✨说在前面

亲爱的读者们大家好!💖💖💖,我们又见面了,上一篇文章我给大家介绍了一下vector的定义、常用接口以及模拟实现。如果大家没有掌握好相关的知识,上一篇篇文章讲解地很详细,可以再回去看看,复习一下,再进入今天的内容。

我们今天简单给大家讲解一下STL中的一员——list。ilst对应C语言中的链表,也是STL标准库中的一大容器。如果大家准备好了,那就接着往下看吧~

  👇👇👇
💘💘💘知识连线时刻(直接点击即可)

【C++】_string类字符串万字详细解析

【C++】_vector定义、_vector常用方法解析

  🎉🎉🎉复习回顾🎉🎉🎉

         

 博主主页传送门:愿天垂怜的博客

 ​​​​​

 ​​​​​​

🍋知识点一:什么是list?

•🌰1.list的定义

在C++中,list是一个双向链表容器,它允许在序列的任何位置进行快速的插入和删除操作。与vector(基于数组的连续存储容器)不同,list的元素在内存中不是连续存储的,而是通过指针(或引用)相互连接。这种结构使得list在进行元素插入和删除时不需要移动其他元素,从而提高了这些操作的效率。

我们来看看文档中list的定义:list的文档介绍

那么list的底层是如何实现的呢? 

list的底层实现是一个双向带头循环链表。每个节点(node)包含三个部分:

  1. 数据部分存储元素的值。
  2. 指向前一个节点的指针允许从后向前遍历链表。
  3. 指向下一个节点的指针允许从前向后遍历链表。

这种结构使得list支持高效的插入和删除操作,因为你可以直接修改指针来添加或删除节点,而不需要移动其他元素。然而,由于元素在内存中的非连续存储,list的随机访问(如通过索引直接访问元素)效率较低,因为需要从头或尾开始遍历链表直到找到目标元素。

​​​​​​

•🌰2.list的基本特性

🔥动态大小

 list的大小可以动态地增长和缩小。你可以随时向其中添加或删除元素,而不需要担心容器的容量限制。

🔥非连续存储

 与vector不同,list的元素在内存中不是连续存储的。每个元素都是一个节点,节点之间通过指针(或引用)相互连接。这种非连续存储的特性使得list在进行插入和删除操作时不需要移动其他元素。

 🔥双向遍历

list提供了双向遍历的能力。每个节点都包含指向前一个节点和下一个节点的指针(或引用),这使得你可以轻松地从头遍历到尾,或者从尾遍历到头。

🔥高效的插入和删除

由于list的非连续存储和双向遍历特性,它在序列的任何位置插入或删除元素都是高效的。这些操作通常只需要修改几个指针,而不需要移动其他元素。

🔥不支持随机访问

尽管list提供了灵活的插入和删除操作,但它不支持通过索引直接访问元素。这是因为元素在内存中的位置不连续,无法直接通过索引计算出元素的地址。相反,你需要从头或尾开始遍历链表,直到找到目标元素。

🔥迭代器失效

在list中进行插入或删除操作时,只有指向被插入或删除元素本身的迭代器会失效。其他迭代器(包括指向其他元素的迭代器)仍然保持有效。这与vector不同,后者在插入或删除元素时可能会使所有指向该元素之后元素的迭代器失效。

🔥内存分配

由于list的每个节点都是单独分配的,因此它可能会比vector消耗更多的内存(因为每个节点都需要额外的空间来存储指针)。然而,这种内存分配方式也使得list在处理大量小对象时更加灵活和高效。

🔥排序和搜索

尽管list不支持随机访问,但它仍然提供了排序(如std::sort)和搜索(如std::find)算法。这些算法通过遍历链表来工作,因此它们的效率可能低于在随机访问容器(如vector)上执行的相同算法。

 ​​​​​

•🌰3.常用接口介绍

🔥list对象的常见构造

构造函数( (constructor)接口说明
list (size_type n, const value_type& val = value_type())
构造的 list 中包含 n 个值为 val 元素
list()构造空的list
list (const list& x)拷贝构造函数
list (InputIterator first, InputIterator last)[first, last)区间中的元素构造list

🔥list iterator的使用

函数声接口说明
begin + end
返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
rbegin  rend
返回第一个元素的 reverse_iterator, end 位置 返回最后一个元素下一个位 置的 reverse_iterator, begin 位置 

注意:

1. begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动

2. rbegin(end)于rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动

 🔥list capacity

函数声明接口说明
empty检测list是否为空,是返回true,否则返回false
size返回list中有效节点的个数

🔥list element access

函数声明接口说明
front返回list的第一个节点中值的引用
back返回list的最后一个节点中值的引用

 🔥list modifiers

函数声明接口说明
push_frontlist首元素前插入值为val的元素
pop_front删除list中第一个元素
push_backlist尾部插入值为val的元素
pop_back删除list中最后一个元素
insertlist position 位置中插入值为val的元素
erase删除list position位置的元素
swap交换两个list中的元素
clear清空list中的有效元素

list中还有一些操作,需要用到时大家可参阅list的文档说明。

 ​​​​​​

🍋知识点二:list常用接口

•🌰1.默认成员函数

🔥构造函数(⭐)

接口如下,前面也有,大家再仔细看看:

 在文档中我们可以查看它们的具体用法: 

我们大家也需要学会查看英文文档,有不懂的就去查,锻炼我们查看英文文章的能力。 

代码示例如下:

#include <iostream>
using namespace std;
#include <list>

int main() 
{
	//test_list1();

	list<int> first;                                
	list<int> second(4, 100);                       
	list<int> third(second.begin(), second.end());  
	list<int> fourth(third);                    

	int myints[] = { 16,2,77,29 };
	list<int> fifth(myints, myints + sizeof(myints) / sizeof(int));

	return 0;
}

 有效list数据如下:

100 100 100 100
100 100 100 100
100 100 100 100
16 2 77 29

以上这些就是常规操作。那如果我们要遍历它呢?由于list不支持通过索引直接访问元素,所以有两种方式:

1. 利用迭代器

void print_list(const list<int>& lt)
{
	//迭代器
	list<int>::const_iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

2. 利用范围for

void print_list(const list<int>& lt)
{
	//范围for
	for (auto& e : lt)
	{
		cout << e << " ";
	}
	cout << endl;
}

 ​​​​​

🔥析构函数

析构函数直接用编译器默认生成、调用的就可以了,非常简单~

 

•🌰2.list对象的访问和遍历操作

🔥front、back

【front】和【back】是两个非常常用的成员函数,它们分别用于访问list容器中的第一个元素和最后一个元素。

front

【front】用于访问list中的第一个元素。调用【front】成员函数时,它返回对列表中第一个元素的引用。如果list为空(即没有任何元素),那么调用【front】会导致未定义行为(通常是程序崩溃),因此在使用之前应该检查list是否为空。

 【front】使用示例:

#include <iostream>  
using namespace std;
#include <list>  

int main() 
{
	list<int> myList = { 1, 2, 3, 4, 5 };
	if (!myList.empty()) 
	{
		cout << "The first element is: " << myList.front() << endl;
	}
	else 
    {
		cout << "The list is empty." << endl;
	}

	return 0;
}

back

与【front】类似,【back】用于访问list中的最后一个元素。调用【back】成员函数时,它返回对列表中最后一个元素的引用。同样地,如果list为空,则调用篇【back】会导致未定义行为。 

  【back】使用示例:

#include <iostream>  
using namespace std;
#include <list>  

int main() 
{
	list<int> myList = { 1, 2, 3, 4, 5 };
	if (!myList.empty()) 
	{
		cout << "The last element is: " << myList.back() << endl;
	}
	else 
    {
		cout << "The list is empty." << endl;
	}

	return 0;
}

🔥迭代器(⭐)

C++中的迭代器(Iterator)是一种允许你访问容器中元素的对象,而无需暴露容器的内部结构。迭代器提供了一种统一的方法来遍历容器中的所有元素,无论容器的具体类型如何(如数组、向量vector列表list等)。通过使用迭代器,你可以读取、写入或删除容器中的元素,而无需关心容器的具体实现细节。

🔥list iterator的使用

函数声接口说明
begin + end
返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
rbegin  rend
返回第一个元素的 reverse_iterator, end 位置 返回最后一个元素下一个位 置的 reverse_iterator, begin 位置 

注意:

1. begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动

2. rbegin(end)于rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动

3. 在list中,迭代器并不是原生指针,不能通过+或-来访问对应的节点元素。

	//不能直接加
	it = lt.begin();
	lt.erase(it + 3);

 在这里,我们可以对迭代器进行更加深层次的总结:

按照功能划分

按照迭代器的功能,我们可以将迭代器的类型分为iterator、reverse_iterator、const_iterator和const_reverse_iterator,我们再之前的string和vector中都了解过他们的功能。

按照性质划分

按照迭代器的性质,我们可以将迭代器分为单向迭代器、双向迭代器和随机迭代器。

1、单向迭代器

单向迭代器可以向前遍历容器中的元素,但只能++它们来访问下一个元素。你不能通过单向迭代器回退(即,你不能使用--运算符)。单向迭代器至少满足输入迭代器的所有要求,并添加了能够递增自身的能力。

单向迭代器的有:forward_list、unordered map/set...

2、双向迭代器

双向迭代器比单向迭代器更强大,因为它们可以向前遍历容器(递增),还可以向后遍历(递减)。这意味着你可以使用++和--运算符来遍历容器中的元素。

双向迭代器的有:list、map、set...

3、随机迭代器

随机访问迭代器是最强大的迭代器类型,它们支持上述所有操作,并增加了随机访问容器元素的能力。这意味着你可以使用迭代器算术来访问或比较容器中的元素。

随机迭代器的有:vector、string、deque...

也就是说,迭代器的性质是由对应数据结构的底层结构所决定的。

•🌰3.list对象的容量操作

list对象的容量操作比较简单,我们介绍一下三个:

或如下: 

函数声明接口说明
empty检测list是否为空,是返回true,否则返回false
size返回list中有效节点的个数

🔥empty

【empty】是list容器的一个成员函数,用于检查该容器是否为空

  【empty】使用示例:

#include <iostream>  
using namespace std;
#include <list>  

int main() 
{
	list<int> myList;  
	if (myList.empty()) 
	{
		cout << "List is empty." << endl;
	}
	else 
	{
		cout << "List is not empty." << endl;
	} 
	myList.push_back(10);
	myList.push_back(20);
	if (myList.empty()) 
	{
		cout << "List is empty." << endl;
	}
	else {
		cout << "List is not empty." << endl;
	}

	return 0;
}

运行结果:

List is empty.
List is not empty.

🔥size

【size】是一个非常重要的成员函数,它用于返回容器中元素的数量。这是一个只读操作,其时间复杂度为常数时间O(1),因为list内部通常维护了一个表示元素数量的计数器。

【size】使用示例:

#include <iostream>  
using namespace std;
#include <list>  

int main() 
{
	list<int> myList = { 1, 2, 3, 4, 5 };
	cout << "The size of myList is: " << myList.size() << endl;

	myList.push_back(6);
	cout << "After adding an element, the size of myList is: " 
    << myList.size() << endl;

	return 0;
}

输出将会是:

The size of myList is: 5  
After adding an element, the size of myList is: 6

 ​​​​​

•🌰4.list对象的修改操作

🔥push_back、pop_back

push_back

【push_back】用于在list的末尾添加一个元素。这个操作的时间复杂度是常数时间O(1),因为它只是简单地修改链表末尾的节点来指向新添加的节点,并更新链表的尾部指针。

 【push_back】使用示例:

#include <iostream>  
using namespace std;
#include <list>  

int main() 
{
	list<int> myList;

	myList.push_back(10);
	myList.push_back(20);
 
	for (int n : myList) 
	{
		cout << n << " ";
	}
	cout << endl;
	return 0;
}

注意:在list的成员函数中,有一个叫作【emplace_back】的成员函数。它的功能类似于【push_back】,但是它们有本质上的区别。

在我们现在的阶段,暂时理解一下的区别即可:

struct A
{
public:
	A(int a1 = 1, int a2 = 2)
		:_a1(a1)
		,_a2(a2)
	{
		cout << "A(int a1 = 1, int a2 = 2)" << endl;
	}

	int _a1;
	int _a2;

	A(const A& aa)
		:_a1(aa._a1)
		,_a2(aa._a2)
	{
		cout << "A(const A& aa)" << endl;
	}
};
void test_list2()
{
	list<int> lt1;
	lt1.push_back(1);
	lt1.emplace_back(2);
	lt1.emplace_back(3);
	lt1.emplace_back(4);
	lt1.emplace_back(5);

	print_list(lt1);

	list<A> lt2;
	//有名对象
	A aa1(1, 2);
	lt2.push_back(aa1);
	//匿名对象
	lt2.push_back(A(1, 2));
    //隐式类型转换
	lt2.push_back({ 1, 2 });
	//而emplace back支持直接传构造A对象的参数
	lt2.emplace_back((1, 2));
}

pop_back

【pop_back】用于移除list的最后一个元素。与【push_back】一样,这个操作的时间复杂度也是常数时间O(1)。它简单地删除链表末尾的节点,并更新链表的尾部指针。

 【pop_back】使用示例:

#include <iostream>  
using namespace std;
#include <list>  

int main() 
{
	list<int> myList;

	myList.push_back(10);
	myList.push_back(20);
 
	if (!myList.empty()) 
	{
		myList.pop_back();
	}

	for (int n : myList) 
	{
		cout << n << ' ';
	}

	return 0;
}

 ​​​​​

🔥push_front、pop_front

push_front

【push_front】用于在list的开头添加一个元素。这个操作的时间复杂度是常数时间O(1),因为它只是简单地创建一个新节点,将其指向当前列表的第一个节点,并更新列表的头部指针以指向这个新节点。

【push_front】使用示例:

#include <iostream>  
using namespace std;
#include <list>  

int main() 
{
	list<int> myList;

	myList.push_front(10);
	myList.push_front(20);
  
	for (int n : myList) 
	{
		cout << n << " ";
	}

	return 0;
}

 pop_front

 【pop_front】用于移除list的第一个元素。与【push_front】一样,这个操作的时间复杂度也是常数时间O(1)。它简单地删除列表的第一个节点,并更新列表的头部指针以指向下一个节点。

【pop_front】使用示例:

#include <iostream>  
using namespace std;
#include <list>  

int main() 
{
	list<int> myList;
 
	myList.push_front(10);
	myList.push_front(20);
 
	if (!myList.empty()) 
	{
		myList.pop_front();
	}

	for (int n : myList) 
	{
		std::cout << n << " ";
	}

	return 0;
}

 ​​​​​

🔥insert(⭐)

🔥reverse

【reverse】没有参数,也不返回任何值。它的作用是就地(in-place)反转list中元素的顺序。这意味着原始容器被直接修改,而不是创建一个新的反转后的容器。

 【reverse】使用示例:

#include <iostream>
using namespace std;
#include <list>

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

	cout << "原始list: ";
	for (int num : myList) 
	{
		cout << num << " ";
	}
	cout << endl;

	myList.reverse();

	cout << "反转后的list: ";
	for (int num : myList)
	{
		cout << num << " ";
	}
	cout << endl;

	return 0;
}

输出将会是:

原始list: 1 2 3 4 5   
反转后的list: 5 4 3 2 1

 ​​​​​

🔥merge

【merge】用于合并两个已排序的list容器。与通用【merge】算法(定义在<algorithm>头文件中,用于合并两个已排序的范围)不同,list的【merge】成员函数是专门为链表设计的,并且它直接在原链表上操作,而不需要额外的存储空间来存储合并后的结果。

【merge】使用示例:

#include <iostream>
using namespace std;
#include <list>

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

	//合并list1和list2,list2的内容将被移动到list1中,且list2将变为空  
	list1.merge(list2);

	for (int num : list1) 
	{
		std::cout << num << " ";
	}
	std::cout << std::endl; //输出: 1 2 3 4 5 6  

	// list2现在是空的  
	if (list2.empty()) 
	{
		std::cout << "list2 is empty after merge." << std::endl;
	}

	return 0;
}

 注意:

1. 在调用【merge】之前,两个list必须已经是有序的。如果它们不是有序的,则合并后的结果将不是有序的。

2. 【merge】函数会修改调用它的list对象,并清空另一个list对象(other)。

 ​​​​​

🔥sort(⭐)

list是一个双向链表,它不支持随机访问迭代器,因此不能直接使用通用【sort】算法进行排序。但是list提供了自己的【sort】成员函数,该函数使用链表特有的排序算法(如归并排序或某种形式的插入排序),以就地(in-place)方式对链表中的元素进行排序

【sort】使用示例:

#include <iostream>
using namespace std;
#include <list>

int main() 
{
	list<int> myList = { 4, 1, 3, 5, 2 };
	myList.sort();  

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

	return 0;
}

与通用【sort】对比

1. 适用容器

通用【sort】算法适用于支持随机访问迭代器的容器(如vector、deque),而list的【sort】成员函数仅适用于list。

2. 性能

对于list,使用其【sort】成员函数通常比尝试使用通用【sort】算法更高效,因为list的【sort】成员函数利用了链表的结构特性。然而,对于支持随机访问的容器,通用【sort】算法通常能提供更快的排序速度。

3. 稳定性

两者都提供稳定的排序,即相等元素的相对顺序在排序后保持不变。

4. 用法

通用【sort】算法需要指定排序范围的开始和结束迭代器,而list的【sort】成员函数则不需要,因为它直接作用于整个链表。此外,通用【sort】算法允许指定自定义的比较函数或函数对象,而list的【sort】成员函数也支持这一点,但通常是通过成员函数模板的重载来实现的。

测试算法库中【sort】和list中【sort】的性能:

//测试算法库中sort和list中sort的性能
void test_op1()
{
	srand((unsigned int)time(nullptr));
	const int N = 1000000;

	list<int> lt;
	vector<int> v;

	for (size_t i = 0; i < N; i++)
	{
		auto e = rand() + i;
		lt.push_back(e);
		v.push_back(e);
	}
	//排序
	int begin1 = clock();
	sort(v.begin(), v.end());
	int end1 = clock();

	int begin2 = clock();
	lt.sort();
	int end2 = clock();

	printf("vector sort:%d\n", end1 - begin1);
	printf("list1 sort:%d\n", end2- begin2);
}

在Release版本下的其一运行结果为:

vector sort:61
list sort:128

再看另外一个代码:

void test_op2()
{
	srand(time(0));
	const int N = 1000000;
	
	list<int> lt1;
	list<int> lt2;

	for (int i = 0; i < N; i++)
	{
		auto e = rand() + i;
		lt1.push_back(e);
		lt2.push_back(e);
	}
	int begin1 = clock();
	//拷贝vector
	vector<int> v(lt2.begin(), lt2.end());
	//排序
	sort(v.begin(), v.end());
	//拷贝回lt2
	lt2.assign(v.begin(), v.end());

	int end1 = clock();

	int begin2 = clock();
	lt1.sort();
	int end2 = clock();

	printf("list copy vector sort copy list sort:%d\n", end1 - begin1);
	printf("list sort:%d\n", end2 - begin2);
}

 其中lt2先拷贝到vector上用库里的【sort】进行排序,然后lt1依然调用自身的【sort】,我们来看其中一个运行结果:

list copy vector sort copy list sort:82
list sort:191

 显然在数据量比较大的时候,list尽量不要用它自己的【sort】进行排序。

 ​​​​​

🔥unique

【unique】用于移除容器中连续重复的元素,只保留每个元素组中的第一个元素。注意,这里的“重复”是指相邻元素的相等性,而不是在整个容器范围内的唯一性。【unique】函数通过比较相邻元素来工作,如果两个相邻元素相等,则删除第二个元素。

【unique】使用示例:

#include <iostream>
using namespace std;
#include <list>

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

	myList.unique();

	for (int num : myList) 
	{
		cout << num << " ";
	}
	cout << endl; //输出: 1 2 3 4 5  

	return 0;
}

 ​​​​​

remove 

🔥remove、remove_if

【remove】接受一个值作为参数,并移除容器中所有等于该值的元素。它使用元素的==运算符来比较元素与给定值。

 【remove】使用示例:

#include <iostream>
using namespace std;
#include <list>

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

	myList.remove(2);

	for (int num : myList) 
	{
		cout << num << " ";
	}
	cout << endl;//输出: 1 3 4 5  

	return 0;
}

remove_if

如果你希望删除的不是某个特定值的元素,而是满足某个条件的元素,此时就可以使用【remove_if】。【remove_if】接受一个谓词(即一个返回布尔值的函数或函数对象)作为参数,并移除容器中所有使该谓词返回true的元素。

 【remove_if】使用示例: 

#include <iostream>
using namespace std;
#include <list>

int main() 
{
	list<int> myList = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    //这里使用了lambda表达式作为谓词 
	myList.remove_if([](int i) { return i <= 5; });

	for (int num : myList) 
	{
		cout << num << " ";
	}
	cout << endl;//输出: 6 7 8 9  

	return 0;
}

 ​​​​​

🔥splice

【splice】用于合并两个list容器中的部分或全部元素,同时保持元素的相对顺序不变。与list的其他成员函数相比,【splice】函数的一个显著优点是它可以在不复制或移动元素的情况下重新排列元素,这就更加高效了,特别是对于大型容器或包含复杂对象的容器。

 【splice】使用示例:

#include <iostream>
using namespace std;
#include <list>

int main() 
{
	list<int> list1 = { 1, 2, 3, 4, 5 };
	list<int> list2 = { 6, 7, 8, 9, 10 };

	//将list2的所有元素移动到list1的开头  
	auto it = list1.begin();
	list1.splice(it, list2);

	//输出处理后的list1  
	for (int num : list1) 
	{
		cout << num << " ";
	}
	cout << endl;//输出: 6 7 8 9 10 1 2 3 4 5  

	return 0;
}

【splice】不仅可以将一个链表的节点转移到另一个链表,也可以调整当前链表的顺序。比如我们想将[1,2,3,4,5,6]中的某个元素后面的节点移动到最前面,我们可以:

#include <iostream>
using namespace std;
#include <list>

int main() 
{
	list<int> lt = { 1, 2, 3, 4, 5, 6};
	
	int x = 0;
	cin >> x;
	list<int>::iterator it = find(lt.begin(), lt.end(), x);
	if (it != lt.end())
	{
		lt.splice(lt.begin(), lt, it, lt.end());
	}
	return 0;
}

list2现在是空的,因为没有元素被复制或移动到list1,而是被重新链接到了list1上。  

 ​​​​​

•🌰5.list的模拟实现

list这块的源代码可以参考:list的源码

源代码看明白是有些难度的,我们可以结合这一篇文章,再参考一下我写的模拟实现:list模拟实现

 • ✨SumUp结语

到这里本篇文章的内容就结束了,本节介绍了C++中list的相关知识。这里的内容虽然很熟悉了,毕竟我们有了string和vector的基础,但是有一定的难度。希望大家能够认真学习,打好基础,迎接接下来的挑战,期待大家继续捧场~💖💖💖

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2119972.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【EST】:Pt/ZrO2单原子催化剂

摘要&#xff1a;制备稳定的单原子催化剂一直是环境催化领域去除各种污染物的研究热点&#xff0c;但在提高反应性和稳定性方面仍存在挑战。在此&#xff0c;通过在锆氧化物负载的铂催化剂&#xff08;Pt–Na/ZrO2&#xff09;中简单掺杂碱金属&#xff0c;成功制备了与碱金属通…

【C++初阶】:C++入门,引用概念及其性质

文章目录 一、引用的概念二、引用的语法规则1、引用特性2、常引用 二、引用的使用场景1. 引用做参数2. 引用做返回值 三、引用和指针的区别 一、引用的概念 首先明确一下&#xff0c;引用不是定义一个新的变量&#xff0c;而是给已经存在的变量起一个别名&#xff0c;变量和他…

相机内存卡格式化了照片怎么恢复?格式化恢复详解

摄影爱好者们都知道&#xff0c;相机内存卡是记录我们美好瞬间的重要媒介。然而&#xff0c;在使用过程中&#xff0c;有时我们会因操作不当或设备故障&#xff0c;不小心格式化了内存卡&#xff0c;从而导致珍贵的照片丢失。面对这种情况&#xff0c;我们该如何恢复这些被格式…

贪吃蛇项目实现(C语言)——附源码

前言 贪吃蛇是一款十分经典的游戏&#xff0c;其通过控制贪吃蛇的上下左右移动来吃食物&#xff0c;延长自己的身体&#xff0c;也会因为撞到墙体和自身而死亡。下面我们通过C语言来实现贪吃蛇。 1.技术要点 C语言枚举&#xff0c;结构体&#xff0c;链表&#xff0c;动态内…

内网中的RDP利用

学习参考 https://www.freebuf.com/articles/network/276242.html能跟着实操的都实操一下。熟悉一些命令&#xff0c;过程。 实验环境&#xff1a;win2008&#xff0c;192.168.72.139 两个用户&#xff1a; administrator&#xff0c;shizuru RDP服务 确定/开启 RDP服务确…

力扣第79题 单词搜索

前言 记录一下刷题历程 力扣第79题 单词搜索 单词搜索 原题目&#xff1a;给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 单词必须按照字母顺序&#xff0c;通过相邻…

解决ubuntu安装modelsim20.1 32位库依赖失败问题(附简易安装方法)

先说方法&#xff1a;不用管&#xff0c;直接继续安装软件就行。 注意&#xff0c;选中 libgtk-3-0t64:i386 而非 libgtk-3-0:i386 注意&#xff0c;选中 libcanberra0t64:i386 而非 libcanberra0:i386 注意&#xff0c;选中 libpng16-16t64:i386 而非 libpng16-16:i386 注意&…

白盒测试_学习总结

目录 一、白盒测试的概念理解 二、白盒测试的分类 1、静态分析 2、动态分析 &#xff08;1&#xff09;逻辑覆盖测试 a、语句覆盖 b、判定覆盖 c、条件覆盖 d、判定条件覆盖 e、条件组合覆盖 f、路径覆盖 &#xff08;2&#xff09;基本路径测试法 3、总结 一、白盒…

Arduino基础入门学习——BH1750(GY-302)+ LED模拟自动路灯的实现

BH1750&#xff08;GY-302&#xff09; LED 一、前言三、准备工作四、程序代码五、运行结果六、结束语 一、前言 相信很多人都见过一种路灯&#xff0c;白天的时候它是不亮的&#xff0c;等到了晚上环境变暗时就开始它的照明工作了&#xff0c;不了解的人可能认为是人为操纵它工…

如何编写Prompt,利用AI高效生成图表——图表狐(FoxChart)指南

在数据可视化领域&#xff0c;图表是数据的重要表达方式。为了让更多人能够轻松高校地生成美观、专业的图表&#xff0c;图表狐(FoxChart)应用而生。然而&#xff0c;要想充分发挥AI的潜力&#xff0c;编写合适的Prompt至关重要。本文介绍一些编写Prompt的原则&#xff0c;帮助…

代码随想录算法训练营第39天|198.打家劫舍、 213.打家劫舍II、337. 打家劫舍 III

目录 198.打家劫舍1、题目描述2、思路3、code4、复杂度分析 213.打家劫舍II1、题目描述2、思路3、code4、复杂度分析 337. 打家劫舍 III1、题目描述2、思路3、code4、复杂度分析 198.打家劫舍 题目链接&#xff1a;添加链接描述 1、题目描述 你是一个专业的小偷&#xff0c;计…

8、用户行为数据同步

1、 数据通道 用户行为数据由Flume从Kafka直接同步到HDFS&#xff0c;由于离线数仓采用Hive的分区表按天统计&#xff0c;所以目标路径要包含一层日期。具体数据流向如下图所示。 2、 日志消费Flume配置概述 按照规划&#xff0c;该Flume需将Kafka中topic_log的数据发往HDFS。…

cmake编译MQTT-C源码

Windows端编译MQTT-C源码&#xff0c;获取mqttc库&#xff08;动态库与静态库&#xff09;&#xff0c;用于集成到Qt工程中使用mqtt订阅与发布功能。 编译源码与编译出来的mqttc动态库、静态库下载​​​​​​​​​​​​​​https://download.csdn.net/download/qq_38159549…

直播怎么录屏?录屏网页的工具有吗?推荐这3款你千万不要错过~

直播与网页录屏&#xff1a;三款必备录屏软件推荐 为什么要记录直播&#xff1f;直播可以捕捉实时发生事件&#xff0c;是真真实实的one take&#xff0c;更重要的是可以记录直播画面中的实时弹幕、评论区的互动&#xff0c;无论是激动人心的体育赛事、教育课程还是互动性强的连…

C++11 14 17 20 23进化史

C11、C14、C17、C20和C23是C语言标准的不同版本&#xff0c;它们之间在功能、特性和语法上存在一些区别。以下是对这些版本主要区别的概述&#xff1a; C11 C11是C语言的一个重要标准&#xff0c;引入了大量新特性和改进&#xff0c;使C变得更加易用和强大。主要特性包括&…

Qt工程使用MQTT-C库与mqtt服务器数据通信

实现mqtt订阅与发布话题&#xff0c;与mqtt服务器进行数据通信 编译环境&#xff1a;Qt5.15.2 vs2019 需要mqttc库&#xff1a;mqttc.lib, mqttc.dll&#xff08;根据MQTT-C源码编译出来的库&#xff0c;参考cmake编译MQTT-C源码-CSDN博客&#xff09; 一、Qt pro文件编写 …

android kotlin 基础复习 继承 inherit

1、新建文件kt 2、代码&#xff1a; /**用户基类**/ open class Person1(name:String){/**次级构造函数**/constructor(name:String,age:Int):this(name){//初始化println("-------基类次级构造函数---------")println("name:${name},age:${age}")} }/**子…

信息安全工程师(1)计算机网络分类

一、按分布范围分类 广域网&#xff08;WAN&#xff09;&#xff1a; 定义&#xff1a;广域网的任务是提供长距离通信&#xff0c;运送主机所发送的数据。其覆盖范围通常是直径为几十千米到几千千米的区域&#xff0c;因此也被称为远程网。特点&#xff1a;连接广域网的各个结点…

计算机毕业设计 财会信息管理系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

【吊打面试官系列-Redis面试题】怎么理解 Redis 事务?

大家好&#xff0c;我是锋哥。今天分享关于【怎么理解 Redis 事务&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; 怎么理解 Redis 事务&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 1&#xff09;事务是一个单独的隔离操作&#xff1…