栈和队列
特点
栈和队列是限定了插入和删除只能在线性结构的端点进行的线性表。
栈 stack
概念:一个只能在线性表尾部实现数据的插入和删除的结构,这种特性被归纳为:后进先出。在问题的求解过程中,我们可以利用栈这一特性来解决很多相关问题:数值转换,表达式求值,符号匹配,迷宫问题……
概念:一个只能在队尾实现插入数据和队头实现删除数据的结构,这种特性被归纳为:先进先出。同样的,在问题的求解过程中,我们也可以利用队列来解决相关很多问题:多任务排队,实时操作系统信号控制,网络数据的缓冲……
栈和队列的具体存储和使用
栈的具体存储和使用
栈是一个后进先出的结构,我们称为LIFO(Last In First Out)结构。由于栈仅仅是在表尾进行插入和删除,因此我们将表头命名为“栈底”bottom/base,表尾命名为“栈顶”top
操作:
将元素插入进栈,也就是将元素插入进栈顶的操作,称为入栈(压栈)。
将元素从栈中删除,也就是说将元素从栈顶删除的操作,称为出栈(弹栈)。
总结:
-
① 栈只能在栈顶实现数据的插入和删除操作
-
② 栈的逻辑结构依然是一对一的线性关系
-
③ 栈的存储结构可以使用顺序表(顺序栈)也可以使用链表(链栈),绝大多数情况下,推荐使用顺序表。
-
④ 运算规则:LIFO
栈的代码实现
-
① 顺序栈
Stack* CreateStack() { Stack* p = (Stack*)malloc(sizeof(Stack)); if (p == NULL){ return NULL; } memset(p, 0, sizeof(Stack)); p->iTop = -1; return p; } int FreeStack(Stack* s) { if (s != NULL) { free(s); s = NULL; return TYPE_OK; } else { return TYPE_ERR; } } //判断空栈 int isEmpty(Stack* s){ if (s == NULL){ return TYPE_ERR; } if (s->iTop == -1){ return TYPE_EMPTY; } return TYPE_OK; } //判断满栈 int isFull(Stack* s){ if (s == NULL){ return TYPE_ERR; } if (s->iTop == SIZE-1){ return TYPE_FULL; } return TYPE_OK; } //入栈 int Push(Stack* s, data_t tData) { //判定栈存在 if (s == NULL){ return TYPE_ERR; } //判定栈不满 if (isFull(s) == TYPE_FULL){ return TYPE_FULL; } //将栈顶游标增加1 s->iTop++; //将新数据放入进栈顶 s->data[s->iTop] = tData; printf("%d 入栈\n", s->data[s->iTop]); return TYPE_OK; } //出栈 int Pop(Stack* s, data_t* pData) { //判定栈存在 if (s == NULL){ return TYPE_ERR; } //判定栈不空 if (isEmpty(s) == TYPE_EMPTY) { return TYPE_EMPTY; } //打印出栈数据 *pData = s->data[s->iTop]; //将数据带出函数 printf("%d出栈\n", *pData); //将栈顶游标减小1 s->iTop--; return TYPE_OK; } void PrintStack(Stack *s) { int i; for (i = 0; i <= s->iTop; i++) { printf("%d\t", s->data[i]); } putchar('\n'); } void ClearStack(Stack* s) { s->iTop = -1; }
-
② 链栈
本质上就是尾插尾删的链表再加上一个指向最后一个元素的指针
注:链式栈一般来讲不考虑栈满的情况,因为链栈的内存是动态的开辟的,所以一般来说不会有空间不够的情况。
队列的具体存储和使用
队列queue,是一个先进先出的结构,限定了数据元素只能在尾部插入和头部删除的线性表,这种结构被称为FIFO结构(First In First Out)
将元素从队列的尾部插入进队列,称为入队;
将元素从队列的头部删除出队列,称为出队。
总结:
① 实现方式:头插尾删
② 本质上其逻辑结构依然也是线性表
③ 存储结构可以使用顺序表及链表存储,在日常的开发中循环队列较为常见
④ 运算规则:FIFO
队列的代码实现
① 顺序队列
#include "queue.h" //创建队列 Queue* CreateQueue() { Queue* p = (Queue*)malloc(sizeof(Queue)); if (p == NULL) { return NULL; } memset(p, 0, sizeof(Queue)); p->head = p->tail = 0; return p; } //入队 void EnQueue(Queue* p, data_t tData){ //队列存在 if (p == NULL) { printf("队列不存在\n"); exit(0); } //判断队列是否满 if ((p->tail) - (p->head) >= N - 1) { printf("队列满\n"); exit(0); } //操作队尾下标实现入队 if (p->tail < N - 1) { p->data[p->tail] = tData; printf("%d入队\n", p->data[p->tail]); p->tail++; } } //出队 void DeQueue(Queue* p, data_t *pData){ //判断队列存在 if (p == NULL) { printf("队列不存在\n"); exit(0); } //判断队列空 if (p->head >= p->tail){ printf("队列为空\n"); } //使用队头下标操作元素 //方法1 让head下标移动 /* *pData = p->data[p->head]; p->head++; */ //方法2 让后续所有元素向前移动一位 *pData = p->data[p->head]; printf("%d出队\n", *pData); int t = p->head; for (; t < p->tail; t++) { p->data[t] = p->data[t + 1]; } p->tail--; } void PrintQueue(Queue* p) { int i = p->head; while (i != p->tail){ printf("%d\t", p->data[i]); i++; } putchar('\n'); }
② 链式队列
链式队列的实现,本质上是单链表的头删和尾插操作。
-
新手小白记录学习
-
来自于y老师讲解数据结构和自读严蔚敏教授数据结构后的一个整理day5