数据结构】舍友用一把云顶之弈的时间学会了<栈和队列>

146 阅读8分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第N天,点击查看活动详情

{ assert(ps); //此时的top就是长度 return ps->top; } 1 2 3 4 5 6 🌿3.6栈顶 STDatatype StackTop(ST* ps)//函数原型 { assert(ps); //找栈顶的话得保证指向的空间不为空 assert(!StackEmpty(ps)); //此时的top-1就是栈顶数据 return ps->a[ps->top - 1]; } 1 2 3 4 5 6 7 8 🌿3.7栈的销毁 void StackDestory(ST* ps)//函数原型 { assert(ps); if (ps->a) { free(ps->a); } ps->a = NULL; ps->top = 0; ps->capacicy = 0; } 1 2 3 4 5 6 7 8 9 10 11 🌹4、栈的实现(总代码) 🌿4.1 Stack.h #pragma once

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

typedef int STDatatype; typedef struct Stack { STDatatype* a; //动态开辟的空间 int top; //栈顶 int capacicy; //容量 }ST;

//注意链表和顺序表我们写Print,但是栈不写,因为栈是后进先出的,所以不能Printf void StackInit(ST* ps);//初始化 void StackPush(ST* ps, STDatatype x);//插入 void StackPop(ST* ps);//删除 bool StackEmpty(ST* ps);//判空 int StackSize(ST* ps);//长度 STDatatype StackTop(ST* ps);//栈顶 void StackDestory(ST* ps);//销毁

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 🌿4.2 Stack.c #include "Stack.h"

void StackInit(ST* ps) { assert(ps); //初始化 ps->a = NULL; ps->top = 0; ps->capacicy = 0; } void StackPush(ST* ps, STDatatype x) { assert(ps); //检查空间,满了就增容 if (ps->top == ps->capacicy) { //第一次开辟空间容量为4,其它次容量为当前容量2 int newcapacity = ps->capacicy == 0 ? 4 : ps->capacicy * 2; //第一次开辟空间,a指向空,realloc的效果同malloc STDatatype tmp = realloc(ps->a, sizeof(STDatatype) * newcapacity); //检查realloc //realloc失败 if (tmp == NULL) { printf("realloc fail\n"); exit(-1); } //realloc成功 ps->a = tmp; ps->capacicy = newcapacity; } //插入数据 ps->a[ps->top] = x; ps->top++; } void StackPop(ST* ps) { assert(ps); //删除的话得保证指向的空间不为空 assert(!StackEmpty(ps)); //删除 --ps->top; } bool StackEmpty(ST* ps) { assert(ps); //等于0是真,否则为假 return ps->top == 0; } int StackSize(ST* ps) { assert(ps); //此时的top就是长度 return ps->top; } STDatatype StackTop(ST* ps) { assert(ps); //找栈顶的话得保证指向的空间不为空 assert(!StackEmpty(ps)); //此时的top-1就是栈顶数据 return ps->a[ps->top - 1]; } void StackDestory(ST* ps) { assert(ps); //a为真代表它指向动态开辟的空间 if (ps->a) { free(ps->a); } ps->a = NULL; ps->top = 0; ps->capacicy = 0; }

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 🌿4.3 main.c #include"Stack.h"

int main() { ST st; StackInit(&st);//初始化 StackPush(&st, 1);//插入数据 StackPush(&st, 2); StackPush(&st, 3); StackPush(&st, 4); StackPush(&st, 5); StackPop(&st);//删除数据 StackPop(&st); printf("%d\n",StackSize(&st)); //StackTop(&st); //StackDestory(&st); return 0; }

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 🌅 2⃣️ 队列 学完栈了我们接着来学一下队列

🌹1、队列的定义 队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。 队列是一种先进先出(First In First Out)的线性表,简称FIFO。

🌹2、队列的数据存储 队列的两个经典操作

入队:从队尾(rear)进行数据插入 出队:从队头(front)进行数据删除 遵循先入先出原则

🌹2、循环队列 我们还会经常使用一种循环队列,顾名思义就是头尾相连的队列 循环队列的优点:我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

循环队列的运算:

队列为空时 :q -> front = q -> rear =0 front+1:q -> front = (q -> front +1) % Maxsize / / 取模是为了防止溢出 rear +1: q -> rear = (q -> rear +1) % Maxsiz / / 取模是为了防止溢出 队列长度:(Q->rear - Q->front + Maxsize) % Maxsize。

根据d1我们可以知道,当q -> front == q ->rear我们并不能知道这时候的队列是队空还是队满,所以我们需要预留一个存储单元,当rear+1 = front 的时候,为队满 判断条件

队空 :q -> front == q -> rear 队满 : (q ->rear + 1) % Maxsize == q -> front 🌹3、队列的实现 下面分布来进行队列的实现

🌿3.1 队列的初始化 void QueueInit(Queue* pq)//函数原型 { assert(pq); //把2个指针置空 pq->phead = pq->ptail = NULL; } 1 2 3 4 5 6 🌿3.2 插入数据 void QueuePush(Queue* pq, QDataType x)//函数原型 { assert(pq); QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));//开辟空间 if (newnode == NULL) { printf("malloc fail\n"); exit(-1); } newnode->data = x; newnode->next = NULL; //第一次插入 if (pq->phead == NULL) { pq->phead = pq->ptail = newnode; } //非第一次插入 else { pq->ptail->next = newnode; pq->ptail = newnode; } }

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 🌿3.3 删除数据 void QueuePop(Queue* pq)//函数原型 { assert(pq); //需要判断,当链表为空时不能删除 assert(!QueueEmpty(pq)); //只有一个节点的情况 if (pq->phead->next == NULL) { free(pq->phead); pq->phead = pq->ptail = NULL; } //多个节点的情况 else { QueueNode* next = pq->phead->next; free(pq->phead) ; pq->phead = next; } }

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 🌿3.4 判断是否为空 bool QueueEmpty(Queue* pq)//函数原型 { assert(pq); //空链表返回true,非空链表返回false return pq->phead == NULL; } 1 2 3 4 5 6 🌿3.5 队列长度 QDataType QueueSize(Queue* pq)//函数原型 { assert(pq); //如果需要频繁的调用QueueSize这个接口,可以在Queue这个结构体中增加一个成员用于记录长度 int sz = 0; QueueNode* cur = pq->phead; while (cur) { sz++; cur = cur->next; } return sz; } 1 2 3 4 5 6 7 8 9 10 11 12 13 🌿3.6 队头 QDataType QueueFront(Queue* pq)//函数原型 { assert(pq); //链表为空时不能取头 assert(!QueueEmpty(pq));

return pq->phead->data;

} 1 2 3 4 5 6 7 8 🌿3.7 队尾 QDataType QueueBack(Queue* pq)//函数原型 { assert(pq); //链表为空时不能取尾 assert(!QueueEmpty(pq));

return pq->ptail->data;

} 1 2 3 4 5 6 7 8 🌿3.8 队列销毁 void QueueDestory(Queue* pq)//函数原型 { assert(pq); QueueNode* cur = pq->phead; //遍历链表 while (cur) { QueueNode* next = cur->next; free(cur); cur = next; } pq->phead = pq->ptail = NULL; } 1 2 3 4 5 6 7 8 9 10 11 12 13 🌹4、队列实现(总代码) 🌿4.1 Queue.h #pragma once

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

//结构体 typedef int QDataType; typedef struct QueueNode { struct QueueNode* next; //指向下一个节点 QDataType data; //存储整型数据 }QueueNode;

typedef struct Queue { QueueNode* phead;//头指针 QueueNode* ptail;//尾指针 }Queue;

//函数 void QueueInit(Queue* pq); void QueuePush(Queue* pq, QDataType x); bool QueueEmpty(Queue* pq); void QueuePop(Queue* pq); QDataType QueueSize(Queue* pq); QDataType QueueFront(Queue* pq); QDataType QueueBack(Queue* pq); void QueueDestory(Queue* pq);

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 🌿4.2 Queue.c #include"Queue.h"

void QueueInit(Queue* pq) { assert(pq); //把2个指针置空 pq->phead = pq->ptail = NULL; } void QueuePush(Queue* pq, QDataType x) { assert(pq); //malloc空间,如果需要频繁的开辟空间建议再实现一个BuyQueueNode用于malloc QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode)); if (newnode == NULL) { printf("malloc fail\n"); exit(-1); } newnode->data = x; newnode->next = NULL; //第一次插入 if (pq->phead == NULL) { pq->phead = pq->ptail = newnode; } //非第一次插入 else { pq->ptail->next = newnode; pq->ptail = newnode; } } void QueuePop(Queue* pq) { assert(pq); //链表为空时不能删除 assert(!QueueEmpty(pq)); //只有一个节点的情况 if (pq->phead->next == NULL) { free(pq->phead); pq->phead = pq->ptail = NULL; } //多个节点的情况 else { QueueNode* next = pq->phead->next; free(pq->phead) ; pq->phead = next; } } bool QueueEmpty(Queue* pq) { assert(pq); //空链表返回true,非空链表返回false return pq->phead == NULL; } QDataType QueueSize(Queue* pq) { assert(pq); //如果需要频繁的调用QueueSize这个接口,可以在Queue这个结构体中增加一个成员用于记录长度 int sz = 0; QueueNode* cur = pq->phead; while (cur) { sz++; cur = cur->next; } return sz; } 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; } void QueueDestory(Queue* pq) { assert(pq);

QueueNode* cur = pq->phead;//遍历链表 while (cur) { QueueNode* next = cur->next; free(cur); cur = next; } pq->phead = pq->ptail = NULL; }

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 🌿4.3 main.c #include"Queue.h"

int main() { struct QueueNode q; QueueInit(&q);//初始化 QueuePush(&q, 1);//插入 QueuePush(&q, 2); QueuePush(&q, 3); QueuePush(&q, 4); QueuePop(&q);//删除 QueuePop(&q); printf("%d",QueueFront(&q));//取头 printf("%d",QueueBack(&q)); //取尾 QueueDestory(&q);//释放

return 0; }

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16