初级数据结构:栈和队列

目录

一、栈

(一)、栈的定义

(二)、栈的功能

(三)、栈的实现

1.栈的初始化

2.动态扩容

3.压栈操作

4.出栈操作

5.获取栈顶元素

6.获取栈顶元素的有效个数

7.检查栈是否为空

8.栈的销毁

9.完整代码

二、队列

(一)、队列的定义

(二)、队列的功能

(三)、队列的实现

1、队列初始化

2、队尾入队列

3、队头出队列

4、获取队列头部元素

5、获取队列尾部元素

6、获取队列有效元素个数

7、检查队列是否为空

8、队列的销毁

(四)、循环队列

三、栈和队列的比较


一、栈

(一)、栈的定义

栈是一种遵循后进先出(LIFO,Last In First Out)原则的数据结构。栈的主要操作包括入栈(Push)和出栈(Pop)。入栈操作是将元素添加到栈顶,这一过程中,栈顶指针上移,新元素被放置在栈顶位置;出栈操作则是移除栈顶元素,同时栈顶指针下移。此外,还可以通过获取栈顶元素(Top)操作来查看栈顶元素但不将其移除。

形象的来说,压栈操作就像堆叠盘子,一个盘子放在另一个盘子上。当你想取出盘子时,你必定会从顶部取出。如果从中间取盘子,那就有盘子打烂的风险,这也就是出栈操作。

栈也是一种线性表,在对于表达式求值和符号匹配等方面有很大用途。

如果你已经学完了顺序表,那么栈的实现对你来说轻而易举。

(二)、栈的功能

1、压栈(Push):将一个元素添加到栈顶。比如我们往弹夹里装子弹的动作就相当于入栈操作。

2、出栈(Pop):从栈顶移除一个元素。对应弹夹射击时弹出子弹的过程。

3、获取栈顶元素(Peek):获取栈顶元素,但不将其从栈中移除。这就像是我们查看弹夹最上面的子弹是什么类型,但不把它射出。

4、获取栈中有效元素个数(Size):就像我们查看弹夹中的子弹数目。

5、判断栈是否为空(IsEmpty):检查栈中是否没有元素。当弹夹里没有子弹时,就可以说栈为空。

(三)、栈的实现

对于栈来说,我们可以使用数组或者链表来实现栈。相对而言,使用数组来实现栈比用链表来实现更优。因为进行栈的压栈操作时,数组尾部插入数据的代价更小。而如果使用双向链表又过于麻烦。因此,我们对于栈的结构体定义如下:

typedef struct Stack
{int* data;//动态数组int top;//指向栈顶元素,在后续的初始化中,将其初始化为0还是-1,决定着top指向栈顶元素还是指向栈顶元素后一位int capicity;//容量大小}Stack;

1.栈的初始化

和顺序表一样,我们需要先进行动态内存申请一定的空间代码如下:

void StackInit(Stack* ps)
{//动态内存申请4个整形空间,空间申请小一些方便检查后续扩容是否正确int* ptr = (int*)malloc(sizeof(int) * 4);if (ptr == NULL){//判断空间是否申请成功,失败则打印错误信息perror("StackInit::malloc");return;}ps->data = ptr;ps->capicity = 4;//我这里是让top指向栈顶元素的后一位,看自己的想法//这里top的指向如何会影响后续获取栈顶元素功能实现的代码ps->top = 0;
}

2.动态扩容

这个动态扩容由于我们使用数组来实现栈,因此动态扩容函数与顺序表基本一致,代码如下:

void Expansion(Stack* ps)
{assert(ps);if (ps->top == ps->capicity){printf("空间不足\n");int* ptr = (int*)realloc(ps->data, sizeof(int) * (ps->capicity * 2));if (ptr == NULL){perror("Expansion::realloc");return;}ps->data = ptr;capicity*=2;}
}

3.压栈操作

实质上就是顺序表的尾插。代码如下:

void StackPush(Stack* ps)
{assert(ps);//判断是否需要扩容Expansion(ps);printf("请输入数字\n");//如果你的top初始化为-1,那么这里就需要先top++,再赋值scanf("%d", &ps->data[ps->top]);printf("入栈成功\n");ps->top++;
}

4.出栈操作

实质上就是顺序表的尾删,直接使top指针往前移一步,等下次压栈操作后,数据覆盖即可达到出栈作用,代码如下:

void StackPop(Stack* ps)
{assert(ps);if (ps->top > 0){ps->top--;printf("出栈成功\n");}else{printf("栈中无元素\n");}
}

5.获取栈顶元素

因为我们这里top初始化为0,所以top一直指向栈顶元素后一位,如果我们想要获取栈顶元素就需要使top减一,代码如下:

int StackTop(Stack* ps)
{assert(ps);return ps->data[ps->top-1];
}

6.获取栈顶元素的有效个数

直接返回top即可,代码如下:

int StackSize(Stack* ps)
{assert(ps);return ps->top;
}

7.检查栈是否为空

当top与初始化的top数相等时,栈就为空,代码如下:

int StackEmpty(Stack* ps)
{assert(ps);if (ps->top == 0){return 0;}else{return ps->top;}
}

8.栈的销毁

和顺序表的销毁一致,先释放栈的空间,然后将指针data置空,容量也即capicity和top都置为0。代码如下:

void StackDestroy(Stack* ps)
{assert(ps);free(ps->data);ps->data = NULL;ps->capicity = 0;ps->top = 0;printf("销毁成功\n");
}

至此,一个基础的栈就实现了,完整代码如下。

9.完整代码

stack.h中: 

#pragma once
#pragma warning(disable : 4996)
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef struct Stack
{int* data;int top;int capicity;
}Stack;
// 初始化栈 
void StackInit(Stack* ps);
//动态扩容
void Expansion(Stack* ps);
// 入栈 
void StackPush(Stack* ps);
// 出栈 
void StackPop(Stack* ps);
// 获取栈顶元素 
int StackTop(Stack* ps);
// 获取栈中有效元素个数 
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps);
// 销毁栈 
void StackDestroy(Stack* ps);

Fstack.c中:

#include"stack.h"
void StackInit(Stack* ps)
{int* ptr = (int*)malloc(sizeof(int) * 4);if (ptr == NULL){perror("StackInit::malloc");return;}ps->data = ptr;ps->capicity = 4;ps->top = 0;
}void Expansion(Stack* ps)
{assert(ps);if (ps->top == ps->capicity){printf("空间不足\n");int* ptr = (int*)realloc(ps->data, sizeof(int) * (ps->capicity * 2));if (ptr == NULL){perror("Expansion::realloc");return;}ps->data = ptr;ps->capicity*=2; }
}void StackPush(Stack* ps)
{assert(ps);Expansion(ps);printf("请输入数字\n");scanf("%d", &ps->data[ps->top]);printf("入栈成功\n");ps->top++;
}void StackPop(Stack* ps)
{assert(ps);if (ps->top > 0){ps->top--;printf("出栈成功\n");}else{printf("栈中无元素\n");}
}int StackTop(Stack* ps)
{assert(ps);return ps->data[ps->top-1];
}int StackSize(Stack* ps)
{assert(ps);return ps->top;
}int StackEmpty(Stack* ps)
{assert(ps);if (ps->top == 0){return 0;}else{return ps->top;}
}void StackDestroy(Stack* ps)
{assert(ps);free(ps->data);ps->data = NULL;ps->capicity = 0;ps->top = 0;printf("销毁成功\n");
}

stack.h:

#include"stack.h"
Stack ps;
int main()
{// 初始化栈 StackInit(&ps);int a,b;do{printf("请输入数字\n");scanf("%d", &a);switch (a){case 1:// 入栈 StackPush(&ps);break;case 2:// 出栈 StackPop(&ps);break;case 3:// 获取栈顶元素 b = StackTop(&ps);printf("栈顶元素:%d\n", b);break;case 4:// 获取栈中有效元素个数 printf("%d\n", StackSize(&ps));break;case 5:// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 printf("%d\n", StackEmpty(&ps));break;case 0:// 销毁栈 StackDestroy(&ps);printf("退出\n");break;}} while (a);return 0;
}

二、队列

(一)、队列的定义

在数据结构的大家庭中,队列是一位独特而重要的成员。想象一下,在超市结账的队伍里,先来的顾客排在前面先结账离开,后来的顾客则依次在队尾加入等待,这就是典型的 “先进先出(FIFO,First In First Out)原则,而队列正是这种原则在计算机领域的完美体现。

队列作为一种特殊的线性表,它的操作被严格限定在两端进行。一端被称为队尾(rear),专门用于插入新元素,就像新顾客加入结账队伍的末尾;另一端是队头(front),负责删除元素,恰似排在队伍最前面的顾客完成结账后离开。这种操作受限的特性,赋予了队列先进先出的独特性质,也使得它在众多算法和实际应用中发挥着关键作用 。无论是操作系统中的任务调度、网络通信中的数据包处理,还是广度优先搜索算法中的节点遍历,队列都扮演着至关重要的角色

和栈一样,它的实现同样可以使用数组和链表来实现。因为上述我们使用了数组来实现栈,故此次队列的实现我们采用链表来实现,也即链式队列。

在链式队列中,每个节点包含数据域和指针域,数据域用于存储队列元素,指针域则指向下一个节点。队列通过两个指针来管理:头指针(front)指向链表的头节点,代表队头;尾指针(rear)指向链表的尾节点,代表队尾 。

入队操作时,创建一个新节点存储新元素,然后将其插入到链表的尾部,同时更新尾指针指向新节点;出队操作则是删除链表头部的节点,返回该节点的数据,并更新头指针指向下一个节点。比如,有一个初始为空的链式队列,当元素 3 入队时,创建一个新节点存储 3,此时头指针和尾指针都指向这个新节点 。接着元素 4 入队,创建新节点并插入到链表尾部,尾指针更新指向新节点。当出队时,删除头指针指向的节点(包含元素 3),头指针移动到下一个节点(包含元素 4) 。

链式队列的优点是不需要预先知道队列的最大容量,因为链表可以动态地分配内存,避免了顺序队列可能出现的溢出问题;而且在进行插入和删除操作时,只需要修改指针,不需要移动大量元素,效率较高。然而,链式队列也有缺点,由于每个节点都需要额外的指针域来指向下一个节点,这会占用更多的内存空间;并且链表不支持随机访问,访问特定位置的元素需要从头开始遍历链表,时间复杂度较高 。

(二)、队列的功能

1、队尾入队列

2、队头出队列

3、获取队列头部元素

4、获取队列尾部元素

5、获取队列有效元素个数

6、检查队列是否为空

(三)、队列的实现

因为链式队列需要头指针和尾指针,因此我们不能只像链表那样只用一个结构体。我们需要再使用一个结构体以使进行函数传参时更方便,故结构体的构造如下:

//节点
typedef struct QueueNode
{int data;struct QueueNode* next;
}Qnode;
typedef struct Queue
{//头指针Qnode* head;//尾指针Qnode* tail;//计数存储数据个数int size;
}Queue;

1、队列初始化

void QueueInit(Queue* q)
{assert(q);q->head = NULL;q->tail = NULL;q->size = 0;
}

因为还未存储数据,故头指针和尾指针都置空,以防止野指针出现。不要忘记将size也初始化为0;

2、队尾入队列

先看图:

如果我们是往队列中插入第一个节点,此时头指针和尾指针都指向空。我们就需要将头指针和尾指针都指向新节点,再将节点的next指针指向NULL;

而如果我们插入新节点时,队列中已有数据存储,也即有节点存在,将上述两图结合起来看,第一张代表插入新节点前,第二张代表插入新节点后。我们需要先使尾指针指向的节点的next指针指向新节点,再让尾指针指向新节点,最后再使新节点置空即可。代码如下:

void QueuePush(Queue* q)
{assert(q);//申请新节点Qnode* ptr = (Qnode*)malloc(sizeof(Qnode));if (ptr == NULL){perror("QueuePush::malloc");return;}printf("请输入数字\n");scanf("%d", &ptr->data);//提前将新节点next指针置空ptr->next = NULL;//判断队列是否为空if (q->head == NULL && q->tail == NULL){q->head = q->tail = ptr;}else{q->tail->next = ptr;q->tail = ptr;}q->size++;
}

3、队头出队列

先看代码:

void QueuePop(Queue* q)
{assert(q);//判断头指针是否为空,为空那还出什么队列assert(q->head);//先存储头指针指向的节点的下一个节点的位置Qnode* headnext = q->head->next;//释放头指针指向的节点空间free(q->head);//再让头指针指向之前存储的节点q->head = headnext;//如果队列中只有一个节点,那释放空间后,头指针是空,但//尾指针没有被置为空,而是处于野指针状态,因此也要将//尾指针置空if (q->head == NULL){q->tail = NULL;}q->size--;printf("出队列成功\n");
}

4、获取队列头部元素

我们知道在链式队列中,链表头即是队头,链表尾即是队尾。获取队列头部元素即可以直接通过头指针获取。代码如下:

int QueueFront(Queue* q)
{assert(q);if (q->head == NULL){printf("队列无元素\n");return NULL;}return q->head->data;
}

5、获取队列尾部元素

和获取队列头部元素一致,更改指针即可。代码如下:

int QueueBack(Queue* q)
{assert(q);if (q->tail == NULL){printf("队列无元素\n");return NULL;}return q->tail->data;
}

6、获取队列有效元素个数


int QueueSize(Queue* q)
{assert(q);return q->size;
}

7、检查队列是否为空

为空返回0,不为空返回非零结果。

int QueueEmpty(Queue* q)
{assert(q);return q->size;
}

8、队列的销毁

队列的销毁和链表的销毁一致,遍历一遍,在遍历中存储下一个节点的位置,销毁当前节点,更新条件即可。代码如下:

void QueueDestroy(Queue* q)
{assert(q);Qnode* ptr = q->head;if (q->head == NULL){return;}while (ptr){Qnode* ptrnext = ptr->next;free(ptr);ptr = ptrnext;}q->head = q->tail = NULL;printf("队列销毁成功\n");q->size = 0;
}

至此,一个基础的队列就完成了,完整代码如下:

9、完整代码

queue.h:

#pragma once
#pragma warning(disable : 4996)
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
//节点
typedef struct QueueNode
{int data;struct QueueNode* next;
}Qnode;
typedef struct Queue
{//头指针Qnode* head;//尾指针Qnode* tail;//计数存储数据个数int size;
}Queue;
// 初始化队列 
void QueueInit(Queue* q);
// 队尾入队列 
void QueuePush(Queue* q);
// 队头出队列 
void QueuePop(Queue* q);
// 获取队列头部元素 
int QueueFront(Queue* q);
// 获取队列队尾元素 
int QueueBack(Queue* q);
// 获取队列中有效元素个数 
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q);
// 销毁队列 
void QueueDestroy(Queue* q);

Fqueue.c:

#include"queue.h"
void QueueInit(Queue* q)
{assert(q);q->head = NULL;q->tail = NULL;q->size = 0;
}void QueuePush(Queue* q)
{assert(q);//申请新节点Qnode* ptr = (Qnode*)malloc(sizeof(Qnode));if (ptr == NULL){perror("QueuePush::malloc");return;}printf("请输入数字\n");scanf("%d", &ptr->data);//提前将新节点next指针置空ptr->next = NULL;//判断队列是否为空if (q->head == NULL && q->tail == NULL){q->head = q->tail = ptr;}else{q->tail->next = ptr;q->tail = ptr;}q->size++;
}void QueuePop(Queue* q)
{assert(q);//判断头指针是否为空,为空那还出什么队列assert(q->head);//先存储头指针指向的节点的下一个节点的位置Qnode* headnext = q->head->next;//释放头指针指向的节点空间free(q->head);//再让头指针指向之前存储的节点q->head = headnext;//如果队列中只有一个节点,那释放空间后,头指针是空,但//尾指针没有被置为空,而是处于野指针状态,因此也要将//尾指针置空if (q->head == NULL){q->tail = NULL;}q->size--;printf("出队列成功\n");
}int QueueFront(Queue* q)
{assert(q);if (q->head == NULL){printf("队列无元素\n");return NULL;}return q->head->data;
}int QueueBack(Queue* q)
{assert(q);if (q->tail == NULL){printf("队列无元素\n");return NULL;}return q->tail->data;
}int QueueSize(Queue* q)
{assert(q);return q->size;
}int QueueEmpty(Queue* q)
{assert(q);return q->size;
}void QueueDestroy(Queue* q)
{assert(q);Qnode* ptr = q->head;if (q->head == NULL){return;}while (ptr){Qnode* ptrnext = ptr->next;free(ptr);ptr = ptrnext;}q->head = q->tail = NULL;printf("队列销毁成功\n");q->size = 0;
}

queue.c:

#include"queue.h"
Queue Que;
int main()
{int a;// 初始化队列 QueueInit(&Que);do{printf("输入数字\n");scanf("%d", &a);switch (a){case 1:// 队尾入队列 QueuePush(&Que);break;case 2:// 队头出队列 QueuePop(&Que);break;case 3:// 获取队列头部元素 printf("%d\n",QueueFront(&Que));break;case 4:// 获取队列队尾元素 printf("%d\n",QueueBack(&Que));break;case 5:// 获取队列中有效元素个数 printf("%d\n",QueueSize(&Que));break;case 6:// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 printf("%d\n",QueueEmpty(&Que));break;case 0:// 销毁队列 QueueDestroy(&Que);break;}} while (a);return 0;
}

(四)、循环队列

在了解顺序队列时,我们提到了假溢出问题,而循环队列就是为了解决这个问题而引入的。循环队列是一种特殊的顺序队列,它将顺序队列的首尾相连,把存储队列元素的表从逻辑上看成一个环。

在循环队列中,我们仍然使用front指针指示队头位置,rear指针指示队尾位置。当rear指针到达数组末尾时,如果数组前面还有空闲空间,它可以重新回到数组开头,继续利用前面的空闲空间。例如,假设我们有一个大小为 5 的循环队列数组,初始时front和rear都为 0 。当进行入队操作时,rear指针依次移动到 1、2、3、4 位置。当rear到达 4 时,如果再进行入队操作,rear就会回到 0 位置(通过取模运算实现)。

判断循环队列是否为空和满的条件与普通顺序队列有所不同。在循环队列中,当front等于rear时,表示队列为空;而当(rear + 1) % 队列容量 == front时,表示队列已满。这里的取模运算保证了rear指针在到达数组末尾时能够回到开头,实现循环的效果。

三、栈和队列的比较

栈和队列虽然都是线性数据结构,但它们在很多方面存在差异:

  1. 进出顺序:栈遵循后进先出(LIFO)原则,最后进入栈的元素最先出栈;而队列遵循先进先出(FIFO)原则,最先进入队列的元素最先出队 。例如,在程序调用栈中,函数调用是按照后进先出的顺序进行的,而在网络请求队列中,请求是按照先进先出的顺序被处理的。
  2. 插入删除操作:栈的插入(入栈)和删除(出栈)操作都在栈顶进行;队列的插入(入队)操作在队尾进行,删除(出队)操作在队头进行 。比如,往栈中添加元素就像往一摞盘子上放盘子,只能放在最上面,从栈中取出元素也只能从最上面取;而队列中添加元素就像排队买票,新来的人站在队伍末尾,离开的人从队伍最前面离开。
  3. 遍历数据速度:栈只能从栈顶开始遍历,若要访问栈底元素,需要依次弹出栈顶元素,遍历过程中需要开辟临时空间来保存数据状态,以确保遍历前后数据的一致性;队列基于地址指针进行遍历,可以从队头或队尾开始遍历,但不能同时进行双向遍历,遍历过程中不会改变数据结构,所以无需开辟额外空间,遍历速度相对较快 。例如,在遍历一个包含 100 个元素的栈时,如果要获取栈底元素,需要将栈顶的 99 个元素依次弹出并保存,然后才能访问栈底元素,最后再将弹出的元素依次压回栈中;而遍历一个包含 100 个元素的队列时,从队头开始遍历,直接按照顺序访问每个元素即可。
  4. 限定条件:栈只允许在一端进行插入和删除操作;队列允许在一端插入,在另一端删除操作 。这是它们最基本的操作限制,决定了它们在不同场景下的适用性。例如,在实现表达式求值时,利用栈的特性可以方便地处理运算符优先级;而在实现任务调度时,利用队列的特性可以保证任务按照提交顺序依次执行。
  5. 应用场景:栈常用于处理具有后进先出特性的问题,如函数调用、表达式求值、括号匹配等;队列常用于处理具有先进先出特性的问题,如网络请求处理、消息队列、任务调度等 。例如,在编译器中,使用栈来处理函数调用和递归,确保函数的正确返回和局部变量的正确管理;在分布式系统中,使用消息队列来异步处理消息,提高系统的吞吐量和响应速度。

如果你认为你已经对栈和队列掌握完全,那你可以尝试做一下下面的题目看看:

1.有效的括号

2.用队列实现栈

3.用栈实现队列

4.设计循环队列

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LLM:DeepSeek 系列(二)

原文链接 3、DeepSeek-V2 DeepSeek-V2 发布于 2024 年 5 月&#xff0c;为多领域专家&#xff08;MoE&#xff09;语言模型&#xff0c;包含总共 2360 亿个参数&#xff0c;其中每个词元激活 210 亿个参数&#xff0c;并支持 12.8 万个词元的上下文长度。DeepSeek-V2 采用包括…

【学术投稿】第五届计算机网络安全与软件工程(CNSSE 2025)

重要信息 官网&#xff1a;www.cnsse.org 时间&#xff1a;2025年2月21-23日 地点&#xff1a;中国-青岛 简介 第五届计算机网络安全与软件工程&#xff08;CNSSE 2025&#xff09;将于2025年2月21-23日在中国-青岛举行。CNSSE 2025专注于计算机网络安全、软件工程、信号处…

开源项目介绍-词云生成

开源词云项目是一个利用开源技术生成和展示词云的工具或框架&#xff0c;广泛应用于文本分析、数据可视化等领域。以下是几个与开源词云相关的项目及其特点&#xff1a; Stylecloud Stylecloud 是一个由 Maximilianinir 创建和维护的开源项目&#xff0c;旨在通过扩展 wordclou…

Docker基础以及单体实战

Docker 一、Docker1.1 Docker组成1.2 Dcoker运行图1.3 名称空间Namepace 1.4 docker、Docker compose、kubermetes 二、Docker安装2.1 在线Docker安装2.2 使用官方通用安装脚本2.3 二进制安装Docker三、Docker基础命令3.1 启动类3.2 镜像类3.3 容器类3.4 网络类3.5 Docker comp…

2025年日祭

本文将同步发表于洛谷&#xff08;暂无法访问&#xff09;、CSDN 与 Github 个人博客&#xff08;暂未发布&#xff09; 本蒟自2025.2.8开始半停课。 任务计划&#xff08;站外题与专题&#xff09; 数了一下&#xff0c;通过人数比较高的题&#xff0c;也就是我准备补的题&a…

UIAbility 生命周期方法

生命周期流程图 UIAbility的生命周期官方文档地址https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V13/uiability-lifecycle-V13 1. onCreate(want: Want, launchParam: LaunchParam) 触发时机&#xff1a;Ability首次创建时 作用&#xff1a;初始化核心资源…

模型 冗余系统(系统科学)

系列文章分享模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。为防故障、保运行的备份机制。 1 冗余系统的应用 1.1 冗余系统在企业管理中的应用-金融行业信息安全的二倍冗余技术 在金融行业&#xff0c;信息安全是保障业务连续性和客户资产安全的关键。随着数字化…

【大数据技术】Spark分布式实现词频统计(hadoop+python+spark)

Spark分布式实现词频统计&#xff08;hadooppythonspark&#xff09; 搭建完全分布式高可用大数据集群&#xff08;VMwareCentOSFinalShell&#xff09; 搭建完全分布式高可用大数据集群&#xff08;HadoopMapReduceYarn&#xff09; 本机PyCharm远程连接CentOS虚拟机&#x…

28.<Spring博客系统⑤(部署的整个过程(CentOS))>

引入依赖 Spring-boot-maven-plugin 用maven进行打包的时候必须用到这个插件。看看自己pom.xml中有没有这个插件 并且看看配置正确不正常。 注&#xff1a;我们这个项目打的jar包在30MB左右。 <plugin><groupId>org.springframework.boot</groupId><artif…

C++Primer学习(2.2)

2.2 变量 变量提供一个具名的、可供程序操作的存储空间。C中的每个变量都有其数据类型,数据类型决定着变量所占内存空间的大小和布局方式、该空间能存储的值的范围&#xff0c;以及变量能参与的运算。对C程序员来说,“变量(variable)”和“对象(object)”一般可以互换使用。 术…

电脑开机提示按f1原因分析及终极解决方法来了

经常有网友问到一个问题&#xff0c;我电脑开机后提示按f1怎么解决&#xff1f;不管理是台式电脑&#xff0c;还是笔记本&#xff0c;都有可能会遇到开机需要按F1&#xff0c;才能进入系统的问题&#xff0c;引起这个问题的原因比较多&#xff0c;今天小编在这里给大家列举了比…

Linux系统命令无法使用(glib库相关问题)

1.背景描述 Yum强制安装了一些软件&#xff0c;安装软件成功无报错&#xff0c;完成后不久突然发现系统出问题了&#xff0c;所有的命令无法使用了&#xff0c;如ls、mv、cat等基本命令报错。 relocation error&#xff1a; /lib64/libpthread.so.0: symbol_libc_dl_error_tsd …

Jupyter Notebook自动保存失败等问题的解决

一、未生成配置文件 需要在命令行中&#xff0c;执行下面的命令自动生成配置文件 jupyter notebook --generate-config 执行后会在 C:\Users\用户名\.jupyter目录中生成文件 jupyter_notebook_config.py 二、在网页端打开Jupyter Notebook后文件保存失败&#xff1b;运行代码…

【含文档+PPT+源码】基于Python校园跑腿管理系统设计与实现

项目介绍 本课程演示的是一款基于Python校园跑腿管理系统设计与实现&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Python学习者。 1.包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本套系统 3.…

TypeScript 中的联合类型:灵活的类型系统

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

WebStorm设置Vue Component模板

下载vue.js插件 下面有模板样例 Composition API&#xff1a;这是 Vue 3 的一项新特性&#xff0c;允许通过 setup 函数来组织组件逻辑。Options API&#xff1a;这是 Vue 2 和 Vue 3 都支持的传统方式&#xff0c;通过定义组件的 data、methods、computed 等来组织逻辑。 Comp…

解锁AI语音魅力——yoyo鹿鸣在线语音合成器,让创意声音即刻绽放!

yoyo鹿鸣-在线语音合成 人工智能语音克隆生成&#xff0c;二次元&#xff5e; AI工具 | AI探金 可以在AI探金社区来找我&#xff5e; yoyo鹿鸣 - 在线语音生成器 需求人群&#xff1a; 有语音合成需求的用户。 使用场景示例&#xff1a; 合成yoyo鹿鸣语音 等等 产品特色&a…

基于STM32的智能鱼缸水质净化系统设计

&#x1f91e;&#x1f91e;大家好&#xff0c;这里是5132单片机毕设设计项目分享&#xff0c;今天给大家分享的是智能鱼缸水质净化系统。 目录 1、设计要求 2、系统功能 3、演示视频和实物 4、系统设计框图 5、软件设计流程图 6、原理图 7、主程序 8、总结 1、设计要求…

t113-qt

修改QT配置: # # qmake configuration for building with arm-linux-gnueabi-g ## MAKEFILE_GENERATOR UNIX # CONFIG incremental # QMAKE_INCREMENTAL_STYLE sublib# include(../common/linux.conf) # include(../common/gcc-base-unix.conf) # inc…

apisix的real-ip插件使用说明

k8s集群入口一般都需要过负载均衡&#xff0c;然后再到apisix。 这时候如果后台业务需要获取客户端ip&#xff0c;可能拿到的是lb或者网关的内网ip。 这里一般要获取真实ip需要做几个处理。 1. 负载均衡上&#xff0c;一般支持配置获取真实ip参数&#xff0c;需要配置上。然…