深入理解栈与队列:从基本概念到高级实现


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

在这里插入图片描述

文章目录

  • 🐳一、栈
    • 💨1.1 栈的概念及结构
    • 💨1.2 栈的创建
      • ✨<1> 栈的存储结构
      • ✨<2> 栈的接口
    • 💨1.3 栈的实现
      • 💥【1】初始化栈
      • 💥【2】进栈
      • 💥【3】出栈
      • 💥【4】获取栈顶元素
      • 💥【5】获取栈中有效元素个数
      • 💥【6】判空
      • 💥【7】销毁栈
  • 🐳二、队列
    • 💨2.1 队列的概念及结构
    • 💨2.2 队列的创建
      • ✨<1>队列的结构
      • ✨<2>队列的接口
    • 💨2.3 队列的实现
      • 💥【1】初始化队列
      • 💥【2】队尾入队
      • 💥【3】队头出队
      • 💥【4】取队头元素
      • 💥【5】取队尾元素
      • 💥【6】获取队列中有效元素个数
      • 💥【7】判空
      • 💥【8】销毁队列
  • 🐳三、源码
    • 💨3.1 栈
    • 💨3.2 队列

🐳一、栈

💨1.1 栈的概念及结构

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

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶

出栈:栈的删除操作叫做出栈。出数据也在栈顶

在这里插入图片描述
在这里插入图片描述

💨1.2 栈的创建

栈的实现一般可以使用 数组或者链表 实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

✨<1> 栈的存储结构


 下面是定长的静态栈的结构,实际中一般不实用,所以我们主要实现下面的支持动态增长的栈
//typedef int STDataType;
//#define N 10
//typedef struct Stack
//{
//	STDataType _a[N];
//	int _top; // 栈顶
//}Stack;

// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;		// 栈顶
	int capacity;  // 容量 
}Stack;

✨<2> 栈的接口

// 初始化栈 
void StackInit(Stack* ps);
// 入栈 
void StackPush(Stack* ps, STDataType data);
// 出栈 
void StackPop(Stack* ps);
// 获取栈顶元素 
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数 
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps);
// 销毁栈 
void StackDestroy(Stack* ps);

💨1.3 栈的实现

💥【1】初始化栈

创建一个空的栈,使其数组指针为 NULL,容量为 0,栈顶指针为 0

// 初始化栈 
void StackInit(Stack* ps)
{
	assert(ps);
	ps->a = NULL;
	
	ps->capacity = 0;
	ps->top = 0;//top指向栈顶元素的下一个
	
	// 表示top指向栈顶元素
	//pst->top = -1;
}

💥【2】进栈

  1. 初始化:首先,函数通过assert来检查传入的栈指针ps是否非空,以确认该栈存在。如果栈已满(ps->top == ps->capacity),则不执行任何操作。
  2. 扩容:如果栈不满,但接近满载,代码将进行扩容操作。如果栈的当前容量ps->capacity0(即初始状态或刚经历过扩容),新的容量设为 4。否则,新的容量为当前容量的两倍。这样做是为了预先保留足够的空间,避免频繁的扩容操作。
  3. 重新分配内存:使用realloc函数重新分配栈的内存空间。新的内存大小为新的容量乘以数据类型的大小。如果重新分配失败(realloc返回NULL),则输出错误信息并返回。
  4. 数据入栈:将数据data存储在栈顶的位置,即将data赋值给ps->a[ps->top]。然后,栈顶指针ps->top自增 1,表示栈中多了一个元素。
// 入栈 
void StackPush(Stack* ps, STDataType data)
{
	assert(ps);
	if (ps->capacity == ps->top)
	{
		int newcapacity = ps->capacity == 0 ? 4 : (ps->capacity) * 2;
		STDataType* temp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newcapacity);
		if (temp == NULL)
		{
			perror("realloc fail");
			return;
		}
		ps->a = temp;
		ps->capacity = newcapacity;
	}
	ps->a[ps->top] = data;
	ps->top++;
}

💥【3】出栈

  1. 初始化:首先,函数通过 assert 来检查传入的栈指针ps是否非空,以确认该栈存在。这保证了不会对一个不存在的栈进行操作。
  2. 栈非空:接着,函数通过另一个 assert 来确认栈顶指针ps->top大于 0,即栈非空。如果栈为空,那么执行出栈操作没有意义,因此代码会在这里停止执行并输出错误信息。
  3. 出栈:如果栈非空,那么代码将栈顶指针ps->top自减 1,表示栈顶的元素已经出栈。
// 出栈 
void StackPop(Stack* ps)
{
	assert(ps);
	//栈不为空
	assert(ps->top > 0);

	ps->top--;
}

💥【4】获取栈顶元素

  1. 初始化:首先,函数通过assert来检查传入的栈指针ps是否非空,以确认该栈存在。这保证了不会对一个不存在的栈进行操作。
  2. 栈非空:接着,函数通过另一个assert来确认栈顶指针ps->top大于0,即栈非空。如果栈为空,那么获取栈顶元素的操作没有意义,因此代码会在这里停止执行并输出错误信息。
  3. 获取栈顶元素:如果栈非空,那么代码将返回栈顶元素,即ps->a[ps->top - 1]。这里之所以使用ps->top - 1,是因为在C/C++中,数组的索引是从0开始的,而栈顶元素实际上是位于栈顶指针所指示的位置的前一个位置。
// 获取栈顶元素 
STDataType StackTop(Stack* ps)
{
	assert(ps);
	//栈不为空
	assert(ps->top > 0);

	return ps->a[ps->top - 1];
}

💥【5】获取栈中有效元素个数

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

	return ps->top;
}

💥【6】判空

// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps)
{
	assert(ps);
	/*if (ps->top == 0)
		return 1;
	return 0;*/

	return ps->top == 0;
}

💥【7】销毁栈

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

	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}

🐳二、队列

💨2.1 队列的概念及结构

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

入队列:进行插入操作的一端称为 队尾

出队列:进行删除操作的一端称为 队头

在这里插入图片描述

💨2.2 队列的创建

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,需要将后面的元素向前移动,时间复杂度为 O(1) 效率会比较低。
在这里插入图片描述

✨<1>队列的结构

// 链式结构:表示队列 
typedef int QDataType; //元素类型
//队列的节点
typedef struct QListNode
{
	QDataType data;
	struct QListNode* next;
}QNode; 
// 队列的结构 
typedef struct Queue
{
	QNode* front; //对头
	QNode* rear; //队尾
	int size; //队列元素个数
}Queue;

✨<2>队列的接口

// 初始化队列 
void QueueInit(Queue* q);
// 队尾入队列 
void QueuePush(Queue* q, QDataType data);
// 队头出队列 
void QueuePop(Queue* q);
// 获取队列头部元素 
QDataType QueueFront(Queue* q);
// 获取队列队尾元素 
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数 
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q);
// 销毁队列 
void QueueDestroy(Queue* q);

💨2.3 队列的实现

💥【1】初始化队列

通过断言检查输入的有效性,然后初始化队列的front、rear和size属性,为后续队列操作做好准备。

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

	q->front = q->rear = NULL;
	q->size = 0;
}

💥【2】队尾入队

创建一个新的节点,并将其添加到队列的尾部。如果队列为空,新节点会同时成为队列的前端和后端;如果队列不为空,新节点会添加到队列的后端,并更新队列的后端为新节点。

  1. 断言:通过assert语句检查输入的队列指针是否非空。如果队列指针为空,则程序会在此处停止并输出错误信息。
  2. 动态分配内存:创建一个新的节点newnode,并为其分配内存空间。使用malloc函数实现动态内存分配。如果分配失败(即malloc返回NULL),则输出错误信息并结束函数。
  3. 设置新节点数据:设置新节点的数据为输入的data,并将新节点的下一个节点设置为NULL
  4. 插入新节点:如果队列为空(即队列的前端q->front和队列的后端q->rear都为NULL),那么将新节点设置为队列的前端和后端。如果队列不为空,那么将新节点添加到队列的后端,并更新队列的后端为新节点。
  5. 更新队列大小:每添加一个新节点,队列的大小增加1,因此增加队列的size属性。
// 队尾入队列 
void QueuePush(Queue* q, QDataType data)
{
	assert(q);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return;
	}

	newnode->data = data;
	newnode->next = NULL;

	if (q->front == NULL)
	{
		q->front = q->rear = newnode;
	}
	else
	{
		q->rear->next = newnode;
		q->rear = newnode;
	}
	
	q->size++;//队列元素加一
}

💥【3】队头出队

删除队列的队头元素,并更新队列的队头和队尾。同时,需要注意处理队列为空的情况,以防止产生野指针。

  1. 断言:通过assert语句检查输入的队列指针是否非空,以及队列是否非空。如果队列指针为空或队列为空,则程序会在此处停止并输出错误信息。
  2. 获取队头节点:保存当前队列的队头节点cur
  3. 更新队头:将队列的队头更新为下一个节点。如果队列变为空(即队列的队头和队尾都为NULL),那么将队尾也设置为NULL
  4. 释放内存:由于已经删除了队头节点,需要使用free函数释放该节点的内存空间。
  5. 更新队列大小:每删除一个元素,队列的大小减1
// 队头出队列 
void QueuePop(Queue* q)
{
	assert(q);
	//队列不为空
	assert(q->front);
	QNode* cur = q->front;
	q->front = q->front->next;
	//队列只有一个元素的情况,要考虑队尾的指针,防止野指针
	if (q->front == NULL)
		q->rear = NULL;

	free(cur);

	q->size--;//队列元素减一
}

💥【4】取队头元素

// 获取队列头部元素 
QDataType QueueFront(Queue* q)
{
	assert(q);
	//队列不为空
	assert(q->front);
	
	return q->front->data;
}

💥【5】取队尾元素

// 获取队列队尾元素 
QDataType QueueBack(Queue* q)
{
	assert(q);
	//队列不为空
	assert(q->front);

	return q->rear->data;
}

💥【6】获取队列中有效元素个数

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

	return q->size;
}

💥【7】判空

通过比较队列的队头指针q->front是否等于NULL来判断队列是否为空。如果队列为空(即队列的前端和后端都为NULL),则返回非零结果(这里可能是一个表示“真”的值,例如1)。如果队列非空,则返回0。

// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q)
{
	assert(q);

	return q->front == NULL;
}

💥【8】销毁队列

遍历队列中的所有节点并逐个释放其内存空间,然后重置队列的属性以使其成为一个空队列。

  1. 断言:通过assert语句检查输入的队列指针是否非空。如果队列指针为空,则程序会在此处停止并输出错误信息。
  2. 遍历队列:通过一个循环遍历队列中的所有节点。循环继续进行,直到当前节点cur为空。 * 获取当前节点:使用队列的前端指针q->front来获取当前节点cur。 * 释放内存:使用free函数释放当前节点cur的内存空间。 * 移动指针:将当前节点cur的指针更新为下一个节点。
  3. 重置队列属性:将队列的前端指针q->front和后端指针q->rear重置为NULL,并将队列的大小属性q->size重置为0。
// 销毁队列 
void QueueDestroy(Queue* q)
{
	assert(q);

	QNode* cur = q->front;
	while (cur)//当cur为空时结束
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}

	q->front = q->rear = NULL;
	q->size = 0;
}

🐳三、源码

💨3.1 栈

//Stack.h文件
#pragma once

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

 下面是定长的静态栈的结构,实际中一般不实用,所以我们主要实现下面的支持动态增长的栈
//typedef int STDataType;
//#define N 10
//typedef struct Stack
//{
//	STDataType _a[N];
//	int _top; // 栈顶
//}Stack;


// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;		// 栈顶
	int capacity;  // 容量 
}Stack;

// 初始化栈 
void StackInit(Stack* ps);
// 入栈 
void StackPush(Stack* ps, STDataType data);
// 出栈 
void StackPop(Stack* ps);
// 获取栈顶元素 
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数 
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps);
// 销毁栈 
void StackDestroy(Stack* ps);

//Stack.c文件
#define _CRT_SECURE_NO_WARNINGS 1

#include "Stack.h"

// 初始化栈 
void StackInit(Stack* ps)
{
	assert(ps);
	ps->a = NULL;
	
	ps->capacity = 0;
	ps->top = 0;//top指向栈顶元素的下一个
	
	// 表示top指向栈顶元素
	//pst->top = -1;
}
// 入栈 
void StackPush(Stack* ps, STDataType data)
{
	assert(ps);
	if (ps->capacity == ps->top)
	{
		int newcapacity = ps->capacity == 0 ? 4 : (ps->capacity) * 2;
		STDataType* temp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newcapacity);
		if (temp == NULL)
		{
			perror("realloc fail");
			return;
		}
		ps->a = temp;
		ps->capacity = newcapacity;
	}
	ps->a[ps->top] = data;
	ps->top++;
}
// 出栈 
void StackPop(Stack* ps)
{
	assert(ps);
	//栈不为空
	assert(ps->top > 0);

	ps->top--;
}
// 获取栈顶元素 
STDataType StackTop(Stack* ps)
{
	assert(ps);
	//栈不为空
	assert(ps->top > 0);

	return ps->a[ps->top - 1];
}
// 获取栈中有效元素个数 
int StackSize(Stack* ps)
{
	assert(ps);

	return ps->top;
}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps)
{
	assert(ps);
	/*if (ps->top == 0)
		return 1;
	return 0;*/

	return ps->top == 0;
}
// 销毁栈 
void StackDestroy(Stack* ps)
{
	assert(ps);

	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}

//Test.c文件
#define _CRT_SECURE_NO_WARNINGS 1

#include "Stack.h"


void Test()
{
	Stack s;
	StackInit(&s);
	StackPush(&s, 1);
	StackPush(&s, 2);
	StackPush(&s, 3);
	printf("%d ", StackTop(&s));
	StackPop(&s);
	printf("%d ", StackTop(&s));
	StackPop(&s);
	
	StackPush(&s, 4);
	StackPush(&s, 5);
	
	//    一     对     多
	// 入栈顺序  --  出栈顺序
	while (!StackEmpty(&s))
	{
		printf("%d ", StackTop(&s));
		StackPop(&s);
	}
	printf("\n");

}

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

💨3.2 队列

//Queue.h文件
#pragma once

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

// 链式结构:表示队列 
typedef int QDataType; //元素类型
//队列的节点
typedef struct QListNode
{
	QDataType data;
	struct QListNode* next;
}QNode;
// 队列的结构 
typedef struct Queue
{
	QNode* front; //对头
	QNode* rear; //队尾
	int size; //队列元素个数
}Queue;

// 初始化队列 
void QueueInit(Queue* q);
// 队尾入队列 
void QueuePush(Queue* q, QDataType data);
// 队头出队列 
void QueuePop(Queue* q);
// 获取队列头部元素 
QDataType QueueFront(Queue* q);
// 获取队列队尾元素 
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数 
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q);
// 销毁队列 
void QueueDestroy(Queue* q);

//Queue.c文件
#define _CRT_SECURE_NO_WARNINGS 1

#include "Queue.h"

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

	q->front = q->rear = NULL;
	q->size = 0;
}
// 队尾入队列 
void QueuePush(Queue* q, QDataType data)
{
	assert(q);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return;
	}

	newnode->data = data;
	newnode->next = NULL;

	if (q->front == NULL)
	{
		q->front = q->rear = newnode;
	}
	else
	{
		q->rear->next = newnode;
		q->rear = newnode;
	}
	
	q->size++;//队列元素加一
}
// 队头出队列 
void QueuePop(Queue* q)
{
	assert(q);
	//队列不为空
	assert(q->front);
	QNode* cur = q->front;
	q->front = q->front->next;
	//队列只有一个元素的情况,要考虑队尾的指针,防止野指针
	if (q->front == NULL)
		q->rear = NULL;

	free(cur);

	q->size--;//队列元素减一
}
// 获取队列头部元素 
QDataType QueueFront(Queue* q)
{
	assert(q);
	//队列不为空
	assert(q->front);
	
	return q->front->data;
}
// 获取队列队尾元素 
QDataType QueueBack(Queue* q)
{
	assert(q);
	//队列不为空
	assert(q->front);

	return q->rear->data;
}
// 获取队列中有效元素个数 
int QueueSize(Queue* q)
{
	assert(q);

	return q->size;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q)
{
	assert(q);

	return q->front == NULL;
}
// 销毁队列 
void QueueDestroy(Queue* q)
{
	assert(q);

	QNode* cur = q->front;
	while (cur)//当cur为空时结束
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}

	q->front = q->rear = NULL;
	q->size = 0;
}

//Test.c文件
#define _CRT_SECURE_NO_WARNINGS 1

#include "Queue.h"

void Test()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	printf("%d ", QueueFront(&q));
	QueuePop(&q);
	printf("%d ", QueueFront(&q));
	QueuePop(&q);

	QueuePush(&q, 4);
	QueuePush(&q, 5);
	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}

	QueueDestroy(&q);
}

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

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

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

相关文章

【Dubbo】Dubbo负载均衡实现解析

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;2022年度博客之星全国TOP3&#xff0c;专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化&#xff0c;文章内容兼具广度、深度、大厂技术方案&#xff0c;对待技术喜欢推理加验证&#xff0c;就职于…

如何在本地搭建Oracle数据库实现公网环境下通过PLSQL工具进行远程访问

文章目录 前言1. 数据库搭建2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射 3. 公网远程访问4. 配置固定TCP端口地址4.1 保留一个固定的公网TCP端口地址4.2 配置固定公网TCP端口地址4.3 测试使用固定TCP端口地址远程Oracle 前言 Oracle&#xff0c;是甲骨文公司的一款关系…

Loguru:一个超酷的Python库

在项目中&#xff0c;了解代码运行情况至关重要&#xff0c;特别是遇到Bug需要排除问题的时候&#xff0c;而这正是日志记录发挥作用的地方。对于Python开发者来说&#xff0c;Loguru是一个简单但功能强大的日志记录库&#xff0c;它使得跟踪代码的行为变得轻松而高效。 什么是…

【设计模式】聊聊模板模式

原理和实现 设计模式的原理和实现是比较简单的&#xff0c;难的是掌握具体的应用场景和解决什么问题。而模板模式是为来解决复用和拓展两个问题。 模板模式在一个方法中定义好一个算法框架&#xff0c;然后将某些步骤推迟到子类中实现&#xff0c;子类可以在不修改父类流程的时…

论文速览 Arxiv 2023 | DMV3D: 单阶段3D生成方法

注1:本文系“最新论文速览”系列之一,致力于简洁清晰地介绍、解读最新的顶会/顶刊论文 论文速览 Arxiv 2023 | DMV3D: DENOISING MULTI-VIEW DIFFUSION USING 3D LARGE RECONSTRUCTION MODEL 使用3D大重建模型来去噪多视图扩散 论文原文:https://arxiv.org/pdf/2311.09217.pdf…

大语言模型的三阶段训练

为了训练专有领域模型&#xff0c;选择LLaMA2-7B作为基座模型&#xff0c;由于LLaMA模型中文词表有限&#xff0c;因此首先进行中文词表的扩展&#xff0c;然后进行三阶段训练&#xff08;增量预训练&#xff0c;有监督微调&#xff0c;强化学习&#xff09;。 代码将全部上传…

C语言实现带头双向循环链表

文章目录 写在前面1. 链表节点的定义2. 链表的初始化3. 插入数据3.1 头插3.2 尾插3.3 在指定位置的前面插入数据 4 删除数据4.1 头删4.2 尾删4.3 删除指定位置的数据 5 查找并修改数据5. 链表的销毁 写在前面 上面文章用C语言实现了单链表的增删查改&#xff0c;我们知道&…

基于Qt QList和QMap容器类示例

## QList<T> QList<T>容器是一个数组列表,特点如下: 1.大多数情况下可以用QList。像prepend()、append()和insert()这种操作,通常QList比QVector快的多。这是因为QList是基于index标签存储它的元素项在内存中(虽然内存不连续,这点与STL的list 是一样的),比…

23111904计算机程序设计-基于SpringbootfreemarkerMysql的宿舍寝室维修上报管理系统

文章目录 系统功能系统实现开发环境 编程技术交流、源码分享、模板分享、网课分享 企鹅&#x1f427;裙&#xff1a;776871563 系统功能 《基于SpringbootfreemarkerMysql实现的宿舍|寝室维修上报管理系统》该项目含有源码、文档等资料、配套开发软件、软件安装教程、项目发布…

软考小记-软件工程

模块的控制范围包括模块本身及其所有的从属模块。模块的作用范围是指模块一个判定的作用范围&#xff0c;凡是受这个判定影响的所有模块都属于这个判定的作用范围.&#xff0c;原则上一个模块的作用范围应该在其控制范围之内&#xff0c;若没有&#xff0c;则可以将判定所在模块…

Flutter最新稳定版3.16 新特性介绍

Flutter 3.16 默认采用 Material 3 主题&#xff0c;Android 平台预览 Impeller&#xff0c;DevTools 扩展等等 Flutter在每个季度通常都会有个稳定版本的发布。在2023 Q4的更新中为大家带来的是Flutter 3.16。这个版本将 Material 3 设为新的默认主题&#xff0c;并为 Android…

openGauss学习笔记-127 openGauss 数据库管理-设置账本数据库-修复账本数据库

文章目录 openGauss学习笔记-127 openGauss 数据库管理-设置账本数据库-修复账本数据库127.1 前提条件127.2 背景信息127.3 操作步骤 openGauss学习笔记-127 openGauss 数据库管理-设置账本数据库-修复账本数据库 127.1 前提条件 系统中需要有审计管理员或者具有审计管理员权…

JUnit 单元自动化

一、Junit 是什么&#xff1f; Junit 是 Java 中用于单元测试的框架。使用 Junit 能让我们快速高效的完成单元测试。 自动化测试&#xff1a;JUnit提供了自动化测试的能力&#xff0c;开发人员可以编写一次测试用例&#xff0c;然后通过简单的命令或集成到持续集成工具中进行…

Python----函数中的说明文档

说明文档&#xff1a;就是一行注释&#xff0c;在每次 定义一个函数后&#xff08;def XXX(): 的下一行&#xff09;&#xff0c;开发的人写一段注释文字&#xff0c;告诉别人这个函数是干嘛用的。 案例&#xff1a;定义函数的说明文档 ① 定义函数的说明文档 # 1、定义一个…

map与set的封装

目录 红黑树的结点 与 红黑树的迭代器 红黑树的实现&#xff1a; 迭代器&#xff1a; ​编辑 红黑树的查找&#xff1a; 红黑树的插入&#xff1a; ​编辑 检查红色结点&#xff1a;​编辑红黑树的左旋 ​编辑红黑树的右旋 ​编辑红黑树的双旋 Map的封装 ​编辑set的…

CnosDB有主复制演进历程

分布式存储系统的复杂性涉及数据容灾备份、一致性、高并发请求和大容量存储等问题。本文结合CnosDB在分布式环境下的演化历程&#xff0c;分享如何将分布式理论应用于实际生产&#xff0c;以及不同实现方式的优缺点和应用场景。 分布式系统架构模式 分布式存储系统下按照数据复…

2.5A、3MHz开关充电器解决方案

今天给大家介绍的是属于电源管理芯片中的开关型锂离子电池充电芯片&#xff0c;在前面介绍了一款锂离子电池充电池TP4054&#xff0c;相比于之前的那款芯片&#xff0c;这款芯片具有更强大的功能与应用。 基本概述 ETA6002是一款开关式锂离子电池充电器&#xff0c;具有动态电…

hypermesh常用快捷键

#hypermesh常用快捷键

UE 视差材质 学习笔记

视差材质节点&#xff1a; 第一个是高度图&#xff0c; Heightmap Channel就是高度图的灰色通道&#xff0c;在RGBA哪个上面&#xff0c;例如在R上就连接(1,0,0,0)&#xff0c;G上就连接&#xff08;0,1,0,0&#xff09;逐次类推 去看看对比效果&#xff1a; 这个是有视差效果…

OpenCV快速入门:窗口交互

文章目录 前言一、鼠标操作1.1 鼠标操作简介1.2 鼠标事件类型&#xff08;event类型&#xff09;1.3 鼠标事件标志&#xff08;flags&#xff09;1.4 代码示例1.4.1 获取鼠标坐标位置1.4.2 监听鼠标滚轮事件1.4.3 在图像中显示鼠标坐标 二、键盘操作2.1 代码示例2.2 waitKey的等…
最新文章