数据结构:栈和队列

朋友们、伙计们,我们又见面了,本期来给大家解读一下栈和队列方面的相关知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!

C 语 言 专 栏:C语言:从入门到精通

数据结构专栏:数据结构

个 人 主 页 :  stackY、

目录

前言: 

1.栈

1.1栈的概念及结构

1.2栈的实现

1.2.1栈的创建

1.2.2栈的初始化

1.2.3入栈

1.2.4检测栈是否为空

1.2.5出栈 

1.2.6获取栈顶元素

1.2.7获取栈中有效元素的个数

1.2.8销毁栈

1.2.9访问栈

1.3栈的完整代码

2.队列 

2.1队列的概念及结构

2.2队列的实现

2.2.1队列的创建

2.2.2队列的初始化

2.2.3队尾入队列

2.2.4检测队列是否为空

2.2.5队头出队列

2.2.6获取队头数据

2.2.7获取队尾数据

2.2.8获取队列有效元素个数

2.2.9销毁队列

2.2.10访问队列

2.3队列完整代码


前言: 

在之前的顺序表中我们提到过线性表:线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的, 线性表在物理上存储时,通常以数组和链式结构的形式存储。
顺序表的实现我们使用的是数组,链表的实现我们使用的是链式结构,那么关于栈和队列又该怎么样实现呢?话不多说,直接开始:

1.栈

1.1栈的概念及结构

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。
进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。
栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶
出栈:栈的删除操作叫做出栈。出数据也在栈顶

1.2栈的实现

我们以STL为模板来实现栈,因此栈的功能也就分为:初始化、销毁、入栈、出栈、获取栈顶元素、获取栈中有效元素个数、检测栈是否为空。

栈的实现还是和之前的数据结构的实现一样分模块来写, 当然也是可以在一个源文件中完成的,当然小编还是推荐大家分模块来写,先创建两个源文件:Test.c和Stack.c,再创建一个头文件:Stack.h,这里的文件命名不做要求,表示的有意义就行。同样的我们在Test.c中实现基本逻辑,在Stack.c中实现栈的细节,在Stack.h中进行函数声明和头文件包含等,话不多说,我们直接开始:

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为我们的入栈和出栈都是在尾部进行操作,如果使用链表的话,在进行入栈的时候还得找尾,在出栈的时候还得找尾的前一个,代价有点大,所以选择数组,因为数组在尾上插入数据的代价比较小。

1.2.1栈的创建

栈的创建有两种方式:

1.静态栈

头文件:Stack.h

//静态栈
typedef int STDataType;
#define N 10
typedef struct Stack
{
	STDataType _a[N];
	int _top; // 栈顶
}Stack;

2.动态栈

头文件:Stack.h

//动态栈
typedef int STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;        //栈顶
	int capacity;  //栈的容量
}Stack;

由于静态栈不常用也不实用,所以在这里我们就来使用动态栈:

1.2.2栈的初始化

在创建栈的时候栈中有一个指针,栈顶,和栈的容量,因此我们要将这些数据初始化一下,先将指针置为NULL,再将容量置为0,这里的栈顶初始化就有两种说法了:

1.如果将top置为0的话,那么在插入数据的时候是先插入然后top++,在插入完之后top始终指向的是最后一个数据的下一个位置。

2.如果将top置为-1,那么就要先top++然后再插入数据,再插入完之后top指向的是最后一个数据的位置。

两种方式都可行,但是将top置为0在后面的操作中会比较方便。

头文件:Stack.h

//对栈的初始化
void StackInit(Stack* pst);

源文件:Stack.c

//对栈的初始化
void StackInit(Stack* pst)
{
	assert(pst);
	pst->a = NULL;
	//top为-1时,插入一个数据之后top指向的是刚刚插入数据的位置
	//pst->top = -1     
	//top为0时,插入一个数据之后top指向的是刚刚插入数据后面的位置
	pst->top = 0;
	pst->capacity = 0;
}

注意:这里传参为什么不用二级指针,因为我们改变的是结构体成员,使用结构体指针就完全OK。

1.2.3入栈

入栈也叫做插入数据,先要检测容量,如果top等于容量,那么就需要扩容,然后将数据插入到top的位置,然后top++:

头文件:Stack.h

//入栈
void StackPush(Stack* pst, STDataType x);

源文件:Stack.c

//入栈
void StackPush(Stack* pst, STDataType x)
{
	assert(pst);
	//检测容量
	if (pst->top == pst->capacity)
	{
		int NewCapacity = pst->capacity == 0 ? 4 : 2 * pst->capacity;
		//当pst->a为NULL时执行的功能是和malloc一样
		STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * NewCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = NewCapacity;
	}
	//入栈
	pst->a[pst->top] = x;
	pst->top++;
}

1.2.4检测栈是否为空

检测栈是否为空:就是检测栈顶是否为0,如果栈顶top为0,则为空,栈为空返回true,不为空返回false:

头文件:Stack.h

//检测栈是否为空
bool StackEmpty(Stack* pst);

源文件:Stack.c

//检测栈是否为空
bool StackEmpty(Stack* pst)
{
	assert(pst);
	/*if (pst->top == 0)
	{
		return true;
	}
	else
	{
		return false;
	}*/
	return pst->top == 0;
}

1.2.5出栈 

出栈就比较简单了,直接将top--,但是这里还存在一个问题,如果栈为空,那么就不能再出栈,因此我们需要对栈进行判空:

头文件:Stack.h

//出栈
void StackPop(Stack* pst);

源文件:Stack.c

//出栈
void StackPop(Stack* pst)
{
	assert(pst);
	//判断栈是否为空
	assert(!StackEmpty(pst));
	//出栈
	pst->top--;
}

1.2.6获取栈顶元素

获取栈顶元素就是将数组中下标为top-1的元素返回,并且在返回前先要判断栈中是否有无数据:

头文件:Stack.h

//获取栈顶元素
STDataType StackTop(Stack* pst);

源文件:Stack.c

//获取栈顶元素
STDataType StackTop(Stack* pst)
{
	assert(pst);
	assert(!StackEmpty(pst));
	//top指向的是栈顶的下一个位置的元素
	return pst->a[pst->top-1];
}

1.2.7获取栈中有效元素的个数

栈中有效元素的个数就是top,只需要将top直接返回即可:

头文件:Stack.h

//获取栈中的有效元素的个数
int StackSize(Stack* pst);

源文件:Stack.c

//获取栈中的有效元素的个数
int StackSize(Stack* pst)
{
	assert(pst);

	return pst->top;
}

1.2.8销毁栈

由于我们创建的是动态的栈,因此在程序结束之后我们需要将动态开辟的空间释放掉,然后将top和capacity都置为0:
头文件:Stack.h

//销毁栈
void StackDestroy(Stack* pst);

源文件:Stack.c

//销毁栈
void StackDestroy(Stack* pst)
{
	assert(pst);

	//释放
	free(pst->a);
	pst->a = NULL;
	//重置为0
	pst->top = pst->capacity = 0;
}

1.2.9访问栈

上面的这些接口都比较简单,那么在实现上面的接口之后怎么访问栈呢?我们可以通过循环来进行访问,循环的判断条件就是栈不为空,然后打印栈顶的元素,然后再删除栈顶的元素,依次类推,就可以访问栈中的全部元素了:

源文件:Test.c

#include "Stack.h"

void TestStack()
{
	Stack st;
	//初始化
	StackInit(&st);

	//入栈
	StackPush(&st, 1);
	StackPush(&st, 2);
	StackPush(&st, 3);
	StackPush(&st, 4);

	//获取栈中的有效元素的个数
	printf("Size:%d\n", StackSize(&st));

	//访问栈的元素
	while (!StackEmpty(&st))
	{
		//打印栈顶的数据
		printf("%d ", StackTop(&st));
		//出栈
		StackPop(&st);
	}

	//销毁栈
	StackDestroy(&st);
}

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

1.3栈的完整代码

头文件:Stack.h

#pragma once

//   栈的实现

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

//动态栈
typedef int STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;        //栈顶
	int capacity;  //栈的容量
}Stack;


//对栈的初始化
void StackInit(Stack* pst);

//入栈
void StackPush(Stack* pst, STDataType x);

//出栈
void StackPop(Stack* pst);

//获取栈顶元素
STDataType StackTop(Stack* pst);

//获取栈中的有效元素的个数
int StackSize(Stack* pst);

//检测栈是否为空
bool StackEmpty(Stack* pst);

//销毁栈
void StackDestroy(Stack* pst);

源文件:Stack.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "Stack.h"

//对栈的初始化
void StackInit(Stack* pst)
{
	assert(pst);
	pst->a = NULL;
	//top为-1时,插入一个数据之后top指向的是刚刚插入数据的位置
	//pst->top = -1     
	//top为0时,插入一个数据之后top指向的是刚刚插入数据后面的位置
	pst->top = 0;
	pst->capacity = 0;
}

//入栈
void StackPush(Stack* pst, STDataType x)
{
	assert(pst);
	//检测容量
	if (pst->top == pst->capacity)
	{
		int NewCapacity = pst->capacity == 0 ? 4 : 2 * pst->capacity;
		//当pst->a为NULL时执行的功能是和malloc一样
		STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * NewCapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = NewCapacity;
	}
	//入栈
	pst->a[pst->top] = x;
	pst->top++;
}

//出栈
void StackPop(Stack* pst)
{
	assert(pst);
	//判断栈是否为空
	assert(!StackEmpty(pst));
	//出栈
	pst->top--;
}

//获取栈顶元素
STDataType StackTop(Stack* pst)
{
	assert(pst);
	assert(!StackEmpty(pst));
	//top指向的是栈顶的下一个位置的元素
	return pst->a[pst->top-1];
}

//获取栈中的有效元素的个数
int StackSize(Stack* pst)
{
	assert(pst);

	return pst->top;
}

//检测栈是否为空
bool StackEmpty(Stack* pst)
{
	assert(pst);
	/*if (pst->top == 0)
	{
		return true;
	}
	else
	{
		return false;
	}*/
	return pst->top == 0;
}

//销毁栈
void StackDestroy(Stack* pst)
{
	assert(pst);

	//释放
	free(pst->a);
	pst->a = NULL;
	//重置为0
	pst->top = pst->capacity = 0;
}

源文件:Test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "Stack.h"

void TestStack()
{
	Stack st;
	//初始化
	StackInit(&st);

	//入栈
	StackPush(&st, 1);
	StackPush(&st, 2);
	StackPush(&st, 3);
	StackPush(&st, 4);

	//获取栈中的有效元素的个数
	printf("Size:%d\n", StackSize(&st));

	//访问栈的元素
	while (!StackEmpty(&st))
	{
		//打印栈顶的数据
		printf("%d ", StackTop(&st));
		//出栈
		StackPop(&st);
	}

	//销毁栈
	StackDestroy(&st);
}


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

2.队列 

2.1队列的概念及结构

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out)
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头

2.2队列的实现

我们以STL为模板来实现队列,因此队列的功能也就分为:初始化、销毁、入列、出列、获取队头元素、获取队尾元素,获取队列中有效元素个数、检测队列是否为空。

队列的实现还是和之前的数据结构的实现一样分模块来写, 当然也是可以在一个源文件中完成的,当然小编还是推荐大家分模块来写,先创建两个源文件:Test.c和Queue.c,再创建一个头文件:Queue.h,这里的文件命名不做要求,表示的有意义就行。同样的我们在Test.c中实现基本逻辑,在Queue.c中实现队列的细节,在Queue.h中进行函数声明和头文件包含等,话不多说,我们直接开始:

队列也可以使用数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,每一次出一个数据都要将后面的数据挪动,代价大,效率低,使用链表可以直接今天头删,和尾插。

2.2.1队列的创建

队列的实现使用链表比较合适,因此我们需要先创建一个单链表表示链式结构,由于单链表尾插每次都需要找尾,效率比较低,所以我们可以再创建一个队列结构,这个队列结构中包含这个队列的头、尾、有效元素个数:

头文件:Queue.h

//链式结构:表示队列
typedef int QDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;

//队列的结构
typedef struct Queue
{
	//头指针
	QNode* phead;
	//尾指针
	QNode* ptail;
	//队列中有效元素个数
	int size;
}Queue;

2.2.2队列的初始化

初始化队列比较简单,将队列中的头、尾指针置为NULL,再将有效元素个数置为0:

头文件:Queue.h

//初始化队列
void QueueInit(Queue* pq);

源文件:Queue.c

//初始化队列
void QueueInit(Queue* pq)
{
	assert(pq);

	pq->phead = NULL;
	pq->ptail= NULL;
	pq->size = 0;
}

2.2.3队尾入队列

从队尾插入队列就相当于尾插,先创建一个新结点,表示要插入的结点,这时就要区分第一次插入和后面几次插入,第一次插入时要注意空指针的问题,队列为空的条件是头指针和尾指针都为空,后面的插入直接链接到尾,然后更新尾,然后将有效元素个数加一:

  

头文件:Queue.h

//队尾入队列
void QueuePush(Queue* pq, QDataType x);

源文件:Queue.c

//队尾入队列
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	
	//创建新的结点
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->next = NULL;
	newnode->data = x;

	//第一次尾插
	if (pq->ptail == NULL)
	{
		assert(pq->phead == NULL);

		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	//有效元素++
	pq->size++;
}

2.2.4检测队列是否为空

在出队列时先要进行对队列的检查,检测队列是否为空,若为空就不能再进行删除,实现这个接口有两种方式:

1.头指针和尾指针都指向空。

2.队列中有效元素个数为空。

根据个人习惯来进行使用

头文件:Queue.h

//检测队列是否为空
bool QueueEmpty(Queue* pq);

源文件:Queue.c

//检测队列是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);

	//1.判断头、尾指针
	/*
	return pq->phead == NULL
		&& pq->ptail == NULL;
	*/

	//2.判断有效元素个数
	return pq->size == 0;
}

2.2.5队头出队列

队头出队列就表示头删,因此先要判断队列中是否有数据,然后进行头删,头删的话需要保存头的后一个结点,然后将头结点释放,再将新的头指向保存的结点,然后再将有效元素个数减一,这样子就完成了头删,但是如果我们仔细画图的话就会发现,这样删除的话并不完美,当只有一个结点的时候,进行头删的话这里的尾指针就变成了野指针,所以还是得区分两种情况,只有一个结点和有多个结点,如果只有一个结点,那么直接进行释放,然后将头尾结点都置为NULL,有多个结点的话可以直接释放:

头文件:Queue.h

//对头出队列
void QueuePop(Queue* pq);

源文件:Queue.c

//队头出队列
void QueuePop(Queue* pq)
{
	assert(pq);
	//判空
	assert(!QueueEmpty(pq));

	//一个结点
	if (pq->phead->next == NULL)
	{
		//直接释放
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	//多个结点
	else
	{
		//记录头的下一个
		QNode* Next = pq->phead->next;
		//释放
		free(pq->phead);
		//更新头结点
		pq->phead = Next;
	}
	
	pq->size--;
}

2.2.6获取队头数据

队头数据就是头指针的结点中的data,如果队列是空队列则不能获取,所以先得判断一下:

头文件:Queue.h

//获取队头的元素
QDataType QueueFront(Queue* pq);

源文件:Queue.c

//获取队头的元素
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	//先判空
	assert(!QueueEmpty(pq));

	return pq->phead->data;
}

2.2.7获取队尾数据

队尾数据就是尾指针的结点中的data,如果队列是空队列则不能获取,所以先得判断一下:

头文件:Queue.h

//获取队尾的元素
QDataType QueueBack(Queue* pq);

源文件:Queue.c

//获取队尾的元素
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	//先判空
	assert(!QueueEmpty(pq));

	return pq->ptail->data;
}

2.2.8获取队列有效元素个数

有效元素个数就是size,可直接进行返回:

头文件:Queue.h


//获取队列有效元素的个数
int QueueSize(Queue* pq);

源文件:Queue.c

//获取队列有效元素的个数
int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}

2.2.9销毁队列

由于链式结构的每一个结点都是动态开辟的,因此在程序结束之后我们需要将这些结点一一释放掉,这里要注意,即便是空队列也要进行释放,空队列只表示结点中没有数据,但不能表示没有结点,所以空队列也是要进行释放,销毁队列也有两种方式,大家根据习惯来进行使用:

1.保存当前结点,然后释放。

2.保存下一个结点,然后释放当前结点。

头文件:Queue.h

//销毁队列
void QueueDestroy(Queue* pq);

源文件:Queue.c

//销毁队列
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

2.2.10访问队列

当我们完成上述接口时,可以来测试一下,顺便对队列进行访问,访问的时候依旧是队列不为空我们就可以访问,每一次访问的是队头的元素,然后将队头的元素出列,再访问下一个元素,依次类推:

源文件:Test.c

void TestQueue()
{
	Queue q;
	
	//初始化
	QueueInit(&q);

	//入列
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);

	//有效元素个数
	printf("Size:%d\n", QueueSize(&q));

	//访问队头元素
	printf("Front:%d\n", QueueFront(&q));

	//访问队尾元素
	printf("Back:%d\n", QueueBack(&q));

	//访问队列
	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}

	//销毁
	QueueDestroy(&q);
}


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

2.3队列完整代码

头文件:Queue.h

#pragma once

//       队列

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

//链式结构:表示队列
typedef int QDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;

//队列的结构
typedef struct Queue
{
	//头指针
	QNode* phead;
	//尾指针
	QNode* ptail;
	//队列中有效元素个数
	int size;
}Queue;


//初始化队列
void QueueInit(Queue* pq);

//销毁队列
void QueueDestroy(Queue* pq);

//队尾入队列
void QueuePush(Queue* pq, QDataType x);

//检测队列是否为空
bool QueueEmpty(Queue* pq);

//对头出队列
void QueuePop(Queue* pq);

//获取队头的元素
QDataType QueueFront(Queue* pq);

//获取队尾的元素
QDataType QueueBack(Queue* pq);

//获取队列有效元素的个数
int QueueSize(Queue* pq);

 源文件:Queue.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "Queue.h"

//初始化队列
void QueueInit(Queue* pq)
{
	assert(pq);

	pq->phead = NULL;
	pq->ptail= NULL;
	pq->size = 0;
}

//销毁队列
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

//队尾入队列
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	
	//创建新的结点
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->next = NULL;
	newnode->data = x;

	//第一次尾插
	if (pq->ptail == NULL)
	{
		assert(pq->phead == NULL);

		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	//有效元素++
	pq->size++;
}

//检测队列是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);

	//1.判断头、尾指针
	/*
	return pq->phead == NULL
		&& pq->ptail == NULL;
	*/

	//2.判断有效元素个数
	return pq->size == 0;
}

//队头出队列
void QueuePop(Queue* pq)
{
	assert(pq);
	//判空
	assert(!QueueEmpty(pq));

	//一个结点
	if (pq->phead->next == NULL)
	{
		//直接释放
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	//多个结点
	else
	{
		//记录头的下一个
		QNode* Next = pq->phead->next;
		//释放
		free(pq->phead);
		//更新头结点
		pq->phead = Next;
	}
	
	pq->size--;
}

//获取队头的元素
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	//先判空
	assert(!QueueEmpty(pq));

	return pq->phead->data;
}

//获取队尾的元素
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	//先判空
	assert(!QueueEmpty(pq));

	return pq->ptail->data;
}

//获取队列有效元素的个数
int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}


源文件:Test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "Queue.h"

void TestQueue()
{
	Queue q;
	
	//初始化
	QueueInit(&q);

	//入列
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);

	//有效元素个数
	printf("Size:%d\n", QueueSize(&q));

	//访问队头元素
	printf("Front:%d\n", QueueFront(&q));

	//访问队尾元素
	printf("Back:%d\n", QueueBack(&q));

	//访问队列
	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}

	//销毁
	QueueDestroy(&q);
}


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

今天的博客就分享到这里,喜欢的老铁留下你的三连,感谢感谢!我们下期再见!! 

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

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

相关文章

面试了一个00后,绝对能称为是内卷届的天花板

前言 公司前段缺人&#xff0c;也面了不少测试&#xff0c;结果竟然没有一个合适的。一开始瞄准的就是中级的水准&#xff0c;也没指望来大牛&#xff0c;提供的薪资也不低&#xff0c;面试的人很多&#xff0c;但平均水平很让人失望。令我印象最深的是一个00后测试员&#xf…

期刊介绍|骨科老牌期刊,无版面费,审稿极速,毕业不二之选!

今天给大家介绍一本中药方面的期刊&#xff1a;JOURNAL OF ORTHOPAEDIC RESEARCH 一、基本信息 1、期刊名称&#xff1a;JOURNAL OF ORTHOPAEDIC RESEARCH&#xff1b; 2、期刊ISSN: 0736-0266&#xff1b; 3、研究方向&#xff1a;医学-整形外科&#xff1b; 4、出版社&#x…

Maven多环境配置与使用、跳过测试的三种方法

文章目录 1 多环境开发步骤1:父工程配置多个环境,并指定默认激活环境步骤2:执行安装查看env_dep环境是否生效步骤3:切换默认环境为生产环境步骤4:执行安装并查看env_pro环境是否生效步骤5:命令行实现环境切换步骤6:执行安装并查看env_test环境是否生效 2 跳过测试方式1:IDEA工具…

(转载)从0开始学matlab(第9天)—第一阶段总结

1.编程实例 下面的例子将向大家介绍如何用 MATLAB 解决问题。 例1 温度转换程序 问题&#xff1a; 设计一个 MATLAB 程序&#xff0c;读取一个华氏温度的输入&#xff0c;输出开尔文温度。 答案&#xff1a; 华氏温度和开尔文温度的转换关系式可在物理学课本中找到。其关系式…

JVM面试题(一)

JVM内存分哪几个区&#xff0c;每个区的作用是什么? java虚拟机主要分为以下几个区: JVM中方法区和堆空间是线程共享的&#xff0c;而虚拟机栈、本地方法栈、程序计数器是线程独享的。 &#xff08;1&#xff09;方法区&#xff1a; a. 有时候也成为永久代&#xff0c;在该区内…

电极法测污水常规五参数(PH、电导率、溶解氧、温度、浊度)

检测水质常规五参数的意义&#xff1a; pH&#xff1a;地表水水质中pH值的变化会影响藻类对氧气的摄入能力及动物对食物的摄取敏感度&#xff1b; 电导率&#xff1a;主要是测水的导电性&#xff0c;监测水体中总的离子浓度。包含了各种化学物质、重金属、杂质等等各种导电性物…

低代码,或将颠覆开发行业?

前言 传统的软件开发过程往往需要耗费大量的时间和精力&#xff0c;因为开发人员需编写复杂的代码以完成各种功能。 低代码行业的发展&#xff0c;正好解决了这个问题&#xff0c;让复杂的代码编写一去不复返了。 文章目录 前言引入强大的平台总结 引入 低代码平台 是一种通过可…

超级独角兽 Databricks 的崛起之路

在数据扩张以及 AI 兴起的时代&#xff0c;数据存储和分析平台拥有巨大价值和能量。 随着互联网数据的爆炸性增长&#xff0c;数据已经成为企业的新型资源&#xff0c;犹如石油般重要。越来越多的企业希望利用各种结构化和非结构化数据来发挥自己的优势。 然而&#xff0c;他…

通过关键字搜索接口获取alibaba国际站商品列表

作为一名技术爱好者&#xff0c;我们总会遇到各种各样的技术问题&#xff0c;需要寻找合适的技术解决方案。而在互联网时代&#xff0c;我们可以快速通过搜索引擎获取丰富的技术资源和解决方案。然而&#xff0c;在不同的技术分享中&#xff0c;我们常常会遇到质量参差不齐的文…

计算机网络第一章(谢希仁第8版学习)

作者&#xff1a;爱塔居 专栏&#xff1a;计算机网络 作者简介&#xff1a;大三学生&#xff0c;希望和大家一起加油 文章目录 目录 文章目录 一、网络、互连网、互联网&#xff08;因特网&#xff09;的概念 二、因特网的组成 三、交换方式 3.1 电路交换 3.2 分组交换 3.3 电路…

微信小程序nodejs+vue校园二手商城交易(积分兑换)38gw6

随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;校园二手交易被用户普遍使用&#xff0c;为方便用户能够可以随时…

实操带你使用Mybatis_plus(2)

文章目录 一、通用ServiceService CRUD 接口a> IServiceb>创建Service接口和实现类测试 二、常用注解1、TableName2、TableId雪花算法3、TableField4、TableLogic 一、通用Service Service CRUD 接口 通用 Service CRUD 封装IService 接口&#xff0c;进一步封装 CRUD …

大模型高效调参—PEFT库( Parameter-Efficient Fine-Tuning)

介绍 在面对特定的下游任务时&#xff0c;如果进行Full FineTuning&#xff08;即对预训练模型中的所有参数都进行微调&#xff09;&#xff0c;太过低效&#xff1b;而如果采用固定预训练模型的某些层&#xff0c;只微调接近下游任务的那几层参数&#xff0c;又难以达到较好的…

桥梁安全监测,智能化桥梁结构健康监测方案

桥梁是现代城市交通网络中不可或缺的组成部分&#xff0c;但由于长期受到自然环境和人为因素的影响&#xff0c;桥梁的安全问题一直备受关注。传统的桥梁检测方式主要是靠人力进行巡查&#xff0c;这种方式效率低下、成本高&#xff0c;而且难以全面掌握桥梁结构的真实情况。随…

软件测试外包干了4年,感觉废了..

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入湖南某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…

国考省考行测:资料分析,两年复合增长率

国考省考行测&#xff1a;资料分析&#xff0c;两年复合增长率 2022找工作是学历、能力和运气的超强结合体! 公务员特招重点就是专业技能&#xff0c;附带行测和申论&#xff0c;而常规国考省考最重要的还是申论和行测&#xff0c;所以大家认真准备吧&#xff0c;我讲一起屡屡…

聊一聊适配器模式

接口不能用&#xff1f;行&#xff0c;我帮你适配 一、概述 适配器模式&#xff08;Adapter&#xff09;&#xff0c;是23种设计模式中的结构型模式之一&#xff1b;它就像我们电脑上接口不够时&#xff0c;需要用到的拓展坞&#xff0c;起到转接的作用。它可以将新的功能和原…

SNAP软件处理Sentinel-2 L2A数据为hdr或者tif文件

1.打开Sen2Cor插件处理好的或者下载好的L2A文件 若不知道如何将下载的L1C数据处理为L2A级数据可查看该篇博文 Sentinel-2数据下载及处理_dropoutgirl的博客-CSDN博客 在Bands文件夹下少了B10波段栅格文件: 这主要是因为波段10是卷云波段&#xff0c;需要的大气顶部&#xff0…

【观察】从业界首款“空间穿越屏”,看华为全屋智能的进化与重构

这个时代&#xff0c;“家”的构成不再是简单的一家三口&#xff0c;客厅、厨房、卧室也不再只是承担某个单一功能或场景的空间。 无数身在异乡打拼的青年&#xff0c;开始向往一个专属的独立空间&#xff1b;那些奔波劳碌的中年夫妻&#xff0c;在为家人创造更好生活环境的同时…

Win11系统不兼容怎么回退到Win10系统使用?

Win11系统不兼容怎么回退到Win10系统使用&#xff1f;有用户将自己的电脑系统升级到了Win11之后&#xff0c;发现使用起来非常的卡顿&#xff0c;自己的电脑配置不足。那么这个情况怎么去进行问题的解决呢&#xff1f;来看看以下详细的解决方法分享吧。 准备工作&#xff1a; 1…