数据结构栈和队列
- 格式:docx
- 大小:23.65 KB
- 文档页数:13
数据结构--栈和队列基础知识⼀概述栈和队列,严格意义上来说,也属于线性表,因为它们也都⽤于存储逻辑关系为 "⼀对⼀" 的数据,但由于它们⽐较特殊,因此将其单独作为⼀篇⽂章,做重点讲解。
既然栈和队列都属于线性表,根据线性表分为顺序表和链表的特点,栈也可分为顺序栈和链表,队列也分为顺序队列和链队列,这些内容都会在本章做详细讲解。
使⽤栈结构存储数据,讲究“先进后出”,即最先进栈的数据,最后出栈;使⽤队列存储数据,讲究 "先进先出",即最先进队列的数据,也最先出队列。
⼆栈2.1 栈的基本概念同顺序表和链表⼀样,栈也是⽤来存储逻辑关系为 "⼀对⼀" 数据的线性存储结构,如下图所⽰。
从上图我们看到,栈存储结构与之前所了解的线性存储结构有所差异,这缘于栈对数据 "存" 和 "取" 的过程有特殊的要求:1. 栈只能从表的⼀端存取数据,另⼀端是封闭的;2. 在栈中,⽆论是存数据还是取数据,都必须遵循"先进后出"的原则,即最先进栈的元素最后出栈。
拿图 1 的栈来说,从图中数据的存储状态可判断出,元素 1 是最先进的栈。
因此,当需要从栈中取出元素 1 时,根据"先进后出"的原则,需提前将元素 3 和元素 2 从栈中取出,然后才能成功取出元素 1。
因此,我们可以给栈下⼀个定义,即栈是⼀种只能从表的⼀端存取数据且遵循 "先进后出" 原则的线性存储结构。
通常,栈的开⼝端被称为栈顶;相应地,封⼝端被称为栈底。
因此,栈顶元素指的就是距离栈顶最近的元素,拿下图中的栈顶元素为元素 4;同理,栈底元素指的是位于栈最底部的元素,下中的栈底元素为元素 1。
2.2 进栈和出栈基于栈结构的特点,在实际应⽤中,通常只会对栈执⾏以下两种操作:向栈中添加元素,此过程被称为"进栈"(⼊栈或压栈);从栈中提取出指定元素,此过程被称为"出栈"(或弹栈);2.3 栈的具体实现栈是⼀种 "特殊" 的线性存储结构,因此栈的具体实现有以下两种⽅式:1. 顺序栈:采⽤顺序存储结构可以模拟栈存储数据的特点,从⽽实现栈存储结构。
栈和队列先进先出和后进先出的数据结构栈和队列是常用的数据结构,它们分别以先进先出(FIFO)和后进先出(LIFO)的方式来组织和管理数据。
在许多编程语言中,栈和队列被广泛应用于解决各种问题。
本文将从定义、特点、应用和实现这几个方面来介绍栈和队列。
一、定义栈(Stack)是一种只允许在固定一端进行插入和删除操作的线性数据结构。
这一端被称为栈顶,而另一端被称为栈底。
栈的特点是先进后出。
队列(Queue)是一种先进先出的线性数据结构,允许在一端进行插入操作,而在另一端进行删除操作。
插入操作在队列的尾部进行,删除操作则在队列的头部进行。
二、特点2.1 栈的特点(1)插入和删除操作只能在栈顶进行,保证数据的顺序。
(2)栈是一种后进先出(LIFO)的数据结构,也就是最后插入的元素最先被删除。
(3)栈只能在栈顶进行插入和删除操作,不允许在中间或者底部进行操作。
2.2 队列的特点(1)插入操作只能在队列的尾部进行,保证数据的顺序。
(2)删除操作只能在队列的头部进行,始终删除最先插入的元素。
(3)队列是一种先进先出(FIFO)的数据结构,也就是最先插入的元素最早被删除。
三、应用3.1 栈的应用(1)函数调用和递归:栈被用于保存函数调用时的局部变量和返回地址。
(2)表达式求值:使用栈来实现中缀表达式转换为后缀表达式,然后计算结果。
(3)括号匹配:通过栈检查括号是否配对合法。
(4)浏览器的前进和后退:把浏览器的访问记录保存在栈中,方便前进和后退操作。
3.2 队列的应用(1)任务调度:使用队列管理任务,在现有任务执行完毕后按照先后顺序执行新任务。
(2)缓存管理:常用的缓存淘汰策略是先进先出,即最早进入缓存的数据最早被淘汰。
(3)消息队列:实现进程间的异步通信,提高系统的并发性和可扩展性。
(4)打印队列:打印任务按照先后顺序排队执行,保证打印的顺序。
四、实现栈和队列可以通过数组或链表来实现。
使用数组实现的栈和队列称为顺序栈和顺序队列,而使用链表实现的栈和队列称为链式栈和链式队列。
栈和队列是信息学竞赛中经常涉及的数据结构,它们在算法和程序设计中有着广泛的应用。
掌握栈和队列的基本原理和操作方法,对于参加信息学竞赛的同学来说是非常重要的。
本文将深入探讨栈和队列的相关知识点,帮助大家更好地理解和掌握这两种数据结构。
一、栈的定义与特点栈是一种先进后出(LIFO)的数据结构,它的特点是只允许在栈顶进行插入和删除操作。
栈可以用数组或链表来实现,常见的操作包括压栈(push)、出栈(pop)、获取栈顶元素(top)等。
栈的应用非常广泛,比如在计算机程序中,函数的调用和返回值的存储就是通过栈来实现的。
二、栈的基本操作1. 压栈(push):将元素压入栈顶2. 出栈(pop):将栈顶元素弹出3. 获取栈顶元素(top):返回栈顶元素的值,但不把它从栈中移除4. 判空:判断栈是否为空5. 获取栈的大小:返回栈中元素的个数三、栈的应用1. 括号匹配:利用栈来检查表达式中的括号是否匹配2. 表达式求值:利用栈来实现中缀表达式转换为后缀表达式,并进行求值3. 迷宫求解:利用栈来实现迷宫的路径搜索4. 回溯算法:在深度优先搜索和递归算法中,通常会用到栈来保存状态信息四、队列的定义与特点队列是一种先进先出(FIFO)的数据结构,它的特点是只允许在队尾进行插入操作,在队首进行删除操作。
队列同样可以用数组或链表来实现,常见的操作包括入队(enqueue)、出队(dequeue)、获取队首元素(front)、获取队尾元素(rear)等。
队列在计算机领域也有着广泛的应用,比如线程池、消息队列等都可以用队列来实现。
五、队列的基本操作1. 入队(enqueue):将元素插入到队列的末尾2. 出队(dequeue):从队列的头部删除一个元素3. 获取队首元素(front):返回队列的头部元素的值4. 获取队尾元素(rear):返回队列的尾部元素的值5. 判空:判断队列是否为空6. 获取队列的大小:返回队列中元素的个数六、队列的应用1. 广度优先搜索算法(BFS):在图的搜索中,通常会用队列来实现BFS算法2. 线程池:利用队列来实现任务的调度3. 消息队列:在分布式系统中,常常会用队列来进行消息的传递4. 最近最少使用(LRU)缓存算法:利用队列实现LRU缓存淘汰在信息学竞赛中,栈和队列的相关题目经常出现,并且有一定的难度。
实验二栈和队列一、实验目的1. 掌握栈的顺序表示和实现2. 掌握队列的链式表示和实现二、实验内容1. 编写一个程序实现顺序栈的各种基本运算。
2. 实现队列的链式表示和实现。
三、实验步骤1. 初始化顺序栈2. 插入元素3. 删除栈顶元素4. 取栈顶元素5. 遍历顺序栈6. 置空顺序栈7. 初始化并建立链队列8. 入链队列9. 出链队列10. 遍历链队列四、实现提示1. /*定义顺序栈的存储结构*/typedef struct {ElemType stack[MAXNUM];int top;}SqStack;/*初始化顺序栈函数*/void InitStack(SqStack *p){q=(SqStack*)malloc(sizeof(SqStack) /*申请空间*/) /*入栈函数*/void Push(SqStack *p,ElemType x){if(p->top<MAXNUM-1){p->top=p->top+1; /*栈顶+1*/p->stack[p->top]=x; } /*数据入栈*/}/*出栈函数*/ElemType Pop(SqStack *p){x=p->stack[p->top]; /*将栈顶元素赋给x*/p->top=p->top-1; } /*栈顶-1*//*获取栈顶元素函数*/ElemType GetTop(SqStack *p){ x=p->stack[p->top];}/*遍历顺序栈函数*/void OutStack(SqStack *p){ for(i=p->top;i>=0;i--)printf("第%d个数据元素是:%6d\n",i,p->stack[i]);} /*置空顺序栈函数*/void setEmpty(SqStack *p){ p->top= -1;}2. /*定义链队列*/typedef struct Qnode{ ElemType data;struct Qnode *next;}Qnodetype;typedef struct{ Qnodetype *front;Qnodetype *rear;}Lqueue;/*初始化并建立链队列函数*/void creat(Lqueue *q){ h=(Qnodetype*)malloc(sizeof(Qnodetype)); /*初始化申请空间*/ h->next=NULL;q->front=h;q->rear=h;for(i=1;i<=n;i++)*利用循环快速输入数据*/{ scanf("%d",&x);Lappend(q,x);} /*利用入链队列函数快速输入数据*/}/*入链队列函数*/void Lappend(Lqueue *q,int x){ s->data=x;s->next=NULL;q->rear->next=s;q->rear=s;}/*出链队列函数*/ElemType Ldelete(Lqueue *q){ p=q->front->next;q->front->next=p->next;if(p->next==NULL)q->rear=q->front;x=p->data;free(p);} /*释放空间*//*遍历链队列函数*/void display(Lqueue *q){ while(p!=NULL) /*利用条件判断是否到队尾*/{ printf("%d-->",p->data);p=p->next;}}五、思考与提高1. 读栈顶元素的算法与退栈顶元素的算法有何区别?2. 如果一个程序中要用到两个栈,为了不发生上溢错误,就必须给每个栈预先分配一个足够大的存储空间。
若每个栈都预分配过大的存储空间,势必会造成系统空间紧张。
如何解决这个问题?(1)链栈只有一个top指针,对于链队列,为什么要设计一个头指针和一个尾指针?(2)一个程序中如果要用到两个栈时,可通过两个栈共享一维数组来实现。
即双向栈共享邻接空间。
如果一个程序中要用到两个队列,能否实现?如何实现?六、完整参考程序1.栈的顺序表示和实现#include <stdio.h>#include <stdlib.h>#include <conio.h>#define TRUE 1#define FALSE 0#define OK 1#define ERROR 0#define INFEASIBLE -1#define OVERFLOW -2typedef int Status;typedef int SElemType;//----- 栈的顺序存储表示-----#define STACK_INIT_SIZE 100#define STACKINCREMENT 10typedef struct {SElemType *base;SElemType *top;int stacksize;} SqStack;Status InitStack(SqStack &S);Status DestroyStack(SqStack &S);Status StackDisplay(SqStack &S);Status GetTop(SqStack S,SElemType &e);Status Push(SqStack &S,SElemType e);Status Pop(SqStack &S,SElemType &e);Status StackEmpty(SqStack S);Status InitStack(SqStack &S){//构造一个空栈SS.base = (SElemType *) malloc(STACK_INIT_SIZE*sizeof(SElemType));if(!S.base) exit(OVERFLOW); //存储分配失效S.top = S.base;S.stacksize = STACK_INIT_SIZE;return OK;}//InitStackStatus DestroyStack(SqStack &S){//销毁栈Sif(S.base) free(S.base);S.top = S.base = NULL;return OK;}//InitStackStatus StackDisplay(SqStack &S){//显示栈SSElemType * p=S.base;int i = 0;if(S.base == S.top){printf("堆栈已空!\n");return OK;}while( p < S.top)printf("[%d:%d]",++i,*p++);printf("\n");return OK;}//StackDisplayStatus GetTop(SqStack S,SElemType &e){//若栈不空,则用e返回S的栈顶元素,//并返回OK;否则返回ERRORif(S.top==S.base) return ERROR;e=*(S.top-1);return OK;}//GetTopStatus Push(SqStack &S,SElemType e){//插入元素e为新的栈顶元素if(S.top-S.base>=S.stacksize){//栈满,追加存储空间S.base=(SElemType*) realloc(S.base,(S.stacksize+STACKINCREMENT)*sizeof(SElemType));if( ! S.base ) exit(OVERFLOW);//存储分配失败S.top = S.base + S.stacksize;S.stacksize += STACKINCREMENT;}* S.top ++ = e;return OK;}//PushStatus Pop(SqStack &S,SElemType &e){//若栈不为空,则删除S的栈顶元素,//用e返回其值,并返回OK;否则返回ERRORif (S.top == S.base) return ERROR;e = * --S.top;return OK;}//PopStatus StackEmpty(SqStack S){//若S为空栈,则返回TRUE,否则返回FALSE。
if(S.top == S.base) return TRUE;else return FALSE;}// StackEmptyvoid main(){SqStack St;Status temp;int flag=1,ch;int e;printf("本程序实现顺序结构的堆栈的操作。
\n");printf("可以进行入栈,出栈,取栈顶元素等操作。
\n");InitStack(St); //初始化堆栈Stwhile(flag){printf("请选择:\n");printf("1.显示栈中所有元素\n");printf("2.入栈\n");printf("3.出栈\n");printf("4.取栈顶元素\n");printf("5.退出程序\n");scanf("%d",&ch);switch(ch){case 1:StackDisplay(St);break;case 2:printf("请输入要入栈的元素(一个整数):");scanf("%d",&e); //输入要入栈的元素temp=Push(St,e); //入栈if(temp!=OK) printf("堆栈已满!入栈失败!\n");else {printf("成功入栈!\n"); //成功入栈StackDisplay(St);}break;case 3:temp=Pop(St,e); //出栈if(temp==ERROR) printf("堆栈已空!\n");else {printf("成功出栈一个元素:%d\n",e); //成功出栈StackDisplay(St);}break;case 4:temp=GetTop(St,e); //取得栈顶元素if(temp==ERROR) printf("堆栈已空!\n");else printf("栈顶元素是:%d\n",e); //显示栈顶元素break;default:flag=0;printf("程序结束,按任意键退出!\n");getch();}}DestroyStack(St);}2. 队列的链式表示和实现#include <conio.h>#include <stdio.h>#include <stdlib.h>#define TRUE 1#define FALSE 0#define OK 1#define ERROR 0#define INFEASIBLE -1#define OVERFLOW -2//Status 是函数的类型,其值是函数结果状态代码typedef int Status;//ElemType 是顺序表数据元素类型,此程序定义为int型typedef int QElemType;//-----单链队列--队列的链式存储结构-----typedef struct QNode{ //定义结点结构QElemType data; //数据域struct QNode *next; //指针域}QNode,*QueuePtr;typedef struct linkqueue{ //定义队列结构QueuePtr front; //队头指针QueuePtr rear; //队尾指针}LinkQueue;Status InitLinkQueue(LinkQueue &); //初始化一个队列Status DestroyLinkQueue(LinkQueue &); //销毁一个队列int LinkQueueLength(LinkQueue &Q); //队列的长度Status EnLinkQueue(LinkQueue &,QElemType); //将一个元素入队列Status DeLinkQueue(LinkQueue &,QElemType &);//将一个元素出队列Status DisplayLinkQueue(LinkQueue); //显示队列中所有元素void main(){LinkQueue LQ;QElemType e;int flag=1,ch,len;Status temp;printf("本程序实现链式结构队列的操作。