手撕无头单链表

news2024/11/26 12:44:59

  • 💓 博客主页:江池俊的博客
  • ⏩ 收录专栏:数据结构探索
  • 👉专栏推荐:✅C语言初阶之路 ✅C语言进阶之路
  • 💻代码仓库:江池俊的代码仓库
  • 🔥编译环境:Visual Studio 2022
  • 🎉欢迎大家点赞👍评论📝收藏⭐

在这里插入图片描述

文章目录

  • 🚨一、什么是链表
    • 1.1 链表的概念
    • 1.2 链表的分类
  • 🚨二、单链表的结构
  • 🚨三、单链表的创建
    • 3.1 单链表的存储结构
    • 3.2 单链表的接口
  • 🚨四、接口实现(增、删、查、改)
    • 4.1 单链表的插入
      • 🔥前提:向内存申请节点空间
      • 📌尾插
      • 📌头插
      • 📌在pos节点之前插入x
      • 📌在pos节点以后插入x
    • 4.2 单链表的删除
      • ♨️尾删
      • ♨️头删
      • ♨️删除pos节点
      • ♨️删除pos的后一个节点
    • 4.3 单链表的其他接口实现
      • 🌟打印单链表
      • 🌟查找
      • 🌟单链表的销毁
  • 🚨五、源码
    • 5.1 `SList.h` 文件
    • 5.2 `SList.c` 文件
    • 5.3 `Test.h` 文件


🚨一、什么是链表

1.1 链表的概念

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

1.2 链表的分类

链表主要有单向、双向、带头节点、不带头节点、循环和非循环这些特点,再经过它们的互相组合就形成了 2 × \times × 2 × \times × 2 = 8种链表的结构。

此处我要讲解的是 不带头单向非循环 链表。


🚨二、单链表的结构

单向链表(又名单链表、线性链表) 是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过从头部开始,依序往下读取。

一个单向链表的节点被分成两个部分。第一个部分保存或者显示关于节点的信息,第二个部分存储下一个节点的地址。单向链表只可向一个方向遍历。

在这里插入图片描述

前面我们学习了顺序表,这里我们先来分析一下顺序表和单链表的优缺点:

顺序表:

优点:

  1. 访问元素时,顺序表可以在常数时间内完成(O(1)),无论数据量多大。
  2. 顺序表可以利用缓存和预读取技术进行优化,以提高访问速度。
  3. 对于随机访问和快速查找操作,顺序表通常比单链表更高效。

缺点:

  1. 顺序表在插入和删除操作上较慢,因为可能需要移动大量的元素来保持数据的连续性。这些操作的平均时间复杂度为O(n),其中n是元素的数量。
  2. 顺序表通常需要连续的内存空间,因此当数据量非常大时,可能会面临内存分配的问题。
  3. 在顺序表中查找特定元素可能需要遍历整个数组,这在处理大量数据时可能会变得很慢。

单链表:

优点:

  1. 插入和删除操作在链表的前端和后端相对较快,时间复杂度为O(1)。
  2. 内存使用效率较高,因为每个节点只存储一个指向下一个节点的引用。
  3. 对于一些特定的问题,如反转链表或找到链表中倒数第k个元素等,单链表有较好的解决方案。

缺点:

  1. 访问链表的中间元素需要从头部开始遍历,时间复杂度为O(n),其中n是链表的长度。
  2. 在大规模数据中,查找操作可能比其他数据结构(如数组或哈希表)慢。
  3. 由于需要额外的空间来存储指针,所以内存使用量比顺序表大。

🚨三、单链表的创建

3.1 单链表的存储结构

typedef int SLTDataType;//定义数据类型的别名,方便后续存储元素类型改变的修改

//定义结点类型 
typedef struct SListNode
{
	SLTDataType data;//每个节点存放一个数据元素
	struct SListNode* next;//结构体指针,指向下一个节点
}SLTNode;

3.2 单链表的接口

// 动态申请一个节点
SLTNode* BuySListNode(SLTDataType x);
//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x);
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x);
//尾删
void SLTPopBack(SLTNode** pphead);
//头删
void SLTPopFront(SLTNode** pphead);
//打印单链表
void SLTPrint(SLTNode* phead);
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
// 在pos节点之前插入x
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
// 在pos节点以后插入x
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
// 删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos);
// 删除pos的后一个节点
void SLTEraseAfter(SLTNode* pos);
//单链表的销毁
void SLTDestroy(SLTNode** pphead);

🚨四、接口实现(增、删、查、改)

4.1 单链表的插入

  • 在插入节点之前我们首先需要动态申请一个节点来保存要插入的数据,并将此节点放在相应的位置。
  • 而申请节点的操作在所有插入函数中都需要进行,故我们可以将这个操作写成一个申请节点的函数,方便各种插入函数的调用。

🔥前提:向内存申请节点空间

// 动态申请一个节点
SLTNode* BuySListNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x; 
	newnode->next = NULL;
	return newnode;
}

在这里插入图片描述

📌尾插

注意:

  1. 特殊情况,当链表为空时,则需要更改链表的头指针的地址,使得它的指向不再是 NULL,而是插入的新的节点的地址。
  2. 这里需要修改链表的头指针的地址,所以在传参的时候需要传入链表头指针的地址,即接收它的形式参数为二级指针。
//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuySListNode(x);
	if (*pphead == NULL) 
	{
		*pphead = newnode;//改变了结构体指针,所以传二级指针
	}
	else
	{
		SLTNode* tail = *pphead; 
		while (tail->next) 
		{ 
			tail = tail->next; 
		}
		tail->next = newnode;//改变的结构体,用结构体的指针即可
	}
}

📌头插

头插的算法比较简单,只需要将新节点的next指向链表的头,然后修改链表的头为新节点即可。即使链表为空时此算法依然成立。

//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuySListNode(x);
	
	newnode->next = *pphead; 
	*pphead = newnode; 
}

📌在pos节点之前插入x

这里我们需要保存 pos 节点之前那个节点的位置,所有使用一个临时结构体指针变量 prev来保存前一个节点的位置,然后将新的节点插入其中即可。

注意:

  • 特殊情况,当 pos 节点等于链表的头节点时,就需要进行头插的操作,将新的节点插入到链表的头节点之前,然后将新节点置为链表的头。
// 在pos节点之前插入x
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	//要么都是空,要么都不是空 --- 当链表不为空时,pos不能为空
	//当链表为空,则pos必须为空
	//总结;pos一定要为有效节点,NULL不是有效节点
	//assert((!pos && !(*pphead)) || (pos && *pphead));

	assert(pos);//pos不为空
	assert(*pphead);//链表不为空
	SLTNode* newnode = BuySListNode(x); 

	if (*pphead == pos)
	{
		//头插
		newnode->next = *pphead;  
		*pphead = newnode; 
	}
	else
	{
		SLTNode* prev = *pphead;//用来保存pos前面的那个节点
		while (prev->next != pos) 
		{
			prev = prev->next; 
		}
		prev->next = newnode; 
		newnode->next = pos;  
	}
}

📌在pos节点以后插入x

因为是在 pos 之后插入,所以自动的 pos 极为插入位置的前一个节点,于是只要把新的节点插入到 pos 节点之后即可。

注意:

  • pos 不能为 NULL,因为 NULL 后面无法再插入节点
// 在pos节点以后插入x
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);
	SLTNode* newnode = BuySListNode(x); 
	newnode->next = pos->next; 
	pos->next = newnode;
}

4.2 单链表的删除

♨️尾删

删除节点操作,首先要确保链表不为空。其次,就是当链表中只有一个节点时,删除链表后,链表变成空,此时链表的头需要置为空。

//尾删
void SLTPopBack(SLTNode** pphead)
{
	assert(pphead&&*pphead);//没有节点不需要删除
	//1.一个节点的删除
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else//2.多个节点的删除
	{
	    //第一种删除方式
		//SLTNode* prev = NULL; 
		//SLTNode* tail = *pphead; 
		//while (tail->next != NULL)
		//{
		//	prev = tail;
		//	tail = tail->next;
		//}
		//free(tail);
		//tail = NULL;//tail是局部变量,不置空也可以,因为出了作用域tail就销毁了
		//prev->next = NULL;
		
		//第二种删除方式	
		SLTNode* tail = *pphead;
		while (tail->next->next != NULL)
		{
			tail = tail->next; 
		}
		free(tail->next);
		tail->next = NULL; 
	}
}

♨️头删

头删也需要确保链表不为空,其次就是正常的删除节点的操作,当只有一个节点时,仍然满足逻辑。

//头删
void SLTPopFront(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* cur = (*pphead)->next; 
	free(*pphead); 
	*pphead = cur; 
}

♨️删除pos节点

  1. 首先,确保链表不为空
  2. 其次,判断链表的头节点是否为要删除的节点:
    • 如果是,则将链表的头节点置为此时头节点的 next
    • 如果不是,则用一个 prev 节点来保存 pos 节点的前一个节点的位置,通过 while 循环找到此节点。
// 删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && *pphead);
	assert(pos);
	if (*pphead == pos)
	{
		//头删
		*pphead = pos->next;
		free(pos);
		pos = NULL;
	}
	else
	{
		SLTNode* prev = *pphead;//用来保存pos节点之前的节点地址
		while (prev->next != pos) 
		{
			prev = prev->next; 
		}
		prev->next = pos->next; 
		free(pos); 
		pos = NULL; 
	}
}

♨️删除pos的后一个节点

个操作比较简单,因为要删除的节点的位置的前一个节点即为 pos,所以在进行删除的时候只需要保存要删除的节点,再让 pos
next 指向 posnextnext ,最后 free 掉保存的这个要删除的节点即可。

注意:

  • 这里需要确保 pos 节点不为空且 posnext 也不为空。
// 删除pos的后一个节点
void SLTEraseAfter(SLTNode* pos)
{
	assert(pos && pos->next);
	SLTNode* cur = pos->next;
	pos->next = cur->next;
	free(cur);
}

4.3 单链表的其他接口实现

🌟打印单链表

此算法只需要遍历一遍单链表,并将每个节点中存储的值打印出来即可,在循环外最好加上打印 NULL 的语句,便于直观的看出链表的结构。

//打印单链表
void SLTPrint(SLTNode* phead)
{
	while (phead)
	{
		printf("%d->", phead->data);
		phead = phead->next;
	}
	printf("NULL\n");
}

🌟查找

同理,只需要遍历链表即可。

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{//空链表可以查找
	while (phead)
	{
		if (phead->data == x)
		{
			return phead; 
		}
		phead = phead->next; 
	}
	return NULL;//链表为空找不到
}

🌟单链表的销毁

  1. 首先,通过assert(pphead);确认传入的链表头指针不为空,如果为空则程序会中断。
  2. 然后,定义两个指针prevcur,其中prev指向当前节点的前一个节点,cur指向当前节点。初始时,prev为空,cur指向链表的头节点。
  3. 使用一个循环遍历链表。在循环中,首先将prev指向当前节点,然后将cur指向下一个节点。然后释放prev指向的节点的内存空间,并将prev置为空。
  4. 循环直到cur为空,即遍历完整个链表。此时,prev将会指向链表的最后一个节点。
  5. 最后,将链表的头指针设置为空,即*pphead = NULL;,表示链表已经销毁。然后输出"单链表销毁成功"。

整个过程就是从链表的头部开始,逐个释放节点的内存空间,直到链表的尾部,完成整个链表的销毁。

//单链表的销毁
void SLTDestroy(SLTNode** pphead)
{
	assert(pphead);
	SLTNode* prev = NULL;
	SLTNode* cur = *pphead;
	while (cur)
	{
		prev = cur;
		cur = cur->next;
		free(prev);
		prev = NULL;
	}
	*pphead = NULL;
	printf("单链表销毁成功\n");
}

🚨五、源码

5.1 SList.h 文件

#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int SLTDataType;//定义数据类型的别名,方便后续存储元素类型改变的修改

//定义结点类型 
typedef struct SListNode
{
	SLTDataType data;//每个节点存放一个数据元素
	struct SListNode* next;//结构体指针,指向下一个节点
}SLTNode;

// 动态申请一个节点
SLTNode* BuySListNode(SLTDataType x);

//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x);

//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x);

//尾删
void SLTPopBack(SLTNode** pphead);

//头删
void SLTPopFront(SLTNode** pphead);

//打印单链表
void SLTPrint(SLTNode* phead);


//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);

// 在pos节点之前插入x
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);

// 在pos节点以后插入x
void SLTInsertAfter(SLTNode* pos, SLTDataType x);

// 删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos);

// 删除pos的后一个节点
void SLTEraseAfter(SLTNode* pos);

//单链表的销毁
void SLTDestroy(SLTNode** pphead);

5.2 SList.c 文件

#define _CRT_SECURE_NO_WARNINGS 1

#include "SList.h"

// 动态申请一个节点空间
SLTNode* BuySListNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x; 
	newnode->next = NULL;
	return newnode;
}

//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuySListNode(x);
	if (*pphead == NULL) 
	{
		*pphead = newnode;//改变了结构体指针,所以传二级指针
	}
	else
	{
		SLTNode* tail = *pphead; 
		while (tail->next) 
		{ 
			tail = tail->next; 
		}
		tail->next = newnode;//改变的结构体,用结构体的指针即可
	}
}

//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuySListNode(x);
	
	newnode->next = *pphead; 
	*pphead = newnode; 
}


//尾删
void SLTPopBack(SLTNode** pphead)
{
	assert(pphead&&*pphead);//没有节点不需要删除
	//1.一个节点的删除
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else//2.多个节点的删除
	{
		//第一种删除方式
		//SLTNode* prev = NULL; 
		//SLTNode* tail = *pphead; 
		//while (tail->next != NULL)
		//{
		//	prev = tail;
		//	tail = tail->next;
		//}
		//free(tail);
		//tail = NULL;//tail是局部变量,不置空也可以,因为出了作用域tail就销毁了
		//prev->next = NULL;

		//第二种删除方式
		SLTNode* tail = *pphead;
		while (tail->next->next != NULL)
		{
			tail = tail->next; 
		}
		free(tail->next);
		tail->next = NULL; 
	}
}

//头删
void SLTPopFront(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* cur = (*pphead)->next; 
	free(*pphead); 
	*pphead = cur; 
}

//打印单链表
void SLTPrint(SLTNode* phead)
{
	while (phead)
	{
		printf("%d->", phead->data);
		phead = phead->next;
	}
	printf("NULL\n");
}

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{//空链表可以查找
	while (phead)
	{
		if (phead->data == x)
		{
			return phead; 
		}
		phead = phead->next; 
	}
	return NULL;//链表为空找不到
}

// 在pos节点之前插入x
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	//要么都是空,要么都不是空 --- 当链表不为空时,pos不能为空
	//当链表为空,则pos必须为空
	//总结;pos一定要为有效节点,NULL不是有效节点
	//assert((!pos && !(*pphead)) || (pos && *pphead));

	assert(pos);//pos不为空
	assert(*pphead);//链表不为空
	SLTNode* newnode = BuySListNode(x); 

	if (*pphead == pos)
	{
		//头插
		newnode->next = *pphead;   
		*pphead = newnode;
	}
	else
	{
		SLTNode* prev = *pphead;//用来保存pos前面的那个节点
		while (prev->next != pos) 
		{
			prev = prev->next;
		}
 		prev->next = newnode;
		newnode->next = pos;  
	}
}

// 在pos节点以后插入x
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);
	SLTNode* newnode = BuySListNode(x); 
	newnode->next = pos->next; 
	pos->next = newnode;
}

// 删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && *pphead);
	assert(pos);
	if (*pphead == pos)
	{
		//头删
		*pphead = pos->next;
		free(pos);
		pos = NULL;
	}
	else
	{
		SLTNode* prev = *pphead;//用来保存pos节点之前的节点地址
		while (prev->next != pos) 
		{
			prev = prev->next; 
		}
		prev->next = pos->next; 
		free(pos); 
		pos = NULL; 
	}
}

// 删除pos的后一个节点
void SLTEraseAfter(SLTNode* pos)
{
	assert(pos && pos->next);
	SLTNode* cur = pos->next;
	pos->next = cur->next;
	free(cur);
}

//单链表的销毁
void SLTDestroy(SLTNode** pphead)
{
	assert(pphead);
	SLTNode* prev = NULL;
	SLTNode* cur = *pphead;
	while (cur)
	{
		prev = cur;
		cur = cur->next;
		free(prev);
		prev = NULL;
	}
	*pphead = NULL;
	printf("单链表销毁成功\n");
}

5.3 Test.h 文件

#define _CRT_SECURE_NO_WARNINGS 1

#include "SList.h"


void TestSLT1()
{
	SLTNode* plist = NULL; 
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	SLTPushFront(&plist, 1); 
	SLTPrint(plist);

	SLTNode* pos = SLTFind(plist, 3);//找到3的节点并返回其地址 
	SLTInsert(&plist, pos, 30);//在3前面插入30 
	SLTPrint(plist); 
	 
	SLTInsertAfter(pos, 40);//在3后面插入40 
	SLTPrint(plist); 

	SLTPopBack(&plist);//尾删 
	SLTPrint(plist);  

	SLTPopFront(&plist);//头删 
	SLTPrint(plist); 

	SLTEraseAfter(pos);//删除3后面的那个节点 
	SLTPrint(plist);  

	SLTErase(&plist, pos);//删除节点3 
	SLTPrint(plist); 

	SLTDestroy(&plist);//销毁单链表 
	SLTPrint(plist); 
}

int main()
{
	TestSLT1();
	return 0;
}

在这里插入图片描述


今天的内容就到这里了,后续我会给大家带来一些链表的 oj 题目,大家敬请期待👏

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

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

相关文章

腾讯云轻量服务器购买优惠,腾讯云轻量应用服务器优惠购买方法

你是否曾经为如何选择合适的服务器而苦恼&#xff1f;在互联网的海洋中&#xff0c;如何找到一个性价比高&#xff0c;性能稳定&#xff0c;价格合理的服务器供应商&#xff0c;确实是一个让人头疼的问题。今天&#xff0c;我要向你介绍的&#xff0c;是腾讯云轻量应用服务器的…

【1567.乘积为正数的最长子数组长度】

目录 一、题目描述二、算法原理三、代码实现 一、题目描述 二、算法原理 三、代码实现 class Solution { public:int getMaxLen(vector<int>& nums) {int nnums.size();vector<int> f(n);vector<int> g(n);f[0]nums[0]>0?1:0;g[0]nums[0]<0?1:0…

思考如何完成一个审批流

思考如何完成一个审批流 这篇文章&#xff0c;可能没有太多的干货&#xff0c;只是对于自己做过项目的一个反思与整理&#xff0c;同时&#xff0c;让这篇文章暴露在公共视野&#xff0c;虚心接受批评指导&#xff0c;向各位前辈同仁进行学习。 如果此文又不当之处&#xff0c;…

数据分析 - 离散概率分布的运用

期望公式 期望的方差 期望的标准差

二十、泛型(9)

本章概要 对缺乏潜在类型机制的补偿 反射将一个方法应用于序列 Java 8 中的辅助潜在类型 使用 Suppliers 类的通用方法 总结&#xff1a;类型转换真的如此之糟吗&#xff1f; 对缺乏潜在类型机制的补偿 尽管 Java 不直接支持潜在类型机制&#xff0c;但是这并不意味着泛型代…

windows虚拟内存自定义分配以及mysql错误:Row size too large (> 8126)

文章目录 虚拟内存概要windows-server配置虚拟内存技术名词解释关于mysql错误Row size too large (> 8126)问题分析解决办法 虚拟内存概要 虚拟内存别称虚拟存储器&#xff08;Virtual Memory&#xff09;。电脑中所运行的程序均需经由内存执行&#xff0c;若执行的程序占用…

【918.环形子数组的最大和】

目录 一、题目描述二、算法原理三、代码实现 一、题目描述 二、算法原理 三、代码实现 class Solution { public:int maxSubarraySumCircular(vector<int>& nums) {int sum0;for(auto x:nums) sumx;vector<int> f(nums.size());vector<int> g(nums.size…

iOS项目集成RN(0)

iOS原有项目集成RN 环境安装RN环境搭建Node & Watchman 安装 创建新应用iOS项目集成RN如果没有iOS项目&#xff0c;新建一个 swift&#xff0c; storyboard项目&#xff0c; 名字&#xff1a;RNTest新建一个 RNDemo目录&#xff0c;一个iOS子目录&#xff0c; 把iOS相关的拷…

科研学习|研究方法——python T检验

一、单样本T检验 目的&#xff1a;检验单样本的均值是否和已知总体的均值相等前提条件&#xff1a; &#xff08;1&#xff09;总体方差未知&#xff0c;否则就可以利用 Z ZZ 检验&#xff08;也叫 U UU 检验&#xff0c;就是正态检验&#xff09;&#xff1b; &#xff08;2&a…

趣学python编程 (一、计算机基础知识科普)

未来是高度科技化和智能化的时代。过去不识字的叫“文盲”&#xff0c;如今不懂点计算机知识&#xff0c;则可能是新时代的“文盲”。不论从事什么行业&#xff0c;了解下计算机和编程都是有益的。Python 连续多年占据最受欢迎的编程语言榜首&#xff0c;未来Python有机会成为像…

(八)五种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB

​ 一、五种算法&#xff08;DBO、LO、SWO、COA、GRO&#xff09;简介 1、蜣螂优化算法DBO 蜣螂优化算法&#xff08;Dung beetle optimizer&#xff0c;DBO&#xff09;由Jiankai Xue和Bo Shen于2022年提出&#xff0c;该算法主要受蜣螂的滚球、跳舞、觅食、偷窃和繁殖行为…

考虑区域多能源系统集群协同优化的联合需求侧响应模型(matlab代码)

该程序复现《考虑区域多能源系统集群协同优化的联合需求侧响应模型》文献模型&#xff0c;程序的核心是对多个区域级多能源系统互联系统进行多目标优化&#xff0c;并且考虑联合需求侧响应&#xff0c;以多个区域多能源系统运行总成本最小、碳排放最小为目标&#xff0c;建立多…

kickstarter数据采集

搜索界面字段如下&#xff1a; 详情界面字段如下&#xff1a; 评论页面采集&#xff1a; 需要的可以找我沟通

超市管理系统

闲着无聊写了个超市管理系统&#xff0c;包括商品管理、进销存、角色权限管理&#xff0c;很不错的项目分享给大家 1 商品管理 1.1 分类管理 分类信息主要包括名称、描述&#xff0c;可以进行停用、启用 1.2 商品信息 1.3 积分商品 1.4 销售统计 2 库存管理 2.1 仓库管理…

idea中git 移除对某个文件的跟踪

应用场景如下 某个log 文件&#xff0c;被同事用git 提交到了服务器&#xff0c;本地拉去之后我们的跟踪也会受影响 取消跟踪的方法如下&#xff1a; 删除本地缓存 git rm --cached "logs/test.log" 提交无效的log git commit -m "ignore log" 再将lo…

【AI视野·今日NLP 自然语言处理论文速览 第六十五期】Mon, 30 Oct 2023

AI视野今日CS.NLP 自然语言处理论文速览 Mon, 30 Oct 2023 Totally 67 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computation and Language Papers An Approach to Automatically generating Riddles aiding Concept Attainment Authors Niharika Sri Parasa,…

Docker启动SRS流媒体服务器

一、安装Docker 1.1、下载windows桌面版Windows 1.2、配置镜像 镜像加速器镜像加速器地址Docker 中国官方镜像https://registry.docker-cn.comDaoCloud 镜像站http://f1361db2.m.daocloud.ioAzure 中国镜像https://dockerhub.azk8s.cn科大镜像站https://docker.mirrors.ustc…

YOLOv5项目实战(3)— 如何批量命名数据集中的图片

前言:Hello大家好,我是小哥谈。本节课就教大家如何去批量命名数据集中的图片,希望大家学习之后可以有所收获!~🌈 前期回顾: YOLOv5项目实战(1)— 如何去训练模型 YOLOv5项目实战(2࿰

操作系统 day10(调度的概念、层次、七状态模型)

调度的概念 调度的层次 作业调度&#xff08;高级调度&#xff09; 进程调度&#xff08;低级调度&#xff09; 内存调度&#xff08;中级调度&#xff09; 挂起态与七状态模型 三层调度的联系和对比

AdServices归因和iAd归因集成

AdServices framework 是 Apple 专门为 ASA 提供的归因框架 。尤其在ATT 政策推出以后&#xff0c;app 获取用户 IDFA 的比例大幅降低&#xff0c;传统的依靠IDFA 的方法也无法准确归因。 但是 Apple 为 ASA 开了一个后门&#xff0c;其他广告渠道无法获取用户的 IDFA 作为身份…