数据结构:为什么说链表是顺序表的升级版(c语言实现)

news2024/11/24 20:55:34
前言:

    我们在之前的几篇文章中详细的讲解了顺序表的特点,增删改查操作和动态顺序表的优点,并使用顺序表的底层结构实现了通讯录项目,似乎顺序表是一个非常完美的数据结构,它可以实现按照需求实现增删查改,对内存的控制也较为合理,空间都是在需要时手动开辟的。但是顺序表真的完美吗?事实上它并不完美,经过我们思考,顺序表还是存在一些问题,例如:(1)顺序表中间/头部的插入删除,时间复杂度为O(N) ( 2) 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。 (3)增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们 再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。这些问题都是我们应该考虑的,而我们接下来要介绍的另一种数据结构——链表,就能在实现属性表的各个功能的前提下很好的解决这些问题。

1.链表

1.1 链表的概念及结构

    链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。换句话说,链表就是一块一块的空间由指针像链子一样将它们链接起来了,方便我们去访问链表的每一个节点。

    它的结构像极了图中的小火车:

我们来看看真实链表的结构 :

      链表是由一个指针指向链表的头节点,每个节点分为两个部分,分别是数据部分和指针部分,数据部分负责存储我们要存储的数据,指针部分负责存储下一个节点的地址,链表的每一个节点都储存着下一个节点的地址,最后一个节点的指针存储的是空指针,这使我们能够很方便的访问整个链表。

链表的特点:

1.从图中可以看出,链表的的结构在逻辑上是连续的,但是在物理上不一定连续。

2.每一个节点一般都是从堆上申请的。

3.从堆上申请的空间,是按照一定的策略分配的,两次申请的空间可能连续,可能不连续

1.2 链表的分类

链表的实际分类种类多达八种:

     链表分为单向和双向链表,带头和不带头链表,循环或者非循环链表,虽然由有这么多种链表,但是我们实际使用时最常用的只有两种:分别是无头单向非循环链表和带头双向循环链表。

1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了。

1.3 单链表的实现

    我们这一期用c语言代码来实现单链表。与顺序表相同,我们将实现链表的文件分成三个,分别是头文件SList.h文件,SLIst.c方法实现文件和测试文件test.c文件:

1.3.2 链表实现 

     链表由数据和指针两部分组成(前面已经详细解释),由于我们不确定会存储哪种类型的数据,使用typedef来作为我们的数据类型,要更数据类型时只需要更改typedef重命名的数据类型就可以:

 1、无头+单向+非循环链表增删查改实现
typedef int SLTDataType;
typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;

}SLTNode;

在这里我们使用的是int类型,最后将链表的名字改为SLTNode。

实现一个顺序表要实现许多方法,链表也是如此:

SLTNode* SLTBuyNode(SLTDataType x);//申请一个节点
void SLTPushBack(SLTNode** pphead, SLTDataType x);//尾删

void SLTPrint(SLTNode* phead);//打印

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

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

void SLTPosFront(SLTNode** pphead);//头删
 
SLTNode* SLTFind(SLTNode** pphead, SLTDataType x);//查找

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//指定位置之前插入

void SLTInsertAfter(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//指定位置之后插入

void SLTErase(SLTNode** pphead, SLTNode* pos);
//指定位置删除

void SLTEraseAfter(SLTNode* pos);
//指定位置之后删除

void SLTDestory(SLTNode** pphead);
//销毁
(1)申请一个节点 

     增删查改,只有里面有数据才能使用其他三个功能,所以我们首先实现插入功能,每次插入数据又需要申请一块空间,这会使得程序多出许多相同的代码,所以我们将申请空间封装成一个函数,在我们需要插入数据时,调用这个函数就可以了。而实现这个函数也非常的简单,只需要申请一块空间,将我们要插入的数据给它的data,next指针指向空就可以了,执行完这些操作后,返回这块空间:

SLTNode* SLTBuyNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail!");
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;

}//申请节点
(2)尾插 

     尾插我们需要考虑到我们要插入数据,传递的是一级指针的地址,就需要用二级指针来接收,传过来的指针不能为空,否则会出现非法访问的错误,如果传过来的是一个空链表,我们直接让头执针指向我们新开辟的空间就可以,如果不是链表,我们定义一个尾指针,让它遍历整个链表最终走到结尾,然后让它指向我们新开辟的节点,实现尾插操作:

void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = SLTBuyNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}

		tail->next = newnode;

	}
}
//尾插
(3)头插 

  头插的操作比较简单,只需要申请一个新节点,让它的next指针指向头节点,再让头指针指向它:

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

     为什么先讲查找呢?因为我们接下来的指定位置插入和删除需要用到它。我们要先判断传过来的地址和指针是否为空,如果为空,则没有查找的必要,确保地址和指针都不为空的情况下,我们才能进行查找操作。将链表遍历,如果发现数据内容相等,则视为找到了,返回这个节点,如果遍历完整个数组还没有找到这个节点,则说明链表里面没有这个节点,返回空指针:

SLTNode* SLTFind(SLTNode** pphead, SLTDataType x)
{
	assert(pphead && *pphead);
	SLTNode* pcur = *pphead;
	while (pcur)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}//查找函数
(5)指定位置之前插入 

  我们首先要确保传过来的地址和链表不为空,指定的那个节点也不能为空,如果为空,我们就无法执行插入操作,还要考虑如果我们指定的节点就是头节点和指定的节点在后面的情况能不能用同一种方法解决,我们发现这两种情况不能用同一种方法解决,所以如果指定的位置就是头节点,我们就使用头插的方法,如果在后面,就需要找到指定位置的前一个节点,让新节点的next指针指向我们指定的那个节点,然后让指定位置的前一个节点的next指针指向新节点:

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead && *pphead);
	assert(pos);
	SLTNode* newnode = SLTBuyNode(x);
	if (*pphead == pos)
	{
		SLTPushFront(pphead,x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		SLTNode* next = prev->next;
		newnode->next = next;
		prev->next = newnode;
	}

}//指定位置之前插入数据
(6)指定位置之后插入

    指定位置之后插入数据需要将它的next指针存储起来,让新节点的next指针存储它的next指针,再将我们指定节点的next指针存储新节点的地址:

void SLTInsertAfter( SLTNode* pos, SLTDataType x)
{
	
	assert(pos);
	SLTNode* newnode = SLTBuyNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}//指定位置后插入
(7)尾删 

      删除操作我们要确保链表不能为空,传过来的地址也不能为空,而链表只有一个节点和多个节点的情况也是不同的,如果链表只有一个节点,我们只要将它置空就可以了,如果有多个节点,我们则需要遍历链表,先将最后一个节点释放,然后将指向它的前一个节点的next指针置空:

void SLTPosBack(SLTNode** pphead)
{
	(pphead && *pphead);
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SLTNode* pcur = *pphead;
		SLTNode* prev = *pphead;
		while (pcur->next != NULL)
		{
			prev = pcur;
			pcur = pcur->next;
		}
		free(pcur);
		prev->next = NULL;
		pcur = prev;
	}
}//尾删
(8)头删 

    头删的实现也比较简单,只需要将头节点的next指针存起来,然后将头节点指向的空间释放,最后让头节点指向我们存起来的next指针:

void SLTPosFront(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}//头删
(9)指定位置删除 

     删除也要确保地址和指针不能为空,我们指定的节点也不能为空,否则无法进行删除操作,链表内只有一个节点和有多个节点的情况也是不一样的,如果只有一个节点,我们调用头删函数就可以了,如果有多个节点,就需要找到指定位置的前一个节点,将我们指定的节点释放后,再将它的前一个节点的next指针置空:

void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && *pphead);
	assert(pos);
	SLTNode* prev = *pphead;
	if (pos == *pphead)
	{
		SLTPosFront(pphead);
	}
	else
	{
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
	
}
//指定位置删除
(10)指定位置之后删除 

   我们要确保链表不能为空,也要确保链表呢有两个及以上的节点,否则我们无法指定删除某一个节点的后一个节点。先将我们要删除的节点的后一个节点存起来,将我们删除的节点释放后,用它的前一个节点的next指针指向那个存起来的节点,我们就能实现指定位置后删除:

void SLTEraseAfter(SLTNode* pos)
{
	assert(pos&&pos->next);
	SLTNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}//指定位置之后删除
(11)打印链表数据 

   打印链表要先确保链表不为空,为空则无法调用该函数,如果确保不为空,我们只需要遍历打印就可以:

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

   由于链表的空间是使用malloc在堆上开辟的,只有在程序结束之后才会释放,所以我们使用完这些空间后要手动释放:

(13)测试

  实现完所有的方法,我们来测试一下吧:

我们调用这些方法都是没有问题的。 

以上就是这一期单链表的所有内容了,它不需要像顺序表一样插入删除数据时要将数据频繁挪动,空间也是插入一个数据开辟一块空间,毫无疑问它称得上是顺序表的升级版,我将源码放在下面,感兴趣的小伙伴可以试试哦。

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* SLTBuyNode(SLTDataType x);//申请一个节点
void SLTPushBack(SLTNode** pphead, SLTDataType x);//尾删

void SLTPrint(SLTNode* phead);//打印

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

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

void SLTPosFront(SLTNode** pphead);//头删
 
SLTNode* SLTFind(SLTNode** pphead, SLTDataType x);//查找

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//指定位置之前插入

void SLTInsertAfter( SLTNode* pos, SLTDataType x);
//指定位置之后插入

void SLTErase(SLTNode** pphead, SLTNode* pos);
//指定位置删除

void SLTEraseAfter(SLTNode* pos);
//指定位置之后删除

void SLTDestory(SLTNode** pphead);
//销毁

SList.c :

#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"

SLTNode* SLTBuyNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail!");
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;

}//申请节点
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = SLTBuyNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}

		tail->next = newnode;

	}
}
//尾插

void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = SLTBuyNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}//头插
void SLTPosBack(SLTNode** pphead)
{
	(pphead && *pphead);
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SLTNode* pcur = *pphead;
		SLTNode* prev = *pphead;
		while (pcur->next != NULL)
		{
			prev = pcur;
			pcur = pcur->next;
		}
		free(pcur);
		prev->next = NULL;
		pcur = prev;
	}
}//尾删

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

SLTNode* SLTFind(SLTNode** pphead, SLTDataType x)
{
	assert(pphead && *pphead);
	SLTNode* pcur = *pphead;
	while (pcur)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}//查找函数

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead && *pphead);
	assert(pos);
	SLTNode* newnode = SLTBuyNode(x);
	if (*pphead == pos)
	{
		SLTPushFront(pphead,x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		SLTNode* next = prev->next;
		newnode->next = next;
		prev->next = newnode;
	}

}//指定位置之前插入数据

void SLTInsertAfter( SLTNode* pos, SLTDataType x)
{
	
	assert(pos);
	SLTNode* newnode = SLTBuyNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}//指定位置后插入
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && *pphead);
	assert(pos);
	SLTNode* prev = *pphead;
	if (pos == *pphead)
	{
		SLTPosFront(pphead);
	}
	else
	{
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
	
}
//指定位置删除
void SLTEraseAfter(SLTNode* pos)
{
	assert(pos&&pos->next);
	SLTNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}//指定位置之后删除
void SLTDestory(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* pcur = *pphead;
	
	while (pcur)
	{
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}//销毁
void SLTPrint(SLTNode* phead)
{
	assert(phead);
	while (phead)
	{
		printf("%d->", phead->data);
		phead = phead->next;
	}
	printf("NULL\n");
}//打印

test.c :

#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"


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

	SLTPrint(plist);

	SLTPushFront(&plist, 9);
	SLTPushFront(&plist, 89);
	SLTPushFront(&plist, 9);
	SLTPrint(plist);

	SLTPosBack(&plist);
	SLTPrint(plist);

	SLTPosFront(&plist);
	/*SLTPosFront(&plist);
	SLTPosFront(&plist);
	SLTPosFront(&plist);
	SLTPosFront(&plist);
	SLTPosFront(&plist);*/
	SLTPrint(plist);

	SLTNode* find = SLTFind(&plist,2);
	SLTEraseAfter(find);
	//SLTErase(&plist, find);
	/*SLTInsertAfter(&plist, find, 32);*/
	SLTPrint(plist);
	SLTDestory(&plist);
	
	

	/*SLTInsert(&plist, find, 8);
	SLTPrint(plist);*/

	/*if (find == NULL)
	{
		printf("找不到!\n");
	}
	else
	{
		printf("找到了!\n");
	}*/
}
int main()
{
	
	test02();
   //test01();
	return 0;
}

 

 

 

    

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

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

相关文章

换电脑后导入git本地仓库记录

导入本地仓库tig记录 换了新电脑&#xff0c;将旧电脑的数据盘查到新的笔记本之后发现&#xff0c;使用pycharm 读取不到本地的git提交记录了&#xff0c;我没有将本地git上传到远程仓库的习惯&#xff0c;这可抓马了&#xff0c;硬盘插回去的话也太麻烦了。试了 vscode 提示设…

冲击2024年CSDN博客之星TOP1:CSDN文章质量分查询在哪里?

文章目录 一&#xff0c;2023年博客之星规则1&#xff0c;不高的入围门槛2&#xff0c;[CSDN博文质量分测评地址](https://www.csdn.net/qc) 二&#xff0c;高分秘籍1&#xff0c;要有目录2&#xff0c;文章长度要足够&#xff0c;我的经验是汉字加代码至少1000字。3&#xff0…

币旺BitonAI系统助力智能化交易 引领加密资产交易行业革新

随着加密货币市场的蓬勃发展&#xff0c;交易者们面临着市场波动、信息过载和日益激烈的竞争等多重挑战。在这样的背景下&#xff0c;智能化交易系统应运而生。AI技术的引入无疑为加密货币交易市场带来了一场革命性的变革。通过深度学习和大数据分析&#xff0c;加密货币交易成…

手机怎么自动切换ip地址

在数字化时代&#xff0c;网络IP地址不仅是设备在网络世界的标识&#xff0c;也是确保用户网络安全和数据隐私的关键因素。对于手机用户来说&#xff0c;在某些情境下可能需要自动切换IP地址&#xff0c;本文将为您介绍手机怎么自动切换IP地址。 随着网络技术的发展&#xff0c…

Claude 3.5 强势出击:解析最新AI模型的突破与应用

近年来&#xff0c;人工智能领域的发展迅猛&#xff0c;各大科技公司纷纷推出了自家的高级语言模型。在这场技术竞赛中&#xff0c;Anthropic的Claude系列模型凭借其强大的性能和创新的功能脱颖而出。最近&#xff0c;Anthropic发布了Claude 3.5 Sonnet模型&#xff0c;引起了广…

Vue73-命名路由

一、路由的name属性 二、小结

【机器学习】正则卷积群理论及Python代码实现

1. 引言 1.1.卷积神经网络CNN 卷积神经网络&#xff08;CNN&#xff09;的数学模型是深度学习中用于处理图像和其他高维数据的关键组成部分。那么&#xff0c;CNN究竟是什么呢&#xff1f; 总结起来&#xff0c;CNN网络主要完成以下操作&#xff1a; 卷积操作&#xff08;Co…

使用Naive UI的级联选择器 Cascader进行省市区选择

序言&#xff1a; 在进行PC版的功能开发时&#xff0c;进行客户管理时老板要让客户便捷的选择自己的省市区等信息&#xff0c;而不是让他们一个个去填写&#xff0c;这时就需要使用级联选择器来进行省市区的选择。 注&#xff1a;element ui/plus的级联选择器也是可以的。 步骤…

Master PDF Editor v5 解锁版安装教程(小巧多功能PDF )

前言 Master PDF Editor&#xff0c;小巧的多功能PDF编辑器&#xff0c;轻松查看&#xff0c;创建&#xff0c;修改&#xff0c;批注&#xff0c;签名&#xff0c;扫描&#xff0c;OCR和打印PDF文档。高级注释工具&#xff0c;可以添加任意便笺指示对象突出显示&#xff0c;加…

Redis单例部署

目录 1. 概述2. 参考3. 环境4. 部署4.1 操作系统4.1.1 修改系统参数4.1.2 关闭透明大页内存4.1.3 修改系统限制 4.2 安装Redis4.2.1 下载Redis4.2.2 创建redis账号4.2.3 添加Redis环境变量4.2.4 创建Redis使用目录4.2.5 安装Redis4.2.6 手动修改配置文件&#xff08;**可跳过&a…

Linux连接工具MobaXterm详细使用教程

目录 一、MobaXterm的下载 1、访问官网 2、下载便携版 3、启动MobaXterm 二、MobaXterm基本使用设置 1、新建会话 2、使用ssh连接第一个会话 3、设置主密码 4、主界面 5、sftp文件上传下载 6、文件拖拽的上传下载 7.右键粘贴 8、查看服务器监测信息​编辑 9、个…

在Linux下使用CMake加载自定义路径第三方库的指南

CMake是一个强大的跨平台构建系统&#xff0c;广泛应用于C项目中。它不仅能够处理标准的构建过程&#xff0c;还可以灵活地集成各种第三方库&#xff0c;包括自定义路径的库、已编译的共享库&#xff08;.so 文件&#xff09;&#xff0c;以及仅包含头文件的库&#xff08;如Ei…

qt 简单实验创建一个可以拖拽和缩放的矩形

1.概要 2.代码 2.1 resizablewidget.h #ifndef RESIZABLEWIDGET_H #define RESIZABLEWIDGET_H#include <QWidget> #include <QMouseEvent>class ResizableWidget: public QWidget {Q_OBJECT public:ResizableWidget(QWidget *parent nullptr); protected:void m…

IPD笔记

IPD笔记 先弄一个一图流&#xff0c;改天再过来继续补充 IPD&#xff08;Integrated Product Development&#xff09;即集成产品开发&#xff0c;是一套产品开发的模式、理念与方法。华为的IPD的核心思想是基于市场需求&#xff0c;将产品开发作为一项投资来管理&#xff0c;以…

【复旦邱锡鹏教授《神经网络与深度学习公开课》笔记】卷积

卷积经常用在信号处理中&#xff0c;用于计算信号的延迟累积。假设一个信号发射器每个时刻 t t t产生一个信号 x t x_t xt​&#xff0c;其信息的衰减率为 w k w_k wk​&#xff0c;即在 k − 1 k-1 k−1个时间步长后&#xff0c;信息为原来的 w k w_k wk​倍&#xff0c;时刻 …

vivado、vitis2022安装及其注意事项(省时、省空间)

1、下载 AMD官网-资源与支持-vivado ML开发者工具&#xff0c;或者vitis平台&#xff0c; 下载的时候有个官网推荐web安装&#xff0c;亲测这个耗时非常久&#xff0c;不建议使用&#xff0c;还是直接下载89G的安装包快。 注意&#xff1a;安装vitis平台会默认安装vivado&…

速卖通测评成本低见效快,自养号测评的实操指南,快速积累销量和好评

对于初入速卖通的新卖家而言&#xff0c;销量和评价的积累显得尤为关键。由于新店铺往往难以获得平台活动的青睐&#xff0c;因此流量的获取成为了一大挑战。在这样的背景下&#xff0c;进行产品测评以积累正面的用户反馈和销售记录&#xff0c;成为了提升店铺信誉和吸引潜在顾…

Mybatis框架的缓存

Mybatis框架的缓存 一.为什么使用缓存 缓存(cache&#xff09;的作用是为了减去数据库的压力&#xff0c;提高查询性能。缓存实现的 原理是从数据库中查询出来的对象在使用完后不要销毁&#xff0c;而是存储在内存&#xff08;缓存&#xff09; 中&#xff0c;当再次需要获取…

算法设计与分析:动态规划法求扔鸡蛋问题 C++

目录 一、实验目的 二、问题描述 三、实验要求 四、算法思想和实验结果 1、动态规划法原理&#xff1a; 2、解决方法&#xff1a; 2.1 方法一&#xff1a;常规动态规划 2.1.1 算法思想&#xff1a; 2.1.2 时间复杂度分析 2.1.3 时间效率分析 2.2 方法二&#xff1a;动态规划加…

Excel导出实例

在上一节的基础上&#xff0c;本文演示下如何导出excel数据。 Excel导出操作演示 继承ocean-easyexcel SDK <dependency><groupId>com.angel.ocean</groupId><artifactId>ocean-easyexcel</artifactId><version>1.0.0</version> …