初级数据结构:栈和队列
目录
一、栈
(一)、栈的定义
(二)、栈的功能
(三)、栈的实现
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指针在到达数组末尾时能够回到开头,实现循环的效果。
三、栈和队列的比较
栈和队列虽然都是线性数据结构,但它们在很多方面存在差异:
- 进出顺序:栈遵循后进先出(LIFO)原则,最后进入栈的元素最先出栈;而队列遵循先进先出(FIFO)原则,最先进入队列的元素最先出队 。例如,在程序调用栈中,函数调用是按照后进先出的顺序进行的,而在网络请求队列中,请求是按照先进先出的顺序被处理的。
- 插入删除操作:栈的插入(入栈)和删除(出栈)操作都在栈顶进行;队列的插入(入队)操作在队尾进行,删除(出队)操作在队头进行 。比如,往栈中添加元素就像往一摞盘子上放盘子,只能放在最上面,从栈中取出元素也只能从最上面取;而队列中添加元素就像排队买票,新来的人站在队伍末尾,离开的人从队伍最前面离开。
- 遍历数据速度:栈只能从栈顶开始遍历,若要访问栈底元素,需要依次弹出栈顶元素,遍历过程中需要开辟临时空间来保存数据状态,以确保遍历前后数据的一致性;队列基于地址指针进行遍历,可以从队头或队尾开始遍历,但不能同时进行双向遍历,遍历过程中不会改变数据结构,所以无需开辟额外空间,遍历速度相对较快 。例如,在遍历一个包含 100 个元素的栈时,如果要获取栈底元素,需要将栈顶的 99 个元素依次弹出并保存,然后才能访问栈底元素,最后再将弹出的元素依次压回栈中;而遍历一个包含 100 个元素的队列时,从队头开始遍历,直接按照顺序访问每个元素即可。
- 限定条件:栈只允许在一端进行插入和删除操作;队列允许在一端插入,在另一端删除操作 。这是它们最基本的操作限制,决定了它们在不同场景下的适用性。例如,在实现表达式求值时,利用栈的特性可以方便地处理运算符优先级;而在实现任务调度时,利用队列的特性可以保证任务按照提交顺序依次执行。
- 应用场景:栈常用于处理具有后进先出特性的问题,如函数调用、表达式求值、括号匹配等;队列常用于处理具有先进先出特性的问题,如网络请求处理、消息队列、任务调度等 。例如,在编译器中,使用栈来处理函数调用和递归,确保函数的正确返回和局部变量的正确管理;在分布式系统中,使用消息队列来异步处理消息,提高系统的吞吐量和响应速度。
如果你认为你已经对栈和队列掌握完全,那你可以尝试做一下下面的题目看看:
1.有效的括号
2.用队列实现栈
3.用栈实现队列
4.设计循环队列
相关文章:
初级数据结构:栈和队列
目录 一、栈 (一)、栈的定义 (二)、栈的功能 (三)、栈的实现 1.栈的初始化 2.动态扩容 3.压栈操作 4.出栈操作 5.获取栈顶元素 6.获取栈顶元素的有效个数 7.检查栈是否为空 8.栈的销毁 9.完整代码 二、队列 (一)、队列的定义 (二)、队列的功能 (三)…...
携程Java开发面试题及参考答案 (200道-下)
insert 一行数据的时候加的是什么锁?为什么? 在 MySQL 中,当执行 INSERT 操作插入一行数据时,加锁的情况会因存储引擎和具体的事务隔离级别而有所不同。一般来说,在 InnoDB 存储引擎下,INSERT 操作加的是行级排他锁(Row Exclusive Lock),以下详细说明原因。 行级排他…...
Python从0到100(八十六):神经网络-ShuffleNet通道混合轻量级网络的深入介绍
前言: 零基础学Python:Python从0到100最新最全教程。 想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…...
98,【6】 buuctf web [ISITDTU 2019]EasyPHP
进入靶场 代码 <?php // 高亮显示当前 PHP 文件的源代码,通常用于调试或展示代码,方便用户查看代码逻辑 highlight_file(__FILE__);// 从 GET 请求中获取名为 _ 的参数值,并赋值给变量 $_ // 符号用于抑制可能出现的错误信息ÿ…...
54【ip+端口+根目录通信】
上节课讲到,根目录起到定位作用,比如我们搭建一个php网站后,注册系统是由根目录的register.php文件执行,那么我们给这个根目录绑定域名https://127.0.0.1,当我们浏览器访问https://127.0.0.1/register.php时࿰…...
计算机网络 应用层 笔记 (电子邮件系统,SMTP,POP3,MIME,IMAP,万维网,HTTP,html)
电子邮件系统: SMTP协议 基本概念 工作原理 连接建立: 命令交互 客户端发送命令: 服务器响应: 邮件传输: 连接关闭: 主要命令 邮件发送流程 SMTP的缺点: MIME: POP3协议 基本概念…...
解析PHP文件路径相关常量
PHP文件路径相关常量包括以下几个常量: __FILE__:表示当前文件的绝对路径,包括文件名。 __DIR__:表示当前文件所在的目录的绝对路径,不包括文件名。 dirname(__FILE__):等同于__DIR__,表示当前…...
蓝桥与力扣刷题(234 回文链表)
题目:给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。 示例 1: 输入:head [1,2,2,1] 输出:true示例 2: 输入&…...
【协议详解】卫星通信5G IoT NTN SIB33-NB 信令详解
一、SIB33信令概述 在5G非地面网络(NTN)中,卫星的高速移动性和广域覆盖特性使得地面设备(UE)需要频繁切换卫星以维持连接。SIB32提供了UE预测当前服务的卫星覆盖信息,SystemInformationBlockType33&#x…...
c语言练习题【数据类型、递归、双向链表快速排序】
练习1:数据类型 请写出以下几个数据的数据类型 整数 a a 的地址 存放a的数组 b 存放a的地址的数组 b的地址 c的地址 指向 printf 函数的指针 d 存放 d的数组 整数 a 的类型 数据类型是 int a 的地址 数据类型是 int*(指向 int 类型的指针) …...
SliverAppBar的功能和用法
文章目录 1 概念介绍2 使用方法3 示例代码 我们在上一章回中介绍了SliverGrid组件相关的内容,本章回中将介绍SliverAppBar组件.闲话休提,让我们一起Talk Flutter吧。 1 概念介绍 我们在本章回中介绍的SliverAppBar和普通的AppBar类似,它们的…...
deepseek 本地化部署和小模型微调
安装ollama 因为本人gpu卡的机器系统是centos 7, 直接使用ollama会报 所以ollama使用镜像方式进行部署, 拉取镜像ollama/ollama 启动命令 docker run -d --privileged -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama 查看ollama 是否启动…...
Heptagon 同步语言介绍
同步语言于20世纪80年代创立,用于建模、设计和实现实时关键反应系统。随着被控制系统的复杂性不断增加,执行速度成为一个重要标准。与此同时,处理器在核心数量上的增长超过了速度的提升。因此,我们正在寻求一种并行执行方式&#…...
Golang 并发机制-5:详解syn包同步原语
并发性是现代软件开发的一个基本方面,Go(也称为Golang)为并发编程提供了一组健壮的工具。Go语言中用于管理并发性的重要包之一是“sync”包。在本文中,我们将概述“sync”包,并深入研究其最重要的同步原语之一…...
数组排序算法
数组排序算法 用C语言实现的数组排序算法。 排序算法平均时间复杂度最坏时间复杂度最好时间复杂度空间复杂度是否稳定适用场景QuickO(n log n)O(n)O(n log n)O(log n)不稳定大规模数据,通用排序BubbleO(n)O(n)O(n)O(1)稳定小规模数据,教学用途InsertO(n)…...
利用腾讯云cloud studio云端免费部署deepseek-R1
1. cloud studio 1.1 cloud studio介绍 Cloud Studio(云端 IDE)是基于浏览器的集成式开发环境,为开发者提供了一个稳定的云端工作站。支持CPU与GPU的访问。用户在使用 Cloud Studio 时无需安装,随时随地打开浏览器即可使用。Clo…...
Codeforces Round 1002 (Div. 2)(A-D)
题目链接:Dashboard - Codeforces Round 1002 (Div. 2) - Codeforces A. Milya and Two Arrays 思路 数组a中不同数的数量*数组b的,就是能够组成不同数的数量 代码 void solve(){int n;cin>>n;int cnt10;int cnt20;map<int,bool> mp;ma…...
半导体器件与物理篇7 微波二极管、量子效应和热电子器件
基本微波技术 微波频率:微波频率涵盖约从0.1GHz到3000GHz,相当于波长从300cm到0.01cm。 分布效应:电子部件在微波频率,与其在较低频率的工作行为不同。 输运线:一个由电阻、电容、电感三种等效基本电路部件所组成的…...
Hot100之图论
200岛屿数量 题目 思路解析 把访问过的格子插上棋子 思想是先污染再治理,我们有一个inArea()函数,是判断是否出界了 我们先dfs()放各个方向遍历,然后我们再把这个位置标为0 我们岛屿是连着…...
CSS 样式化表格:从基础到高级技巧
CSS 样式化表格:从基础到高级技巧 1. 典型的 HTML 表格结构2. 为表格添加样式2.1 间距和布局2.2 简单的排版2.3 图形和颜色2.4 斑马条纹2.5 样式化标题 3. 完整的示例代码4. 总结 在网页设计中,表格是展示数据的常见方式。然而,默认的表格样式…...
DeepSeek相关技术整理
相关介绍 2024年12月26日,DeepSeek V3模型发布(用更低的训练成本,训练出更好的效果)671B参数,激活37B。2025年1月20日,DeepSeek-R1模型发布(仅需少量标注数据(高质量长cotÿ…...
Spring Boot框架下的单元测试
1. 什么是单元测试 1.1 基本定义 单元测试(Unit Test) 是对软件开发中最小可测单位(例如一个方法或者一个类)进行验证的一种测试方式。在 Java 后端的 Spring Boot 项目中,单元测试通常会借助 JUnit、Mockito 等框架对代码中核心逻辑进行快…...
OpenAI 实战进阶教程 - 第四节: 结合 Web 服务:构建 Flask API 网关
目标 学习将 OpenAI 接入 Web 应用,构建交互式 API 网关理解 Flask 框架的基本用法实现 GPT 模型的 API 集成并返回结果 内容与实操 一、环境准备 安装必要依赖: 打开终端或命令行,执行以下命令安装 Flask 和 OpenAI SDK: pip i…...
xss-labs靶场
xss-labs靶场 xss攻击类型 反射型xss 即攻击者将恶意脚本嵌入到url或者表单中,当用户访问特定的url或者提交表单时(用户端请求时),恶意脚本会执行 攻击需要用户点击恶意链接或访问包含恶意参数的url触发 存储型xss 即攻击者将恶意脚本提交…...
Eigen::Tensor使用帮助
0 引言 用python实现了某些算法之后,想转成C来获取更高的性能。但是python数组的操作太灵活了,尤其是3维、4维、5维等高维数组,以及它们的广播、数组坐标、切片等机制。还有numpy的pad、where等操作更是给C转换带来了更多的麻烦。 查阅了相…...
高阶开发基础——快速入门C++并发编程4
目录 使用call_once来确保调用的唯一性 先看我们的原始的单例模式 使用call_once来确保调用的唯一性 一个相似的概念是——单例模式,笔者找到的是stack_overflow的一个问答,如果不喜欢看英文,可以考虑看一下这个CSDN回答: c - H…...
C++基础day1
前言:谢谢阿秀,指路阿秀的学习笔记 一、基础语法 1.构造和析构: 类的构造函数是一种特殊的函数,在创建一个新的对象时调用。类的析构函数也是一种特殊的函数,在删除所创建的对象时调用。 构造顺序:父类->子类 析…...
Deepseek:网页版OR本地部署版本?
使用本地部署的 DeepSeek 还是网页版的 DeepSeek,取决于具体需求和使用场景。以下是两者的对比及推荐建议: 响应速度 网页版 DeepSeek:响应速度受网络状况和服务器负载影响较大。如果网络不稳定或服务器繁忙,可能会出现延迟甚至…...
【文件上传】
目录 一. 介绍二. 本地存储三. 阿里云OSS3.1 准备工作3.2 入门程序3.3 案例集成3.4 程序优化 \quad 一. 介绍 \quad 三要素缺一不可 \quad 二. 本地存储 \quad 解决相同命名覆盖问题 \quad 三. 阿里云OSS \quad \quad 3.1 准备工作 \quad \quad 3.2 入门程序 \quad \quad 3.3…...
股票入门知识
股票入门(更适合中国宝宝体制) 股市基础知识 本文介绍了股票的基础知识,股票的分类,各板块发行上市条件,股票代码,交易时间,交易规则,炒股术语,影响股价的因素…...
Debezium Oracle Connector SCN处理优化指南
Debezium Oracle Connector SCN处理优化指南 📌 问题场景 SCN跳跃场景: 起始SCN:15,000(含数据变更)结束SCN:1,000,000(无中间数据)默认批次大小:10,000 → 需执行985次无效查询🚀 优化方案 1. 自适应批次调整 代码位置:LogMinerStreamingChangeEventSource.j…...
2021版小程序开发5——小程序项目开发实践(1)
2021版小程序开发5——小程序项目开发实践(1) 学习笔记 2025 使用uni-app开发一个电商项目; Hbuidler 首选uni-app官方推荐工具:https://www.dcloud.io/hbuilderx.htmlhttps://dev.dcloud.net.cn/pages/app/list 微信小程序 管理后台:htt…...
软件测试02----用例设计方法
今天目标 1.能对穷举场景设计测试点 2.能对限定边界规则设计测试点 3.能对多条件依赖关系进行设计测试点 4.能对项目业务进行设计测试点 一、解决穷举场景 重点:使用等价类划分法 1.1等价类划分法 重点:有效等价和单个无效等价各取1个即可。 步骤&#…...
分享半导体Fab 缺陷查看系统,平替klarity defect系统
分享半导体Fab 缺陷查看系统,平替klarity defect系统;开发了半年有余。 查看Defect Map,Defect image,分析Defect size,defect count trend. 不用再采用klarity defect系统(license 太贵) 也可以…...
C语言-----数据结构从门到精通
1.数据结构基本概念 数据结构是计算机中存储、组织数据的方式,旨在提高数据的访问和操作效率。它是实现高效算法和程序设计的基石。 目标:通过思维导图了解数据结构的知识点,并掌握。 1.1逻辑结构 逻辑结构主要四种类型: 集合:结构中的数据元素之…...
存储器知识点3
1.只读存储器中内容断电后不会丢失,通常存储固定不变的内容,不需要定时刷新。 2.虚拟存储器将主存和辅存地址空间统一编址,其大小受到辅助存储器容量的限制。使得主存空间得到了扩充,需要硬件支持,并由操作系统调度。…...
Weevely代码分析
亲测php5和php8都无效,只有php7有效 ailx10 1949 次咨询 4.9 网络安全优秀回答者 互联网行业 安全攻防员 去咨询 上一次做weevely实验可以追溯到2020年,当时还是weevely3.7,现在的是weevely4 生成php网页木马依然差不多…… php菜刀we…...
leetcode解题思路分析(一百六十三)1409 - 1415 题
查询带键的排列 给定一个正整数数组 queries ,其取值范围在 1 到 m 之间。 请你根据以下规则按顺序处理所有 queries[i](从 i0 到 iqueries.length-1): 首先,你有一个排列 P[1,2,3,…,m]。 对于当前的 i ,找…...
【MATLAB例程】TOA和AOA混合的高精度定位程序,适用于三维、N锚点的情况
代码实现了一个基于到达角(AOA)和到达时间(TOA)混合定位的例程。该算法能够根据不同基站接收到的信号信息,自适应地计算目标的位置,适用于多个基站的场景 文章目录 主要功能代码结构运行结果程序代码 主要功…...
PyTorch框架——基于深度学习YOLOv8神经网络学生课堂行为检测识别系统
基于YOLOv8深度学习的学生课堂行为检测识别系统,其能识别三种学生课堂行为:names: [举手, 读书, 写字] 具体图片见如下: 第一步:YOLOv8介绍 YOLOv8 是 ultralytics 公司在 2023 年 1月 10 号开源的 YOLOv5 的下一个重大更新版本…...
智慧园区系统对比不同智能管理模式提升企业运营效率与安全性
内容概要 在当今竞争激烈的市场中,企业需要不断提高运营效率与安全性,以应对复杂的环境。这时,“智慧园区系统”应运而生,成为一种有效的解决方案。智能管理模式的多样性让企业在选择系统时有了更多的选择,而在这些模…...
读书笔记 | 《最小阻力之路》:用结构思维重塑人生愿景
一、核心理念:结构决定行为轨迹 橡皮筋模型:愿景张力的本质 书中提出:人类行为始终沿着"现状"与"愿景"之间的张力路径运动,如同橡皮筋拉伸产生的动力。 案例:音乐家每日练习的坚持,不…...
257. 二叉树的所有路径
二叉树的所有路径 已解答 简单 给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。 叶子节点 是指没有子节点的节点。 示例 1: 输入:root [1,2,3,null,5] 输出:[“1->2->5”,“…...
Vulkan 学习(13)---- Vulkan Framebuffercommand buffer
目录 Vulkan Framebuffer创建 VkFramebufferVkFrameBuffer 创建示例 Vulkan command buffercommand buffer pool分配指令缓存池释放指令缓存池录制 command buffer提交 command buffer Vulkan Framebuffer Vulkan 帧缓冲区(FrameBuffer) 是一个容器对象(资源管理类的对象)&…...
从零开始学习安时积分法(STM32实现程序)
在STM32微控制器上实现安时积分法(Coulomb Counting)来估算电池的SOC(State of Charge),需要完成以下几个步骤: 硬件配置: 使用STM32的ADC模块测量电池的电流。使用定时器模块进行时间积分。配置…...
基于Kamailio、MySQL、Redis、Gin、Vue.js的微服务架构
每个服务使用一台独立的服务器的可行部署方案,尤其是在高并发、高可用性要求较高的场景中。这种方案通常被称为分布式部署或微服务架构。以下是针对您的VoIP管理系统(基于Kamailio、MySQL、Redis、Gin、Vue.js)的详细分析和建议。 1. 分布式部…...
Unity 粒子特效在UI中使用裁剪效果
1.使用Sprite Mask 首先建立一个粒子特效在UI中显示 新建一个在场景下新建一个空物体,添加Sprite Mask组件,将其的Layer设置为UI相机渲染的UI层, 并将其添加到Canvas子物体中,调整好大小,并选择合适的Spriteÿ…...
Android 开发:新的一年,新的征程
回顾 2023 年,Android 开发领域可谓成果斐然。这一年,Android 系统不断迭代,新技术、新工具层出不穷,为开发者们带来了前所未有的机遇与挑战。如今,我们站在新的起点,怀揣着对技术的热爱与追求,…...
手写MVVM框架-环境搭建
项目使用 webpack 进行进行构建,初始化步骤如下: 1.创建npm项目执行npm init 一直下一步就行 2.安装webpack、webpack-cli、webpack-dev-server,html-webpack-plugin npm i -D webpack webpack-cli webpack-dev-server html-webpack-plugin 3.配置webpac…...
SQL进阶实战技巧:某芯片工厂设备任务排产调度分析 | 间隙分析技术应用
目录 0 技术定义与核心原理 1 场景描述 2 数据准备 3 间隙分析法 步骤1:原始时间线可视化...