数据结构——链式队列和循环队列

news2024/11/25 12:56:46

目录

引言

队列的定义

队列的分类

1.单链表实现

2.数组实现

队列的功能

队列的声明

1.链式队列

2.循环队列

队列的功能实现

1.队列初始化

(1)链式队列

(2)循环队列

(3)复杂度分析

2.判断队列是否为空

(1)链式队列

(2)循环队列

(3)复杂度分析

3.判断队列是否已满

(1)链式队列

(2)循环队列

(3)复杂度分析

4.返回队头元素

(1)链式队列

(2)循环队列

(3)复杂度分析

5.返回队尾元素

(1)链式队列

(2)循环队列

(3)复杂度分析

6.返回队列大小

(1)链式队列

(2)循环队列

(3)复杂度分析

7.元素入队列

(1)链式队列

(2)循环队列

(3)复杂度分析

8.元素出队列

(1)链式队列

(2)循环队列

(3)复杂度分析

9.打印队列元素

(1)链式队列

(2)循环队列

(3)复杂度分析

10.销毁队列

(1)链式队列

(2)循环队列

(3)复杂度分析

链式队列和循环队列的对比

完整代码

1.链式队列

2.循环队列

结束语


引言

在 数据结构——栈 中我们学习了栈的相关知识,今天我们接着来学习数据结构——队列。

队列的定义

队列(Queue)是一种先进先出(FIFO, First-In-First-Out)的线性数据结构。它只允许在队列的一端(队尾)进行插入(enqueue)操作,而在另一端(队头)进行删除(dequeue)操作。这种操作方式确保了队列中元素的顺序性,即最先进入队列的元素将最先被移除。

如图所示:

队头(Front):队头是指队列中允许删除操作的一端。也就是说,队列中的元素将按照它们被添加到队列中的顺序,从队头开始被逐一移除。

队尾(Rear):队尾是指队列中允许插入操作的一端。新元素将被添加到队尾,以保持队列的先进先出(FIFO)特性。

队列的分类

队列与栈类似,同样可以用两种方式实现。分别是单链表和数组。接下来我们来分析一下如何使用两种方法实现队列。

1.单链表实现

我们可以将链表的头节点与尾节点分别作为队列的队首与队尾,这样我们就能用两个指针来对其进行操作。

如下图所示:

2.数组实现

问题一:

在使用数组实现队列时,可能会出现“假溢出”的情况。这是因为数组的头部和尾部是固定的,当队列的尾部达到数组的末尾时,即使数组的头部还有很多空闲空间,也无法再向队列中添加新元素。这种情况下,队列虽然看起来是满的,但实际上还有很多空间没有被利用。

解决方法:

我们可以将数组相接变成循环数组。

问题二:

变成循环数组又会出现新的问题:当队首与队尾下标都指向同一个节点时,这个队列是还是呢?

解决方法:

多使用一个空间便于我们区分队空和队满。若队列不为空,让队尾下标指向队尾的下一个位置。我们约定以队头指针在队尾指针的下一位置作为队满的标志,即Queue->rear+1=Queue->front

如下图所示:

队满状态:

队列的功能

1.队列的初始化。

2.判断队列是否为空。

3.判断队列是否已满。

4.返回队头元素。

5.返回队尾元素

6.返回队列的大小。

7.元素入队列。

8.元素出队列。

9.打印队列的元素。

10.销毁队列。

队列的声明

1.链式队列

typedef int QDataType;

typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType val;
}QNode;

typedef struct Queue
{
	QNode* front;
	QNode* rear;
	int size;
}Queue;

2.循环队列

typedef int QDataType;

#define MAXSIZE 30    //定义队列的最大值
typedef struct
{
	QDataType* data;
	int front;    //头指针
	int rear;     //尾指针
}Queue;

队列的功能实现

1.队列初始化

给队列中的各个元素给定值,以免出现随机值。

(1)链式队列
//初始化
void QueueInit(Queue* q)
{
	assert(q);
	q->front = NULL;
	q->rear = NULL;
	q->size = 0;
}
(2)循环队列
//初始化
void QueueInit(Queue* q)
{
	// 为队列的数据存储空间分配内存。
	q->data = (QDataType*)malloc(sizeof(QDataType) * MAXSIZE);
	if (q->data == NULL)
	{
		perror("malloc fail:");
		return;
	}
	// 初始化队首和队尾指针为0,表示队列为空
	q->front = q->rear = 0;
}
(3)复杂度分析

时间复杂度:由于链式队列和循环队列花费时间都是一个常数,因此时间复杂度为O(1)。

空间复杂度:由于链式队列花费的是一个固定大小的空间,因此空间复杂度为O(1)。循环队列需要开辟整个队列的大小,因此空间复杂度为O(N)。

2.判断队列是否为空

(1)链式队列

判断size是否为0即可。

代码如下:

//判断队列是否为空
bool QueueEmpty(Queue* q)
{
	assert(q);
	return q->size == 0;
}
(2)循环队列

判断front是否等于rear即可。

代码如下:

//判断队列是否为空
bool QueueEmpty(Queue* q)
{
	assert(q);
	return q->front == q->rear;
}
(3)复杂度分析

时间复杂度:由于链式队列和循环队列花费时间都是一个常数,因此时间复杂度为O(1)。

空间复杂度:由于链式队列和循环队列花费空间都是一个常数,因此空间复杂度为O(1)。

3.判断队列是否已满

(1)链式队列

链式队列不需要判断队列是否已满。

(2)循环队列

循环队列判断是否已满需要进行一些特殊处理。

代码如下:

//判断队列是否已满
bool QueueFull(Queue* q)
{
	assert(q);
    // 取模操作是避免越界
	return q->front == (q->rear + 1) % MAXSIZE;
}
(3)复杂度分析

时间复杂度:由于循环队列花费时间是一个常数,因此时间复杂度为O(1)。

空间复杂度:循环队列花费的是一个固定大小的空间,所以空间复杂度为O(1)。

4.返回队头元素

(1)链式队列
//读取队头数据
QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(q->front);
	return q->front->val;
}
(2)循环队列
//读取队头数据
QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->data[q->front];
}
(3)复杂度分析

时间复杂度:由于链式队列和循环队列花费时间都是一个常数,因此时间复杂度为O(1)。

空间复杂度:由于链式队列和循环队列花费空间都是一个常数,因此空间复杂度为O(1)。

5.返回队尾元素

(1)链式队列
//读取队尾数据
QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(q->rear);
	return q->rear->val;
}
(2)循环队列
//读取队尾数据
QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));

	// 当rear为0时,rear-1会导致负数索引,这在数组中是无效的  
	// 通过加上MAXSIZE并取模MAXSIZE,我们可以确保索引始终在有效范围内  
	// 这里(q->rear - 1 + MAXSIZE) % MAXSIZE计算的是队尾元素的索引
	return q->data[(q->rear - 1 + MAXSIZE) % MAXSIZE];
}
(3)复杂度分析

时间复杂度:由于链式队列和循环队列花费时间都是一个常数,因此时间复杂度为O(1)。

空间复杂度:由于链式队列和循环队列花费空间都是一个常数,因此空间复杂度为O(1)。

6.返回队列大小

(1)链式队列

链式队列求队列大小很简单,直接返回size即可。

代码如下:

//统计队列数据个数
int QueueSize(Queue* q)
{
	assert(q);
	return q->size;
}
(2)循环队列

我们来分析一下:

像这个可以使用rear-front求出队列的大小。但是要知道,这是个循环队列,rear时=是可以跑到front之后的,如下图所示:

解决方法:rear减去front之后加个MAXSIZE就好了。

代码如下:

//统计队列数据个数
int QueueSize(Queue* q)
{
	assert(q);
	return (q->rear - q->front + MAXSIZE) % MAXSIZE;
}
(3)复杂度分析

时间复杂度:由于链式队列和循环队列花费时间都是一个常数,因此时间复杂度为O(1)。

空间复杂度:由于链式队列和循环队列花费空间都是一个常数,因此空间复杂度为O(1)。

7.元素入队列

(1)链式队列

链式队列元素入队需要判断队列是否为空的情况。

代码如下:

//队尾插入
void QueuePush(Queue* q, QDataType x)
{
	assert(q);
	QNode* newNode = (QNode*)malloc(sizeof(QNode));
	if (newNode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newNode->next = NULL;
	newNode->val = x;

	// 如果队列为空 
	if (q->rear == NULL)
	{
		// 新节点既是队首也是队尾
		q->front = q->rear = newNode;
	}
	else
	{
		// 将当前队尾节点的next指向新节点
		q->rear->next = newNode;
		// 更新队尾指针为新节点
		q->rear = newNode;
	}
	q->size++;
}
(2)循环队列

取模操作不能少,确保不会越界。

//队尾插入
void QueuePush(Queue* q, QDataType x)
{
	assert(q);
	if (QueueFull)
	{
		printf("队列已满\n");
		return;
	}
	q->data[q->rear] = x;

	// rear指针向后移动
	// (q->rear + 1) % MAXSIZE这段代码
	// 确保了rear指针的值始终在0到MAXSIZE-1的范围内循环
	q->rear = (q->rear + 1) % MAXSIZE;
}
(3)复杂度分析

时间复杂度:由于链式队列和循环队列花费时间都是一个常数,因此时间复杂度为O(1)。

空间复杂度:由于链式队列和循环队列花费空间都是一个常数,因此空间复杂度为O(1)。

8.元素出队列

(1)链式队列
//队头删除
void QueuePop(Queue* q)
{
	assert(q);
	assert(q->size != 0);
	// 检查队列中是否只有一个节点
	if (q->front->next == NULL)
	{
		free(q->front);
		// 队列变为空,队首和队尾指针都设置为NULL
		q->front = q->rear = NULL;
	}
	// 多个节点
	else
	{
		// 保存下一个节点的指针
		QNode* next = q->front->next;
		// 释放队首节点
		free(q->front);
		// 更新队首指针为下一个节点
		q->front = next;
	}
	q->size--;
}
(2)循环队列
//队头删除
void QueuePop(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	// front指针向后移动
	// (q->front + 1) % MAXSIZE这段代码
	// 确保了front指针的值始终在0到MAXSIZE-1的范围内循环
	q->front = (q->front + 1) % MAXSIZE;
}
(3)复杂度分析

时间复杂度:由于链式队列和循环队列花费时间都是一个常数,因此时间复杂度为O(1)。

空间复杂度:由于链式队列和循环队列花费空间都是一个常数,因此空间复杂度为O(1)。

9.打印队列元素

(1)链式队列
//打印队列元素
void QueuePrint(Queue* q)
{
	assert(q);
	QNode* cur = q->front;
	QNode* tail = q->rear;
	printf("队头->");
	while (cur != tail->next)
	{
		printf("%d->", cur->val);
		cur = cur->val;
	}
	printf("队尾\n");
}
(2)循环队列
//打印队列元素
void QueuePrint(Queue* q)
{
	assert(q);
	int cur = q->front;
	printf("队头->");
	while (cur != q->rear)
	{
		printf("%d->", q->data[cur]);
		// 避免越界
		cur = (cur + 1) % MAXSIZE;
	}
	printf("队尾\n");
}
(3)复杂度分析

时间复杂度:由于链式队列和循环队列都需要遍历整个队列,因此时间复杂度为O(n)。

空间复杂度:由于链式队列和循环队列花费空间都是一个常数,因此空间复杂度为O(1)。

10.销毁队列

(1)链式队列
//销毁
void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* cur = q->front;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	q->front = q->rear = NULL;
	q->size = 0;
}
(2)循环队列
//销毁
void QueueDestroy(Queue* q)
{
	assert(q);
	free(q->data);
	q->data = NULL;
	q->front = q->rear = 0;
}
(3)复杂度分析

时间复杂度:由于链式队列需要遍历整个队列,因此时间复杂度为O(n)。循环队列花费时间是一个常数,因此时间复杂度为O(1)。

空间复杂度:由于链式队列和循环队列花费空间都是一个常数,因此空间复杂度为O(1)。

链式队列和循环队列的对比

链式列表循环列表
数据结构使用链表实现,队列中的每个元素都是一个链表节点,节点之间通过指针相连使用数组实现,但在逻辑上将数组的头尾相连,形成一个环形结构。
内存管理出队或清空队列时需要释放链表节点的内存无需手动释放内存,因为队列元素存储在数组中,数组的内存由系统自动管理
时间效率由于使用头指针与尾指针,因此链式队的出队与入队的时间都相对较小循环队列是基于数组实现的,支持下标的随机访问,所以时间消耗并不大
空间效率空间使用灵活,可以根据需要动态地增加或减少内存分配空间使用相对固定,需要预先定义最大容量。如果队列的实际大小远小于最大容量,则会造成空间浪费

完整代码

1.链式队列

Queue.h

#define _CRT_SECURE_NO_WARNINGS 1

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

typedef int QDataType;

typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType val;
}QNode;

typedef struct Queue
{
	QNode* front;
	QNode* rear;
	int size;
}Queue;

//初始化
void QueueInit(Queue* q);
//销毁
void QueueDestroy(Queue* q);

//队尾插入
void QueuePush(Queue* q, QDataType x);
//队头删除
void QueuePop(Queue* q);

//读取队头数据
QDataType QueueFront(Queue* q);
//读取队尾数据
QDataType QueueBack(Queue* q);

//统计队列数据个数
int QueueSize(Queue* q);
//判断队列是否为空
bool QueueEmpty(Queue* q);

//打印队列元素
void QueuePrint(Queue* q);

Queue.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"Queue.h"

//初始化
void QueueInit(Queue* q)
{
	assert(q);
	q->front = NULL;
	q->rear = NULL;
	q->size = 0;
}

//销毁
void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* cur = q->front;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	q->front = q->rear = NULL;
	q->size = 0;
}

//队尾插入
void QueuePush(Queue* q, QDataType x)
{
	assert(q);
	QNode* newNode = (QNode*)malloc(sizeof(QNode));
	if (newNode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newNode->next = NULL;
	newNode->val = x;

	// 如果队列为空 
	if (q->rear == NULL)
	{
		// 新节点既是队首也是队尾
		q->front = q->rear = newNode;
	}
	else
	{
		// 将当前队尾节点的next指向新节点
		q->rear->next = newNode;
		// 更新队尾指针为新节点
		q->rear = newNode;
	}
	q->size++;
}

//队头删除
void QueuePop(Queue* q)
{
	assert(q);
	assert(q->size != 0);
	// 检查队列中是否只有一个节点
	if (q->front->next == NULL)
	{
		free(q->front);
		// 队列变为空,队首和队尾指针都设置为NULL
		q->front = q->rear = NULL;
	}
	// 多个节点
	else
	{
		// 保存下一个节点的指针
		QNode* next = q->front->next;
		// 释放队首节点
		free(q->front);
		// 更新队首指针为下一个节点
		q->front = next;
	}
	q->size--;
}

//读取队头数据
QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(q->front);
	return q->front->val;
}

//读取队尾数据
QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(q->rear);
	return q->rear->val;
}

//统计队列数据个数
int QueueSize(Queue* q)
{
	assert(q);
	return q->size;
}

//判断队列是否为空
bool QueueEmpty(Queue* q)
{
	assert(q);
	return q->size == 0;
}

//打印队列元素
void QueuePrint(Queue* q)
{
	assert(q);
	QNode* cur = q->front;
	QNode* tail = q->rear;
	printf("队头->");
	while (cur != tail->next)
	{
		printf("%d->", cur->val);
		cur = cur->val;
	}
	printf("队尾\n");
}

2.循环队列

 Queue.h

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>

typedef int QDataType;

#define MAXSIZE 30
typedef struct
{
	QDataType* data;
	int front;
	int rear;
}Queue;

//初始化
void QueueInit(Queue* q);
//销毁
void QueueDestroy(Queue* q);

//队尾插入
void QueuePush(Queue* q, QDataType x);
//队头删除
void QueuePop(Queue* q);

//读取队头数据
QDataType QueueFront(Queue* q);
//读取队尾数据
QDataType QueueBack(Queue* q);

//统计队列数据个数
int QueueSize(Queue* q);
//判断队列是否为空
bool QueueEmpty(Queue* q);

//打印队列元素
void QueuePrint(Queue* q);

//判断队列是否已满
bool QueueFull(Queue* q);

Queue.h

#define _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"

//初始化
void QueueInit(Queue* q)
{
	// 为队列的数据存储空间分配内存。
	q->data = (QDataType*)malloc(sizeof(QDataType) * MAXSIZE);
	if (q->data == NULL)
	{
		perror("malloc fail:");
		return;
	}
	// 初始化队首和队尾指针为0,表示队列为空
	q->front = q->rear = 0;
}

//销毁
void QueueDestroy(Queue* q)
{
	assert(q);
	free(q->data);
	q->data = NULL;
	q->front = q->rear = 0;
}

//队尾插入
void QueuePush(Queue* q, QDataType x)
{
	assert(q);
	if (QueueFull(q))
	{
		printf("队列已满\n");
		return;
	}
	q->data[q->rear] = x;

	// rear指针向后移动
	// (q->rear + 1) % MAXSIZE这段代码
	// 确保了rear指针的值始终在0到MAXSIZE-1的范围内循环
	q->rear = (q->rear + 1) % MAXSIZE;
}

//队头删除
void QueuePop(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	// front指针向后移动
	// (q->front + 1) % MAXSIZE这段代码
	// 确保了front指针的值始终在0到MAXSIZE-1的范围内循环
	q->front = (q->front + 1) % MAXSIZE;
}

//读取队头数据
QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->data[q->front];
}
//读取队尾数据
QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));

	// 当rear为0时,rear-1会导致负数索引,这在数组中是无效的  
	// 通过加上MAXSIZE并取模MAXSIZE,我们可以确保索引始终在有效范围内  
	// 这里(q->rear - 1 + MAXSIZE) % MAXSIZE计算的是队尾元素的索引
	return q->data[(q->rear - 1 + MAXSIZE) % MAXSIZE];
}

//统计队列数据个数
int QueueSize(Queue* q)
{
	assert(q);
	return (q->rear - q->front + MAXSIZE) % MAXSIZE;
}

//判断队列是否为空
bool QueueEmpty(Queue* q)
{
	assert(q);
	return q->front == q->rear;
}

//判断队列是否已满
bool QueueFull(Queue* q)
{
	assert(q);
	return q->front == (q->rear + 1) % MAXSIZE;
}

//打印队列元素
void QueuePrint(Queue* q)
{
	assert(q);
	int cur = q->front;
	printf("队头->");
	while (cur != q->rear)
	{
		printf("%d->", q->data[cur]);
		// 避免越界
		cur = (cur + 1) % MAXSIZE;
	}
	printf("队尾\n");
}

结束语

本篇博客大致介绍了数据结构——队列的内容。

感谢每位能点进来阅读的大佬们!!!

十分感谢!!!

求点赞收藏关注!!!

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

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

相关文章

91. UE5 RPG 实现拖拽装配技能以及解除委托的绑定

在上一篇文章里&#xff0c;实现了通过选中技能&#xff0c;然后点击下方的装备技能插槽实现了技能的装配。为了丰富技能装配功能&#xff0c;在这一篇里&#xff0c;我们实现一下通过拖拽技能&#xff0c;实现拖拽功能&#xff0c;我们需要修改两个用户控件&#xff0c;一个就…

鸿蒙内核源码分析(信号生产篇) | 注意结构体的名字和作用.

信号生产 关于信号篇&#xff0c;本只想写一篇&#xff0c;但发现把它想简单了&#xff0c;内容不多&#xff0c;难度极大.整理了好长时间&#xff0c;理解了为何<<深入理解linux内核>>要单独为它开一章&#xff0c;原因有二 信号相关的结构体多&#xff0c;而且…

RTC碰到LXTAL低频晶振停振怎么办?

GD32F303的RTC模块框图如下图所示&#xff0c;RTC时钟源可选择HXTAL/128、LXTAL或IRC40K&#xff0c;一般为了实现更精准的RTC时间&#xff0c;MCU系统均会外挂32.768KHz LXTAL低频晶振&#xff0c;但由于低频晶振负阻抗较大&#xff0c;不容易起振&#xff0c;若外部电路布线、…

vue3 antdv3 去掉Modal的阴影背景,将圆角边框改为直角的显示,看上去不要那么的立体的样式处理。

1、来个没有处理的效果图&#xff1a; 这个有立体的效果&#xff0c;有阴影的效果。 2、要处理一下样式&#xff0c;让这个阴影的效果去掉&#xff1a; 图片的效果不太明显&#xff0c;但是阴影效果确实没有了。 3、代码&#xff1a; /* 去掉遮罩层阴影 */.ant-modal-mask {…

Maven命令传pom或者jar异常

上传命令&#xff1a;mvn deploy:deploy-file -Durlhttp://****&#xff1a;****/repository/chntdrools7741-releases -DrepositoryId**** -DfileD:/tempRepo/org/kie/kie-api-parent/7.69.0.Final/kie-api-parent-7.69.0.Final.pom -DpomFileD:/tempRepo/org/kie/kie-api-par…

三级_网络技术_39_综合题(命令)

一、 如下图所示&#xff0c;某校园网用10Gbps 的POS技术与Internet相连&#xff0c;POS接网的幅格式早SDH。路由协议的选择方案是校园网内部采用OSPF协议&#xff0c;校园网与lntemnet的连接使用静态路由协议。校园网内的路由器R1设为DHCP服务器&#xff0c;可分配的IP地址是…

【22-54】创建者模式(详解五大模式)

目录 一.创建者模式介绍 二.单例设计模式 2.1 单例模式的结构 2.2 单例模式的实现 2.2.1.1 饿汉式-方式1&#xff08;静态变量方式&#xff09; 2.2.1.2 饿汉式-方式2&#xff08;静态代码块方式&#xff09; 2.2.2.1 懒汉式-方式1&#xff08;线程不安全&#xff09; 2…

用手机写一本电子书

第1步、进入Andi.cn网站 第2步、点击登录&#xff0c;注册用户 第3步、点击去创作&#xff0c;进入创作页面 第4步、点击右下角的小笔&#xff0c;写一篇文章 第5步、下翻&#xff0c;点击提交按钮 第6步、再写一篇文章 第7步、点击栏目设计 第8步、进入栏目设计&#xff0c;点…

excel卓越之道笔记

excel快捷键 1.Alt+=一键求和 2.Tab补全函数名称 3.CONCAT可以连选,CONCATENATE只能一个单元格一个单元格点选 4.excel365用不了phonetic函数,但是可以用concat代替 5.textjoin连接标识码,在Arcgis中筛选出所需要素,也是很好用的 6.法1:alt+; 定位可见单元格,复制后只…

Linux入门——01常用命令

0.命令行解释器shell 用户无法直接给操作系统指令&#xff0c;需要经过shell,才能让操作系统明白。如果用户对操作系统非法操作&#xff0c;会有shell保护。shell本身也是一个进程&#xff0c;当然&#xff0c;用户给shell的指令&#xff0c;shell会派生出子进程进行执行&#…

Unity Protobuf3.21.12 GC 问题(反序列化)

背景&#xff1a;Unity接入的是 Google Protobuf 3.21.12 版本&#xff0c;排查下来反序列化过程中的一些GC点&#xff0c;处理了几个严重的&#xff0c;网上也有一些分析&#xff0c;这里就不一一展开&#xff0c;默认读者已经略知一二了。 如果下面有任何问题请评论区留言提…

【Kubernetes中如何对etcd进行备份和还原】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

不同路径

不同路径 思路&#xff1a; 法一&#xff1a;动态规划 const int N 110; class Solution { int dp[N][N];//dp[i][j]&#xff1a;从起点走到 i j的路径个数。 public:int uniquePaths(int m, int n) {for(int i1;i<n;i){dp[1][i]1;} for(int i1;i<m;i) dp[i][1]1;f…

day36.动态规划+重载操作符

动态规划好难啊(ಥ﹏ಥ) 终于搞懂0-1背包问题的二维数组转一维数组优化的问题了。如图所示: 将二维数组转换成一位数组的核心就是&#xff0c;dp[i][j]选取时&#xff0c;他的值只与dp[i-1][j]&#xff0c;也就是上一行有关&#xff0c;所以可以引出使用一维数组代替二维数组…

python 使用宝塔面板在云服务器上搭建 flask

打开宝塔面板到【网站】&#xff0c;选择【python项目】&#xff0c;点【添加python项目】 填上相关信息&#xff1a; 注意&#xff1a;项目端口是你打算在外网用来访问flask的端口号 勾选【放行端口】&#xff0c;并提交 到阿里云里&#xff0c;选择安全组 手动添加放行端口…

datawind可视化查询-其他函数

飞书文档学习链接:https://www.volcengine.com/docs/4726/47275 1. 用户名函数 用户名函数并非 ClickHouse 官方函数,而是与项目用户信息相结合,用于返回当前使用用户的指定信息的函数。 USERNAME()可返回当前用户的用户名,如下所示。该函数也可与其他函数组合使用 2. J…

51 无显式主键时 mysql 增加的 DB_ROW_ID

前言 这里主要是 探讨, 在我们创建了一个 无主键的数据表, 然后 mysql 会为我们增加的这一个 DB_ROW_ID 的相关 新建一个无主键字段的数据表如下 CREATE TABLE implicit_id_table (username varchar(16) DEFAULT NULL,age int(11) DEFAULT NULL ) ENGINEInnoDB DEFAULT CH…

MySQL范围分区分区表

什么是范围分区分区表&#xff1f; 范围分区是一种根据某个列的范围值来分割表数据的分区方式。在范围分区中&#xff0c;每个分区都有自己的范围条件&#xff0c;当插入数据时&#xff0c;MySQL会根据指定的范围条件将数据分配到相应的分区中。这种分区方式可以使得表的数据按…

2024前端面试题-css篇

1.p和div区别 p自带有一定margin-top和margin-bottom属性值&#xff0c;而div两个属性值为0&#xff0c;也便是两个p之间有不一定间距&#xff0c;而div没有。 2.对css盒模型的理解 标准盒模型&#xff1a;content不包括padding、border、margin ie盒模型&#xff1a;conten…

关于我的生信笔记开通《知识星球》

关于知识星球 1. 为什么到现在才开通《知识星球》 从很早关注我的同学应该了解小杜的知识分享历程&#xff0c;小杜是从2021年11月底开始进入此“坑”&#xff0c;一直坚持到现在&#xff0c;马上3年了&#xff08;24年11月底到期&#xff09;。自己也从一个小青年&#xff0…