拿捏循环链表

目录:

一:单链表(不带头单向不循环)与循环链表(带头双向循环)区别

二:循环链表初始化

三:循环链表头插

四:循环链表尾插

五:循环链表头删

六:循环链表尾删

七:循环链表查找

八:循环链表指定pos  位置的删除

九:循环链表指定pos  位置之前的插入

十:循环链表销毁

十一:结语


1:单链表(不带头单向不循环)与循环链表(带头双向循环)区别
1)结构上

循环链表多了给 前驱指针 pre 

2)链表增删查改

有了pre这个指针,效率大大提升

2:循环链表初始化

讲到初始化,这里主要就是对哨兵位 (暂时称为:phead)进行设置

注意:哨兵位只是占一个位置,并不存储任何有效的数据

循环链表初始状态是空的:phead 自己成环

 

 对应代码:

phead -> next = phead ;

phead -> pre  = phead ;

那么问题就来了,在设计这个初始化函数的时候用一级指针还是二级指针

想必,前期看过我的单链表的博客,自然会说二级指针呀:

 因为是对phead 这个指针进行改变所以是传二级指针。没毛病!不知道大家在做OJ题的时候,我们也涉及到对一级指针的改变,但是我们也可以返回这个一级指针,即可实现

ListNode* ListNodeInit()
{
	/*
	哨兵位:val 没有实际意义(自行赋值)
	初始化的目的就是对哨兵位进行设置
	因为整个接口都是用一级指针,若是初始化用二级指针有点不顺眼
	此函数返回哨兵位地址即可实现对哨兵位的初始化
	*/
	ListNode* phead = NULL;
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	phead = newnode;
	phead->val = -1;
	phead->pre = phead->next = phead;//够成环
	return phead;
}
3:循环链表头插

头插:即在哨兵位的后面进行头插(若是链表为空,此时的头插也即是尾插)

头插分析:显然我们只需改变指针走向即可

注意指针先后问题

 各位老铁康康这样的代码是否之正确:

phead-> next = newnode;

newnode-> next = phead-> next;

newnode-> pre = phead;

newnode->next-> pre = newnode;

 

此代码结合画图,来看, 显然这样是不对的,因为newnode-> next = phead-> next;这句代码让newnode 这个节点自己成环了,所以又怎么可能头插进去呢?

实质性原因是:当我们头插进来newnode 时,应该先执行

newnode-> next = phead->next;   //注意指针先后问题

phead->next->pre = newnode; //让原来头结点的 pre 指向newndoe

newnode-> pre = phead;

phead-> next = newnode;

	ListNode* newnode = BuyNode(x);
	newnode->next = phead->next;
	newnode->pre = phead;
	phead->next->pre = newnode;//原来第一个节点pre与新的头结点连接
	phead->next = newnode;//新的头结点

对于以上问题还可以这样解决:定义一个 next指针来保留一下原来的头结点

4:循环链表尾插

尾差之前我们需要先思考一个问题:对于单链表(不循环,不带头,单向)而言,每次尾插之前都需要遍历链表,来找尾结点

但是对于循环链表而言我们就不需要:找尾结点直接一步到位  phead-> pre

真的是没有对比就没有伤害,所以在这块,咱循环链表还是比较好搞滴

尾插分析:

 这里只需改变指针走向即可

代码:

void ListNodePushBack(ListNode* phead,DataType x)
{
	assert(phead);
	/*
	1:找尾结点 phead->pre
	2:指针连接 
	*/
	ListNode* newnode = BuyNode(x);
	newnode->next = phead;
	newnode->pre = phead->pre;
	phead->pre->next = newnode;//原来尾结点与newnode进行连接
	phead->pre = newnode;//新的尾结点
	
}
5:循环链表头删

依然如此,按照“国际惯例”:找头结点  phead -> next 

删除之前先保留一下 第二个节点  

当把链表所以节点删除后(除哨兵位),会自动保存一个循环链表

 代码见下:

void ListNodePopFront(ListNode* phead)
{
	assert(phead->next != phead);//不相等说明不为空
	ListNode* newFirst = phead->next->next;
	newFirst->pre = phead;

	phead->next = newFirst;//成为新的头结点
	//以下写法也对,但可读性差
	phead->next = phead->next->next;
	phead->next->pre = phead;


}
6:循环链表尾删

 既然谈到尾删,咱这里不得不提一嘴,单链表的尾删

单链表尾删逻辑:

1:链表不为空

2:只有一个节点:传二级指针

3:多个节点:传一级指针

4:找尾结点: 条件 tail -> next != NULL;

 咱就是说,是不是事很多。

相比较之下,循环链表就比较友好:找啥尾结点,直接一步到位  phead-> pre

直接改变指针走向即可。

老问题:保留一下尾结点的前一个节点 tailPre

void ListNodePopBack(ListNode* phead)
{
	assert(phead);
	/*
	空链表: 
	1:找尾结点 phead->pre
	2:成环:  改变新的尾结点与phead 直接的链接
	*/
	assert(phead->next != phead);//不相等说明链表不为空,为空不能删除

	ListNode* tail = phead->pre;
	ListNode* tailPre = tail->pre;
	phead->pre = tailPre;//新的尾结点
    tailPre->next = phead;
    free(tail);
}
7:循环链表查找

此函数可以实现2个功能:一个是查找;另一个是修改

逻辑:按值查找,若是存在,直接返回当前节点,否则返回 NULL

注意是从 第一个节点开始 而不是从哨兵位 开始

ListNode* Find(ListNode* phead, DataType x) //指定数据查找
{
	/*
	从第一个节点开始查找: phead->next
	依次遍历,若是存在返回节点
	否则返回NULL
	*/
	assert(phead);
	ListNode* cur = phead->next;
	while (cur != phead )  //phead 是哨兵位
	{
		if (x == cur->val)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;//没有找到
}
8:循环链表指定pos  位置的删除

这个接口的逻辑其实说白了与尾删没啥不同

注意:pos 这个节点是查找函数返回的

void ListNodeErase(ListNode* pos)//指定位置删除  pos是查找函数返回的
{
	assert(pos);
	ListNode* posPre = pos->pre;
	ListNode* posNext = pos->next;
	posPre->next = posNext;
	posNext->pre = posPre;
	free(pos);
}
9:循环链表指定pos  位置之前的插入

void ListNodeInsert(ListNode* pos, DataType x)//指定位置之前插入
{
	/*
	1:找到pos 前一个节点  pos->pre
	2:注意避免节点找不到
	3:改变指针连接: posPre,newnode,pos
	*/
	assert(pos);//为空直接不玩了
	ListNode* newnode = BuyNode(x);
	ListNode* posPre = pos->pre;
	posPre->next = newnode;
	newnode->pre = posPre;
	newnode->next = pos;
	pos->pre = newnode;

}
10:循环链表销毁

这里的销毁就是一个节点一个节点进行删除

注意:包括哨兵位在内

void ListNodeDestroy(ListNode* phead)
{
	/*
	注意哨兵位也需要删除
	一个节点一个节点删除
	*/
	assert(phead);
	ListNode* cur = phead->next;
	while (cur != phead)
	{
		ListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
}

 各位老铁们,别走开,接下来的问题你值得一看,或许哪天自己面试会遇到类似问题呢?

 前段时间看到过样一个问题:

有个求职者去面试:在他的简历上写着自己是比较熟练数据结构这个模块的。

面试官问了这样一个问题:你能否在10分钟之内,搞一个链表出来?

听到这,求职者心里多多少少是有点担忧“搞,是没有问题,但是这个时间能否在宽裕点……”

进过这种协商,时间定在15分钟

对于这个问题的答卷:很显然,这个求职者没有拿到100分

屏幕前的各位铁子们,假设你是那个求职者,你又会如何回答好这份答卷?

是的,要是我,我一定会用循环链表来搞呀(因为面试官有没有指定具体是8中链表的哪一种)

你仔细想想:循环链表效率多高呀

对于一个链表的基本操作无非不就是:

头删头插

尾删,尾插

任意位置的插入和删除

不知道你是否考虑过这样问题:

循环链表的尾插,头插其实就是任意位置插入的一个特例

尾删,头删其实就是任意位置的删除的一个特例

 完整代码如下:
DList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"
void ListNodePrint(ListNode* phead)
{
	assert(phead);
	ListNode* cur = phead->next;
	printf("guard<==>");
	while (cur != phead)
	{
		printf("%d<==>", cur->val);
		cur = cur->next;
	}
	printf("\n");
}
ListNode* ListNodeInit()
{
	/*
	哨兵位:val 没有实际意义(自行赋值)
	初始化的目的就是对哨兵位进行设置
	因为整个接口都是用一级指针,若是初始化用二级指针有点不顺眼
	此函数返回哨兵位地址即可实现对哨兵位的初始化
	*/
	ListNode* phead = NULL;
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	phead = newnode;
	phead->val = -1;
	phead->pre = phead->next = phead;//够成环
	return phead;
}

ListNode* BuyNode(DataType x)
{
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->val = x;
	newnode->next = NULL;
	newnode->pre = NULL;
	return newnode;
}
void ListNodePushBack(ListNode* phead,DataType x)
{
	assert(phead);
	/*
	1:找尾结点 phead->pre
	2:指针连接 
	*/
	//ListNode* newnode = BuyNode(x);
	//newnode->next = phead;
	//newnode->pre = phead->pre;
	//phead->pre->next = newnode;//原来尾结点与newnode进行连接
	//phead->pre = newnode;//新的尾结点
	ListNodeInsert(phead, x);//注意ListNodeInsert  这个函数功能是在pos 之前插入,所以要想实现尾插,需要传phead ,而不是phead->pre 

}
void ListNodePushFront(ListNode* phead, DataType x)
{
	/*
	注意phead不是头结点(第一个节点),phead 只是一个哨兵位,只占位置
	第一个节点:phead->next
	考虑指针先后问题,避免找不到第一个节点(链表不为空)
	*/
	assert(phead);
	//ListNode* newnode = BuyNode(x);
	//newnode->next = phead->next;
	//newnode->pre = phead;

	//phead->next = newnode;//新的头结点
	以上代码不对
	//ListNode* newnode = BuyNode(x);
	//newnode->next = phead->next;
	//newnode->pre = phead;
	//phead->next->pre = newnode;//原来第一个节点pre与新的头结点连接
	//phead->next = newnode;//新的头结点
	ListNodeInsert(phead->next, x);

}

void ListNodePopBack(ListNode* phead)
{
	assert(phead);
	/*
	空链表: 
	1:找尾结点 phead->pre
	2:成环:  改变新的尾结点与phead 直接的链接
	*/
	assert(phead->next != phead);//不相等说明链表不为空,为空不能删除
	ListNodeErase(phead->pre);

	//ListNode* tail = phead->pre;
	//ListNode* tailPre = tail->pre;
	//phead->pre = tailPre;//新的尾结点
 //   tailPre->next = phead;
	//free(tail);
}
void ListNodePopFront(ListNode* phead)
{
	assert(phead->next != phead);//不相等说明不为空
	ListNodeErase(phead->next);
	//ListNode* newFirst = phead->next->next;
	//newFirst->pre = phead;

	//phead->next = newFirst;//成为新的头结点
	以下写法也对,但可读性差
	//phead->next = phead->next->next;
	//phead->next->pre = phead;


}
ListNode* Find(ListNode* phead, DataType x) //指定数据查找
{
	/*
	从第一个节点开始查找: phead->next
	依次遍历,若是存在返回节点
	否则返回NULL
	*/
	assert(phead);
	ListNode* cur = phead->next;
	while (cur != phead )  //phead 是哨兵位
	{
		if (x == cur->val)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;//没有找到
}

void ListNodeInsert(ListNode* pos, DataType x)//指定位置之前插入
{
	/*
	1:找到pos 前一个节点  pos->pre
	2:注意避免节点找不到
	3:改变指针连接: posPre,newnode,pos
	*/
	assert(pos);//为空直接不玩了
	ListNode* newnode = BuyNode(x);
	ListNode* posPre = pos->pre;
	posPre->next = newnode;
	newnode->pre = posPre;
	newnode->next = pos;
	pos->pre = newnode;

}

void ListNodeErase(ListNode* pos)//指定位置删除  pos是查找函数返回的
{
	assert(pos);
	ListNode* posPre = pos->pre;
	ListNode* posNext = pos->next;
	posPre->next = posNext;
	posNext->pre = posPre;
	free(pos);
}
void ListNodeDestroy(ListNode* phead)
{
	/*
	注意哨兵位也需要删除
	一个节点一个节点删除
	*/
	assert(phead);
	ListNode* cur = phead->next;
	while (cur != phead)
	{
		ListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
}
DList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int DataType;
typedef struct DListNode 
{
	DataType val;
	struct DListNode* next;
	struct DListNode* pre;//前驱指针

}ListNode;
void  ListNodePrint(ListNode*phead);
ListNode* ListNodeInit();

void ListNodePushBack(ListNode* phead,DataType x);
void ListNodePushFront(ListNode* phead, DataType x);

void ListNodePopBack(ListNode* phead);
void ListNodePopFront(ListNode* phead);

ListNode* Find(ListNode* phead,DataType x); //指定数据查找(此函数可以实现2个功能:查找 + 修改)

void ListNodeInsert(ListNode* pos, DataType x);//指定位置之前插入,pos是查找函数返回的

void ListNodeErase(ListNode* pos);//指定位置删除

void ListNodeDestroy(ListNode* phead);
/*
面试题目: 10分钟之内写一个链表
注意:头删,尾删 都只是 ListNodeErase 这个函数一个特例
头插,尾插,同理,是ListNodeInsert 一个特例
所以 可以借用
*/

test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"

void TestPush()
{
	ListNode* plist = ListNodeInit();
	
	//ListNodeInit(&plist);
	//ListNodePushBack(plist, 1);
	//ListNodePushBack(plist, 2);
	//ListNodePushBack(plist, 3);
	ListNodePushFront(plist, 1);
	ListNodePushFront(plist, 2);
	ListNodePushFront(plist, 3);
	ListNodePushFront(plist, 4);
	ListNodePushFront(plist, 5);
	ListNodePushFront(plist, 6);
	ListNodePushFront(plist, 7);
	ListNodePrint(plist);


	ListNodePushBack(plist, 0);
	ListNodePrint(plist);
	ListNodeDestroy(plist);


}
void TestPop()
{
	//ListNode* plist = (ListNode*)malloc(sizeof(ListNode));
	//if (plist == NULL)
	//	return;
	//plist->val = -1;
	//plist->next = plist;
	//plist->pre = plist;
	//等价于以下代码
	ListNode* plist = ListNodeInit();

	ListNodePushBack(plist, 1);
	ListNodePushBack(plist, 2);
	ListNodePushBack(plist, 3);
	ListNodePrint(plist);

	//ListNodePopFront(plist);
	//ListNodePrint(plist);

	//ListNodePopFront(plist);
	//ListNodePrint(plist);

	//ListNodePopFront(plist);
	//ListNodePrint(plist);

	ListNodePopBack(plist);
	ListNodePrint(plist);

	ListNodePopBack(plist);
	ListNodePrint(plist);

	ListNodePopBack(plist);
	ListNodePrint(plist);
	ListNodeDestroy(plist);

}
void Test()
{
	ListNode* plist = ListNodeInit();

	ListNodePushBack(plist, 1);
	ListNodePushBack(plist, 2);
	ListNodePushBack(plist, 3);
	ListNode* pos = Find(plist, 3);
	if (pos != NULL)
	{
		ListNodeInsert(pos, 10);
		ListNodePrint(plist);
	}
	else
	{
		printf("操作失败\n");
	}
	ListNodeDestroy(plist);

}

void Test1()
{
	ListNode* plist = ListNodeInit();

	ListNodePushBack(plist, 1);
	ListNodePushBack(plist, 2);
	ListNodePushBack(plist, 3);
	ListNode* pos = Find(plist, 3);
	if (pos != NULL)
	{
		ListNodeErase(pos);
		ListNodePrint(plist);
	}
	else
	{
		printf("操作失败\n");
	}
	ListNodeDestroy(plist);

}

int main()
{
	TestPush();
	//TestPop();
	//Test();
	//Test1();
	return 0;
}
11:结语

以上就是我今日要为大家share的内容。其实说白了,循环链表的结构看似复杂,实际操作起来,非常简单。(就是多了一个 pre这样的一个前驱指针)。希望各位老铁们能够从这篇博客中学到一些知识,同时欢迎大家随时指正,那咱话不多说,你懂滴!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MessageBox好用吗?

MessageBox作为一种能够对接多系统平台的工具&#xff0c;在数字营销领域具有很大的实用性和价值。它提供了实时的数据同步、个性化的营销策略、用户互动功能等多种功能&#xff0c;可以帮助企业实现更精准、高效的营销活动。具体来说&#xff0c;MessageBox的优点包括&#xf…

Laykefu客服系统后台登录绕过

【产品介绍】 Laykefu 是一款基于workermangatawayworkerthinkphp5搭建的全功能webim客服系统&#xff0c;旨在帮助企业有效管理和提供优质的客户服务 【漏洞介绍】 请求头中Cookie中的”user_name“不为空时即可绕过登录系统后台&#xff0c;恶意攻击者可利用此漏洞获得后台…

基于摄像头的虹膜识别技术

随着苹果公司的指纹识别TouchID的推广流行&#xff0c;三星等公司的积极跟进&#xff0c;生物识别技术正被移动设备厂商所重视。 虹膜是什么&#xff1f; 人的眼睛由巩膜、虹膜、瞳孔三部分构成。巩膜即眼球外围的白色部分&#xff0c;约占总面积的30%&#xff1b;眼睛中心为瞳…

【React】如何使antd禁用状态的表单输入组件响应点击事件?

最近遇到一个需求&#xff0c;需要在<Input.textarea>组件中&#xff0c;设置属性disabled为true&#xff0c;使textarea响应点击事件&#xff0c;但直接绑定onClick并不会在禁用状态下被响应。 解决方法1 之后尝试了很多方法&#xff0c;比如设置csspointer-events:no…

日本失去的三十年:去杠杆用了14年

去年以来&#xff0c;日股在日本央行转鹰预期、基本面改善和一系列监管新规的催化下高歌猛进&#xff0c;日经指数已经逼近90年代资产泡沫时期的高位。今年迄今累计上涨8.51%&#xff0c;领跑全球&#xff0c;“失落的三十年”似乎已经远去。 日本因何走向衰退&#xff1f;“失…

MPLS VPN功能组件(2)

MP-BGP 采用地址族(Address Family)来区分不同的网络层协议,以便正确处理VPN-IPv4路由 传统的BGP-4(RFC1771)只能管理IPv4的路由信息,无法正确处理地址空间重叠的VPN的路由。 为了正确处理VPN路由,VPN使用RFC2858(Multiprotocol Extensions for BGP-4)中规定的MP-BG…

云计算 - 弹性计算技术全解与实践

一、引言 在过去的十年里&#xff0c;云计算从一个前沿概念发展为企业和开发者的必备工具。传统的计算模型通常局限于单一的、物理的位置和有限的资源&#xff0c;而云计算则通过分布式的资源和服务&#xff0c;为计算能力带来了前所未有的"弹性"。 弹性&#xff1a;…

TryHackMe-Vulnerability Capstone练习

本文相关的TryHackMe实验房间链接&#xff1a;TryHackMe | Vulnerability Capstone 先nmap扫一下 接下来我们访问一下 接下来我们searchsploit找一下漏洞 searchsploit Fuel CMS 执行漏洞exp&#xff08;此处使用TryHackMe中的box&#xff09; 如果使用本地机需要下载exp&am…

算法练习-删除二叉搜索树中的节点(思路+流程图+代码)

难度参考 难度&#xff1a;中等 分类&#xff1a;二叉树 难度与分类由我所参与的培训课程提供&#xff0c;但需要注意的是&#xff0c;难度与分类仅供参考。且所在课程未提供测试平台&#xff0c;故实现代码主要为自行测试的那种&#xff0c;以下内容均为个人笔记&#xff0c;旨…

制作离线版element ui文档

链接&#xff1a;https://pan.baidu.com/s/1k5bsCK9WUlZobhFBLItw1g?pwdgeyk 提取码&#xff1a;geyk --来自百度网盘超级会员V4的分享 https://github.com/ElemeFE/element 克隆官方代码 使用nvm切换node版本&#xff0c;推荐使用14.0.0 http://doc.xutongbao.top/doc/#/zh…

Prompt Engineering实战-构建“哄哄模拟器”

目录 一 背景 二 “哄哄模拟器”的Prompt Prompt 的典型构成 三 操作步骤 3.1 创建对话 3.2 游戏测试 一 背景 前几天《AI 大模型全栈工程师》第二节课讲了“Prompt Engineering&#xff0c;提示工程”&#xff0c;里面提到一些prompt相关的技巧&#xff0c;原则&#xf…

浅谈分布式CAP定律、BASE理论

第一节 分布式架构设计理论与Zookeeper环境搭建 1. 分布式架构设计理论 学习Zookeeper之前,我们需要掌握一些分布式系统基础知识&#xff1a;了解分布式系统的概念、原理。 配置管理 域名服务 分布式同步 发布订阅 1. 分布式架构介绍 1.1 什么是分布式 《分布式系统原理和…

构建异步高并发服务器:Netty与Spring Boot的完美结合

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 ChatGPT体验地址 文章目录 前言IONetty1. 引入依赖2. 服务端4. 客户端结果 总结引导类-Bootstarp和ServerBootstrap连接-NioSocketChannel事件组-EventLoopGroup和NioEventLoopGroup 送书…

COMSOL接触(高度非线性)仿真常见报错及解决方法总结

前言 由于COMSOL采用隐式求解器&#xff0c;相较于使用显式求解器的Dyna、Abaqus等软件。要在COMSOL中实现结构接触这一高度非线性问题难度较大&#xff0c;报错时有发生。究其原因&#xff0c;是当物体之间相互接触时&#xff0c;物体受到的应力、运动路径会发生突变&#xff…

LabVIEW高精度微小电容测量

LabVIEW高精度微小电容测量 在电子工程和科研领域&#xff0c;精确测量微小电容值是一项有一定要求的任务&#xff0c;尤其在涉及到高精度和低成本时。设计了一种基于LabVIEW高精度微小电容测量系统&#xff0c;旨在提供一个既经济又高效的解决方案。 该系统的核心在于使用FD…

C语言中的内存函数你知道多少呢?

目录 ​编辑 1. memcpy的使用和模拟实现 1.1函数介绍 ​编辑 1.2函数的使用 1.3模拟实现 2. memmove的使用和模拟实现 2.1函数介绍 2.2函数的使用 2.3模拟实现 3. memset函数的使用 3.1函数介绍 3.2函数的使用 ​编辑 4. memcmp函数的使用 4.1函数介绍 4.2函数…

Python循环语句——range语句

一、引言 在Python编程中&#xff0c;range函数是一个内置函数&#xff0c;用于生成一个不可变的数字序列。它常被用于循环结构&#xff0c;如for循环&#xff0c;来遍历一系列的数字。尽管其使用非常基础&#xff0c;但range的强大之处在于其提供了灵活性&#xff0c;可以创建…

【2024.2.5练习】砍竹子(25分)

题目描述 题目分析 考虑题目是否满足贪心。每次施展魔法会使一段连续的竹子高度变为一半左右的平方根。根据样例&#xff0c;似乎每次让最高的竹子变短就能得到最优解。 假设魔法一次只能对一根竹子使用&#xff0c;永远不出现连续相同高度的竹子&#xff0c;那么显然无论使用…

接口幂等性

接口幂等性 如何实现幂等性实现方式一&#xff1a;数据库唯一主键实现方式二&#xff1a;Token机制实现方式三&#xff1a;数据库乐观锁实现方式四、加分布式锁 如何实现幂等性 其实实现幂等性的方案有不少&#xff0c;但是呢&#xff0c;这就得需要你根据不同的业务场景去选择…

Stable Diffusion 模型下载:Disney Pixar Cartoon Type A(迪士尼皮克斯动画片A类)

文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八案例九案例十 下载地址 模型介绍 目前还没有一个好的皮克斯迪士尼风格的卡通模型&#xff0c;所以我决定自己制作一个。这是将皮克斯风格模型与我自己的Loras合并在一起&#xff0c;创建一个通用的…
最新文章