顺序表讲解

一.数据结构

在学习顺序表之前,我们先需要了解什么是数据结构。

1.什么是数据结构呢?

数据结构是由“数据”和结构两词组合而来。

什么是数据呢?

你的游戏账号,身份信息,网页里的信息(文字,视频,图片),手机app存储的信息等等这些都是数据。

什么是结构呢?

在生活中,我们是有很多数据的,当我们想要使用大量的同一类型的数据的时候,通过手动定义大量的独立变量对于程序员的消耗是很大的,并且可读性非常的查,无法轻易的实现查找数据,排列数据等基本功能。可读性非常差,我们可以借助数组这样的数据结构将大量的数据组织在一起,结构也可以理解为组织这样结构的方式。

数据结构是计算机存储,组织数据的方式。数据结构是指相互之间存在一种或者多种特定关系的数据元素的集合。数据结构反应数据内部的构成,即数据由那部分构成,以什么方式构成,以及数据元素之间呈现的结构。

总结:1.能够存储数据(比如顺序表,链表等结构)2.存储的数据方便查找。

2.为什么需要数据结构?

假设在餐馆中,如果我们不使用排队的方式来管理客户点餐,会导致客户就餐感受差,等餐时间长,餐厅营业混乱等情况。同理在程序中,如果不对数据进行管理,可能会导致数据丢失,数据管理困难等问题。所以我们需要通过数据结构,能够将有效数据组织和管理在一起。按照我们的方式任意对数据进行增删改查等操作。

3.最基础的数据结构:数组。

可是数组有一个极大的缺陷,就是在我们声明数组的时候,数组的大小需要固定,可是我们的数据的多少却是无法确定的,数组过大,会导致空间浪费,数组过小,会导致数据丢失。

也就是最基础的数据结构能够提供的操作已经不能完全满足复杂算法的实现了。
 

二.顺序表 

1.顺序表的概念以及结构

1.1线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表,链表,栈,队列,字符串,数组......

线性表在逻辑上是一定是线性结构,也就是连续的一条直线。但是在物理结构上不一定是连续的。

线性表在物理上存储时,通常以数组和链式结构的形式存储。

案例:蔬菜分为绿叶类,瓜类,菌菇类。线性表指的是具有部分相同特性的一类数据结构的集合。

2.顺序表分类

2.1顺序表和数组的区别

顺序表的底层结构式数组,对数组的封装,实现了常用的增删改查等接口

2.2分类

1.静态顺序表

概念:使用定长数组存储元素

typedef int SLDataType;
#define N 7
struct SeqList {
	SLDataType arr[N];//定长数组
	int size;//存储的有效数据个数
};

typedef struct SeqList SL;

这样的顺序表其实和数组类似:空间少了不够使用,多了会造成空间浪费

2.动态顺序表
struct SeqList {
	SLDataType *arr;//按需求申请空间
	int size;//存储的有效数据个数
	int capacity;//顺序表实际空间容量
};

typedef struct SeqList SL;

什么是按需申请空间呢?

这时候就需要用到我们的malloc函数申请空间用于我们存储数据,如果空间不够了,我们再使用realloc函数对空间进行扩容。 

这样的顺序表就非常的方便了

3.顺序表的实现

#pragma once
#define INIT_CAPACITY 4
typedef int SLDataType;
// 动态顺序表 -- 按需申请
typedef struct SeqList
{
	SLDataType* a;
	int size; // 有效数据个数
	int capacity; // 空间容量
}SL;
//初始化和销毁
void SLInit(SL* ps);//初始化
void SLDestroy(SL* ps);//销毁
void SLPrint(SL* ps);//打印数据
//扩容
void SLCheckCapacity(SL* ps);
//头部插⼊删除 / 尾部插⼊删除
void SLPushBack(SL* ps, SLDataType x);//尾插
void SLPopBack(SL* ps);//尾删
void SLPushFront(SL* ps, SLDataType x);//头插
void SLPopFront(SL* ps);//头删
//指定位置之前插⼊/删除数据
void SLInsert(SL* ps, int pos, SLDataType x);//插入
void SLErase(SL* ps, int pos);//删除
int SLFind(SL* ps, SLDataType x);//查找

我们自己实现顺序表的基础就是完成以上所有函数 。

 1.首先我们先完成初始化函数。

void SLInit(SL* ps);//初始化

	SL ps;
	SLInit(&ps);

首先我们来思考一个问题:为什么在初始化函数中我们使用的是SL*类型的指针作为参数?

这是因为如果是使用SL作为参数的话,我们在函数中的任何操作并不会影响实参,形参只是实参的临时拷贝,改变形参并不会影响实参,所以为了改变实参,我们就需要使用一个指向实参的指针。这里我们为了改变这个结构体ps,我们就需要使用一个指向ps的指针。

然后我们再来思考这个初始化函数应该怎么写。

顺序表这个结构体有三个成员变量,对结构体进行初始化,无非就是对这个三个成员变量进行初始化。

顺序表开始是没有存储任何数据的,所以很明显这里的size就是0了.

观察上面的头文件,有着这样一句代码

typedef int SLDataType;

顺序表是可以储存不同种类的数据的,所以我们在使用的时候不能将数据的类型定死了,这是需要根据情况进行改变的,但是我们在实际开发中是不可能对每一个类型一个一个改变的,所以为了方便,我们将我们要存储的类型重命名为SLDataType这样如果我们需要存储其他类型的数据直接进行改变重命名的对象即可。

这也是为什么结构体的指针是SLDatatype*类型的原因了。

我们此时的顺序表是没有储存任何数据的,我们还需要为其开辟空间吗?如果要,那么开辟多大的空间合适呢?

实际上,开不开辟都可以,这个看自己即可,如果初始就开辟会更加方便,这样不用在后续使用的过程中判断空间是否为0了。

第二个问题开辟多大的空间合适呢?

这个是要看具体的实际情况的,这里我们是自己写,不会用到太多的数据,我们就开辟4个元素大小空间即可。

#define INIT_CAPACITY 4

如果觉得太小直接将4进行更改即可。

因为我们要开辟4个空间,所以此时顺序表的容量就是4了。这时候我们就完成了顺序表的初始化了.

void SLInit(SL* ps) {//初始化
	ps->a = (SLDataType*)malloc(sizeof(SLDataType) * 4);
	ps->size = 0;
	ps->capacity = 4;
};

2.完成插入数据函数 

 接下来就是如何插入元素了

这里解释一下头插尾插什么意思,因为顺序表的本质上是一个数组,只不过这个数组大小是可变的,头插就是在这个顺序表的最前面插入一个数据,尾插就是在顺序表的末尾插入一个数据.头删尾删同理就是删除数据了.

本质上这四种函数其实都是一样的,为了本文的简便性,我们先完成指定位置插入函数和指定位置删除函数,然后再使用这两个函数完成其他函数,也是为了方便大家理解.

void SLInsert(SL* ps, int pos, SLDataType x);

第一个参数就是顺序表结构体的指针了,第二个参数就是指定位置的下表了(position),第三个参数就是我们需要插入的元素.

但是我们需要考虑一个问题,如果顺序表容量不够了怎么办,此时我们是不能进行插入的,否则就造成了数据的丢失,这是非常重大的事故.

所以我们在完成插入函数之前我们需要判断容量是否够 .所以我们可单独再定义一个函数完成此功能.

void SLCheckCapacity(SL* ps) ;

 什么时候顺序表的空间不够了呢?

我们要存储的数据大于空间容量就不够了,也就是ps->size > ps->capacity的时候就空间不足了,但是我们存储数据的时候是一个一个进行存储的,也就是说其实两者相等的时候就已经不够了,因为此时我们已经没有办法存储数据了,所以需要realloc函数进行扩容处理.

此时我们又遇到了一个问题:这个空间扩大到多大合适呢?是加1还是加2呢?

如果我们扩容的空间较小,这时候我们就会反复调用这个函数,此时函数压栈会浪费很多性能和时间

程序效率就会低下,所以最好是扩大2倍或者3倍(这个是有数学推导出来的).

void SLCheckCapacity(SL* ps) {//检测空间大小
	assert(ps);
	if (ps->capacity == ps->size) {
        ps->a = realloc(ps->a,sizeof(SLDataType) * ps->capacity * 2);
	}
};

此时我们就完成了扩容操作.

但是

这样写对不对呢?

我们知道realloc函数,如果开辟失败是会返回NULL的,如果发生这样的情况,ps->a就变成了NULL,我们不仅开辟失败了,还丢失了访问原来开辟空间的权力,造成了内存泄漏的情况,同时我们也丢失了所有的数据.

所以我们需要定义一个临时变量,来储存realloc函数的返回值,如果返回值不为NULL,我们再把返回值传给ps->a.这样就避免了这样的情况.

以为这样就完了吗?

其实还有一个错误.我们进行了扩容,但是我们并没有改变容量大小,这样也会造成数据丢失的情况,所以容量也要*2.

完整版:

void SLCheckCapacity(SL* ps) {//检测空间大小
	assert(ps);
	if (ps->capacity == ps->size) {
		SLDataType* tmp = realloc(ps->a,sizeof(SLDataType) * ps->capacity * 2);
		if (tmp == NULL) {
			perror("realloc");
			exit(1);
		}
			ps->a = tmp;
			ps->capacity *= 2;
	}
};

完成了这些我们再来看插入函数.

假设这里有这样一个顺序表,储存着这样的数据1 2 3 4 5 ... 9

如果我们要在下标为6的位置之前插入一个数据10,也就是在数据6前面插入一个数据10,我们需要怎么完成呢?

首先我们需要对原有的数据进行移动,避免数据丢失了.依次将后面的数据向后移动一位.然后再在下标为5的位置写入数字10, 

 	for (int i = ps->size; i > pos; i--){
		ps->a[i] = ps->a[i - 1];
	}
	ps->a[pos] = x;

我们是从下标为size的位置开始的,所以i从ps->size开始,直到pos的后一位结束.

再移动完了后直接更改即可.

但是这里还有一个错误,也是一个很容易犯错的地方.

我们在插入一个数据后,有效数据的个数其实是变化了的,所以要加1,同理删除要减1,这个一定不能忘了.

完整版:

void SLInsert(SL* ps, int pos, SLDataType x) {//在指定位置之前插入
	assert(ps);
	assert(pos <= ps->size && pos >= 0);
	SLCheckCapacity(ps);
 	for (int i = ps->size; i > pos; i--){
		ps->a[i] = ps->a[i - 1];
	}
	ps->a[pos] = x;
	ps->size++;
};

3.完成删除数据函数

 还是上图的顺序表,如果我们要删除下标为5位置上的数据,我们需要将后面的数据向前挪动一位.

void SLErase(SL* ps, int pos) {//删除指定位置的数据
	assert(ps);
	assert(ps->size > pos && pos >= 0);
	while (pos < ps->size) {
		ps->a[pos] = ps->a[pos + 1];
		pos++;
	}
	ps->size--;
};

这里之所以要断言是因为要删除的下标必须要小于有效数据的个数,并且大于等于0,不然我们删除的就不是有效数据,这是没有意义的,而且可能会非法访问,出现越界的情况.

不要忘了结束要size--;

4.查找函数

int SLFind(SL* ps, SLDataType x);

x就是我们需要查找的数据,如果找到了函数返回下标,否则返回-1;

我们只需要遍历一遍顺序表即可完成。非常简单。

int SLFind(SL* ps, SLDataType x) {//查找
	assert(ps);
	for (int i = 0; i < ps->size; i++) { 
		if (ps->a[i] == x) {
			return i;
		}
	}
	return -1;
};

5.头插/删,尾插/删 

这个是基于我们前面写的指定位置插入和删除函数写的,这里就不过多赘述。 

void SLPushBack(SL* ps, SLDataType x) {
	SLInsert(ps,ps->size,x);
};
void SLPopBack(SL* ps) {
	SLErase(ps,ps->size-1);
};
void SLPushFront(SL* ps, SLDataType x) {
	SLInsert(ps,0,x);
};
void SLPopFront(SL* ps) {
	SLErase(ps,0);
};

这样写其实是有好处的,更加便捷,而且不用考虑顺序表是否为空等其他情况。建议读者自行完成这四个函数,不使用指定位置的函数做跳板,可以更加理解顺序表。 

6.最后就是销毁顺序表

void SLDestroy(SL* ps) {//销毁顺序表
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->size = 0;
};

ps->a是我们动态开辟的内存,所以最后需要释放掉,并且将指针置为NULL,避免非法访问的情况,最后再将有效数据个数和顺序表容量置为0即可。

4.通讯录

学完了顺序表,我们就可尝试使用顺序表完成一个简单的通讯录了,这个我并没有讲解,因为比较简单,会了顺序表的同学都可以看懂。 以下是代码实现:

//SeqList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include"contact.h"
#define INIT_CAPACITY 4
typedef PeoInfo SLDataType;
// 动态顺序表 -- 按需申请
typedef struct SeqList
{
	SLDataType* a;
	int size; // 有效数据个数
	int capacity; // 空间容量
}SL;
//初始化和销毁
void SLInit(SL* ps);
void SLDestroy(SL* ps);
//void SLPrint(SL* ps);
//扩容
void SLCheckCapacity(SL* ps);
//头部插⼊删除 / 尾部插⼊删除
void SLPushBack(SL* ps, SLDataType x);
void SLPopBack(SL* ps);
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);
//指定位置之前插⼊/删除数据
void SLInsert(SL* ps, int pos, SLDataType x);
void SLErase(SL* ps, int pos);
int SLFind(SL* ps, SLDataType x);
//SeqList.c

#define _CRT_SECURE_NO_WARNINGS 
#include"SeqList.h"
//typedef struct SeqList
//{
//	SLDataType* a;
//	int size; // 有效数据个数
//	int capacity; // 空间容量
//}SL;
void SLInit(SL* ps) {//初始化
	ps->a = (SLDataType*)malloc(sizeof(SLDataType) * 4);
	ps->size = 0;
	ps->capacity = 4;
};
void SLDestroy(SL* ps) {//销毁顺序表
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->size = 0;
};
//void SLPrint(SL* ps) {//打印顺序表
//	assert(ps);
//	int i = 0;
//	for (i = 0; i < ps->size; i++) {
//		printf("%d->",ps->a[i]);
//	}
//	printf("\n");
//};
void SLCheckCapacity(SL* ps) {//检测空间大小
	assert(ps);
	if (ps->capacity == ps->size) {
		SLDataType* tmp = realloc(ps->a,sizeof(SLDataType) * ps->capacity * 2);
		if (tmp == NULL) {
			perror("realloc");
			exit(1);
		}
			ps->a = tmp;
			ps->capacity *= 2;
	}
};
void SLInsert(SL* ps, int pos, SLDataType x) {//在指定位置之前插入
	assert(ps);
	assert(pos <= ps->size && pos >= 0);
	SLCheckCapacity(ps);
 	for (int i = ps->size; i>pos; i--){
		ps->a[i] = ps->a[i - 1];
	}
	ps->a[pos] = x;
	ps->size++;
};

void SLErase(SL* ps, int pos) {//删除指定位置的数据
	assert(ps);
	assert(ps->size > pos);
	while (pos < ps->size) {
		ps->a[pos] = ps->a[pos + 1];
		pos++;
	}
	ps->size--;
};
//int SLFind(SL* ps, SLDataType x) {//查找
//	assert(ps);
//	for (int i = 0; i < ps->size; i++) { 
//		if (ps->a->name == x.name) {
//			return i;
//		}
//	}
//};

void SLPushBack(SL* ps, SLDataType x) {
	SLInsert(ps,ps->size,x);
};
void SLPopBack(SL* ps) {
	SLErase(ps,ps->size-1);
};
void SLPushFront(SL* ps, SLDataType x) {
	SLInsert(ps,0,x);
};
void SLPopFront(SL* ps) {
	SLErase(ps,0);
};

 

//contact.h

#define _CRT_SECURE_NO_WARNINGS 

#pragma once
#define NAME_MAX 20
#define SEX_MAX 4
#define TEL_MAX 20
#define ADDR_MAX 100

typedef struct SeqList contact;
typedef struct PersonInfo
{
	char name[NAME_MAX];
	char sex[SEX_MAX];
	int age;
	char tel[TEL_MAX];
	char addr[ADDR_MAX];
}PeoInfo;

//初始化通讯录
void InitContact(contact* con);
//添加通讯录数据
void AddContact(contact* con);
//删除通讯录数据
void DelContact(contact* con);
//展⽰通讯录数据
void ShowContact(contact* con);
//查找通讯录数据
void FindContact(contact* con);
//修改通讯录数据
void ModifyContact(contact* con);
//销毁通讯录数据
void DestroyContact(contact* con);
//contact.c
#define _CRT_SECURE_NO_WARNINGS 
#include"SeqList.h"
#include<string.h>
//初始化通讯录
//typedef struct PersonInfo
//{
//	char name[NAME_MAX];
//	char sex[SEX_MAX];
//	int age;
//	char tel[TEL_MAX];
//	char addr[ADDR_MAX];
//}PeoInfo;
void loadContact(contact*con) {
	FILE* pf = fopen("contact.txt","rb");
	if (pf == NULL) {
		perror("fopen");
		return;
	}
	PeoInfo p;
	while (fread(&p, sizeof(p), 1, pf)) {
		SLPushBack(con,p);
	}
};


void InitContact(contact* con) {//初始化通讯录

	SLInit(con);
	loadContact(con);
};
//添加通讯录数据
void AddContact(contact* con) {
	PeoInfo person;
	printf("请输入联系人的姓名:\n");
	scanf("%s", person.name);

	printf("请输入联系人的性别:\n");
	scanf("%s", person.sex);

	printf("请输入联系人的年龄:\n");
	scanf("%d",&(person.age));

	printf("请输入联系人的电话:\n");
	scanf("%s", person.tel);

	printf("请输入联系人的地址:\n");
	scanf("%s", person.addr);

	SLPushBack(con, person);
	printf("添加成功\n");
};

//查找联系人是否存在
int findbyname(contact *con, char* name) {
	for (int i = 0; i < con->size; i++) {
		if (0 == strcmp(con->a[i].name, name)) {
			return i;
		}
	}
	return -1;
}

//删除通讯录数据
void DelContact(contact* con) {
	char name[NAME_MAX];
	printf("请输入要删除的联系人的名字:\n");
	scanf("%s",name);
	int pos = findbyname(con, name);
	if (pos >= 0) {
		SLErase(con,pos);
		printf("删除成功!!!\n");
	}
	else {
		printf("要删除的联系人不存在!!!\n");
		return;
	}
};
//展⽰通讯录数据
void ShowContact(contact* con) {
	for (int i = 0; i < con->size; i++) {
		printf("%-20s %-4s %-3d %-20s %-100s\n",con->a[i].name,\
			con->a[i].sex,\
			con->a[i].age,\
			con->a[i].tel,\
			con->a[i].addr);\
	}
};
//查找通讯录数据
void FindContact(contact* con) {
	char name[NAME_MAX];
	printf("请输入要查找的联系人的名字:\n");
	scanf("%s", name);
	int pos = findbyname(con, name);
	if (pos >= 0) {
		printf("查找成功!!!\n");
		printf("%-20s %-4s %-3d %-20s %-100s\n", con->a[pos].name, \
			con->a[pos].sex, \
			con->a[pos].age, \
			con->a[pos].tel, \
			con->a[pos].addr); \
	}
	else {
		printf("该联系人不存在!!!\n");
	}
}
//修改通讯录数据
void ModifyContact(contact* con) {
	char name[NAME_MAX];
	printf("请输入要修改的联系人的名字:\n");
	scanf("%s", name);
	int pos = findbyname(con, name);
	if (pos >= 0) {
		printf("请输入联系人的姓名:\n");
		scanf("%s", con->a[pos].name);

		printf("请输入联系人的性别:\n");
		scanf("%s", con->a[pos].sex);

		printf("请输入联系人的年龄:\n");
		scanf("%d", &(con->a[pos].age));

		printf("请输入联系人的电话:\n");
		scanf("%s", con->a[pos].tel);

		printf("请输入联系人的地址:\n");
		scanf("%s", con->a[pos].addr);
		printf("修改成功!\n");
	}
	else {
		printf("该联系人不存在!!!,修改失败!!!\n");
		
	}
};

void SaveContact(contact* con) {
	FILE* pf = fopen("contact.txt","wb");
	if (pf == NULL) {
		perror("fopen\n");
		return;
	}
	//可以一起性读取完
	fwrite(con->a, sizeof(PeoInfo), con->size, pf);
	//for (int i = 0; i < con->size; i++) {
	//	fwrite(con->a + i,sizeof(PeoInfo),1,pf);
	//}
	printf("数据保存成功!!!\n");
}

//销毁通讯录数据
void DestroyContact(contact* con) {
	SaveContact(con);
	SLDestroy(con);
};

 

//test.c
#define _CRT_SECURE_NO_WARNINGS 
#include"SeqList.h"
#include<windows.h>
//void test1() {//测试
//	SL ps;
//	SLInit(&ps);
//	SLPushBack(&ps,1);
//	SLPushBack(&ps,2);
//	SLPushBack(&ps,3);
//
//
//	SLPushFront(&ps,4);
//	SLPushFront(&ps,5);
//	SLPushFront(&ps, 2); 
//	SLPrint(&ps);
//	int find = SLFind(&ps,2);
//	SLErase(&ps,find);
//	SLPrint(&ps);
//};
void menu()
{
	contact con;
	InitContact(&con);
	int choice = -1;
	do {
		printf("---------------------------------------------\n");
		printf("--------1.添加联系人 2.删除联系人------------\n");
		printf("--------3.查找联系人 4.修改联系人------------\n");
		printf("-----------5.展示所有联系人信息--------------\n");
		printf("----------------0.退出-----------------------\n");
		printf("---------------------------------------------\n");
		printf("输入选项:\n");
		scanf("%d",&choice);
		system("cls");
		switch(choice){
	
		case 1:
			AddContact(&con);
			break;
		case 2:
			DelContact(&con);
			break;
		case 3:
			FindContact(&con);
			break;
		case 4:
			ModifyContact(&con);
			break;
		case 5:
			ShowContact(&con);
			break;
		default:
			printf("输入有误,请重新输入!!!\n");
			break;
		};
		
	} while (choice);
	DestroyContact(&con);

};

int main()
{
	//test1();
	menu();

	return 0;
}

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

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

相关文章

onSaveInstanceState()与onRestoreInstanceState()

目录 1.二者作用 2.onSaveInstanceState调用时机 2.1 五种情况 前4种情况Activity生命周期&#xff1a; 2.2 注意事项&#xff1a;确定会被系统回收并销毁&#xff0c;不会调用此方法 两个例子 3.onRestoreInstanceState调用时机 3.1实例——屏幕切换生命周期 3.2 极端…

Python实现读取dxf文件的所有字符

Python实现读取dxf文件的所有字符 import ezdxfdef read_dxf_and_print_text(filename):# 加载DXF文件doc ezdxf.readfile(filename)# 遍历所有的实体for entity in doc.entities:# 检查实体是否是TEXT、MTEXT或DIMENSIONif isinstance(entity, ezdxf.entities.Text):print(f…

初识--数据结构

什么是数据结构&#xff1f;我们为什么要学习数据结构呢....一系列的问题就促使我们不得不了解数据结构。我们不禁要问了&#xff0c;学习C语言不就够了吗&#xff1f;为什么还要学习数据结构呢&#xff1f;这是因为&#xff1a;数据结构能够解决C语言解决不了的问题&#xff0…

Unity多线程简单示例

using UnityEngine; using System.Threading;public class texxxst : MonoBehaviour {Thread thread;void Start(){// 创建一个新的线程&#xff0c;并传入要执行的方法thread new Thread(new ThreadStart(DoWork));// 启动线程thread.Start();}void DoWork(){for (int i 0; …

数据降维方法-主成分分析(PCA)

目录 一、前言 二、向量的表示及基变换 三、基变换 四、协方差矩阵 五、协方差 六、优化目标 一、前言 主成分分析(Principal Component Analysis) 用途&#xff1a;降维中的常用手段 目标&#xff1a;提取最有价值的信息&#xff08;基于方差&#xff09; 问题&#x…

【项目精讲】RESTful简洁描述

RESTful是什么 是一种架构风格/API设计规范将一切数据视为资源利用HTTP请求方式 POST、GET、PUT、DELETE&#xff0c;描述对资源的操作 GET 获取资源POST 新建资源PUT 更新资源DELETE 删除资源 通过HTTP响应状态码&#xff0c;描述对资源的操作结果请求数据和英大数据均为JSO…

YOLOv8模型剪枝实战:DepGraph(依赖图)方法

课程链接&#xff1a;YOLOv8模型剪枝实战&#xff1a;DepGraph(依赖图)方法_在线视频教程-CSDN程序员研修院 YOLOv8是一个当前非常流行的目标检测器&#xff0c;本课程使用DepGraph&#xff08;依赖图&#xff09;剪枝方法对YOLOv8进行网络剪枝&#xff0c;使其更加轻量和实用…

SL4010 低压升压恒压芯片 2.7-24V输入 输出30V/10A 300W功率

SL4010是一款高效能、宽电压范围的低压升压恒压芯片&#xff0c;其卓越的性能和广泛的应用领域使其在市场上备受瞩目。该芯片支持2.7-24V的宽输入电压范围&#xff0c;能够提供稳定的30V/10A输出&#xff0c;最大输出功率高达300W&#xff0c;为各种电子设备提供稳定可靠的电源…

【动态规划-线性dp】【蓝桥杯备考训练】:乌龟棋、最长上升子序列、最长公共子序列、松散子序列、最大上升子序列和【已更新完成】

目录 1、乌龟棋 2、最长上升子序列 3、最长公共子序列 4、松散子序列 5、最大上升子序列和 1、乌龟棋 小明过生日的时候&#xff0c;爸爸送给他一副乌龟棋当作礼物。 乌龟棋的棋盘只有一行&#xff0c;该行有 N 个格子&#xff0c;每个格子上一个分数&#xff08;非负整…

【科研入门】搭建与配置云服务器的论文环境

如何搭建云服务器并且配置基础论文代码环境&#xff1f; 目录 如何搭建云服务器并且配置基础论文代码环境&#xff1f;前言一、租用服务器并选定基础配置具体建议 二、容器初始化三、项目的具体配置四、克隆五、常见问题 前言 此次环境配置以AutoDL为例(可以选用其他服务器租用…

PS入门|学PS一定要先知道图层这玩意儿

前言 开始学习PS的小伙伴肯定是会遇到很多问题&#xff0c;最常见的莫过于为啥我调整了某些参数之后&#xff0c;并没有任何作用。 这个就涉及到图层的问题了。 学PS一定要知道&#xff0c;图层面板怎么看。 正文开始 首先咱们讲的图层面板基本上是在PS里100%会用到的功能。…

jfinal项目操作数据库DB.find()空指针异常问题解决

排查ActiveRecordPlugin是否配置启动成功 查看项目的配置 里面这个别注释了 这样就能和数据库交互了

acwing总结-线性质数筛

质数筛 题目链接:质数筛线性筛法 ac代码&#xff1a; #include<iostream> #include<algorithm> //https://www.bilibili.com/video/BV1LR4y1Z7pm/?spm_id_from333.337.search-card.all.click&vd_source436ccbb3a8f50110aa75654f38e35672 //链接到b站视频 us…

c++ stringstream(老好用了)

前言&#xff1a; 以前没有接触过stringstream这个类的时候&#xff0c;常用的字符串和数字转换函数就是sscanf和sprintf函数。开始的时候就觉得这两个函数应经很叼了&#xff0c;但是毕竟是属于c的。c中引入了流的概念&#xff0c;通过流来实现字符串和数字的转换方便多了。在…

【数据结构】-- 单链表 vs 双向链表

&#x1f308; 个人主页&#xff1a;白子寰 &#x1f525; 分类专栏&#xff1a;python从入门到精通&#xff0c;魔法指针&#xff0c;进阶C&#xff0c;C语言&#xff0c;C语言题集&#xff0c;C语言实现游戏&#x1f448; 希望得到您的订阅和支持~ &#x1f4a1; 坚持创作博文…

基于SSM+Jsp+Mysql的二手车交易网站

开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包…

浏览器工作原理与实践--虚拟DOM:虚拟DOM和实际的DOM有何不同

虚拟DOM是最近非常火的技术&#xff0c;两大著名前端框架React和Vue都使用了虚拟DOM&#xff0c;所以我觉得非常有必要结合浏览器的工作机制对虚拟DOM进行一次分析。当然了&#xff0c;React和Vue框架本身所蕴含的知识点非常多&#xff0c;而且也不是我们专栏的重点&#xff0c…

WPS 365开启企业一站式AI办公;联发科推出生成式AI服务平台;马斯克:AI 明年或超越人类智力

WPS 365 升级一站式 AI 办公 昨日&#xff0c;金山办公生产力大会举行&#xff0c;现场发布了面向组织和企业的办公新质生产力平台 WPS 365&#xff0c;其包含升级的 WPS Office、最新发布的 WPS AI 企业版和 WPS 协作。 WPS 365 将能覆盖一个组织日常办公基本需求&#xff0c…

加密软件如何给文件加密

加密软件如何给文件加密 市面上有很多给文件加密的软件&#xff0c;它是如何给文件加密的呢&#xff1f;加密过程是否安全&#xff0c;效果是否理想呢&#xff1f;下面以安企神软件为例给大家介绍 给文件加密的关键步骤和技术。 1、选择加密算法 安企神软件可能会采用国际公…

设计模式之迭代器模式(上)

迭代器模式 1&#xff09;概述 1.概念 存储多个成员对象&#xff08;元素&#xff09;的类叫聚合类(Aggregate Classes)&#xff0c;对应的对象称为聚合对象。 聚合对象有两个职责&#xff0c;一是存储数据&#xff0c;二是遍历数据。 2.概述 迭代器模式(Iterator Patter…