490 likes | 677 Views
第 4 章 栈和队列. 4.1 栈 4.2 栈的顺序存储结构和操作实现 4.3 栈的链接存储结构 和操作实现 4.4 栈的简单应用 4.5 算术表达式的计算 4.6 栈与递归 4.7 队列. 本章重点 理解并掌握栈、队列的定义、特征及对其的 基本操作. 本章难点 理解递归的定义及工作原理 掌握设计递归算法的思路. 学习目标 理解栈、队列的定义、特征 掌握在两种存储结构上对栈、队列进行的 基本操作的实现 理解递归的定义及工作原理,掌握设计递 归算法的思路. 进栈. 出栈. 栈顶. a n. …….
E N D
第4章 栈和队列 4.1 栈 4.2 栈的顺序存储结构和操作实现 4.3 栈的链接存储结构和操作实现 4.4 栈的简单应用 4.5 算术表达式的计算 4.6 栈与递归 4.7 队列 《数据结构》
本章重点 理解并掌握栈、队列的定义、特征及对其的 基本操作 • 本章难点理解递归的定义及工作原理掌握设计递归算法的思路 《数据结构》
学习目标 • 理解栈、队列的定义、特征 • 掌握在两种存储结构上对栈、队列进行的 基本操作的实现 • 理解递归的定义及工作原理,掌握设计递 归算法的思路 《数据结构》
进栈 出栈 ... 栈顶 an ……... 栈s=(a1,a2,……,an) a2 栈底 a1 4.1 栈 栈,也叫堆栈,是最常用也是最重要的数据结构之一。比如编译器中的语法识别、数学表达式的处理、程序运行中的函数及过程的调用等,都要用到栈的有关特性。它们是栈应用于实际问题的典型。 《数据结构》
一、栈的定义 栈是一种特殊的线性表,插入或删除栈元素的运算只能在表的一端进行,称运算的一端为栈顶,另一端称为栈底。 特点:后进先出 栈又称为“后进先出”的线性表,简称LIFO表。 《数据结构》
练 习 已知一个栈S的输入序列为abcd,下面两个序列能否通过栈的push和pop操作输出;如果能,请写出操作序列;若不能说明原因。 (1) dbca (2) cbda 《数据结构》
4.1.2 栈的运算 • void InitStack(S) • void Push(S,x) • ElemType Pop(S,x) • ElemType Peek(S,x) • int EmptyStack(S) • void ClearStack(S) 《数据结构》
4 3 2 1 0 top 18 46 InitStack(a); 15 举例:栈的运算假定一个栈对象为a,其元素类型ElemType 对应为int,分析下面程序段: Push(a,18); 输出:46 int x=46; 18 Push(a,x); Push(a,x/3); x=Pop(a); printf(“%d”,peek(a)); Pop(a); EmptyStack(a); printf(“%d\n”, Pop(a)); -1 《数据结构》
4.2 栈的顺序存储结构和操作实现 1、栈的顺序存储结构 栈的顺序存储结构简称为顺序栈。通常由一个一维数组和一个记录栈顶元素位置的变量组成。 栈的顺序存储结构所使用的栈数组和栈顶指针同样可以定义在一个记录类型中: struct StackSq{ elemtype *stack; /*存栈元素的数组指针。*/ int top;/*存栈顶元素的下标位置。*/ /*取值范围为0~MaxSize-1 */ int MaxSize; /*存stack数组长度。*/ }; 《数据结构》
top 2、栈的插入和删除操作 栈的插入过程:每次向栈中压入一个元素时,首先使top增1,用以指示新的栈顶位置,然后再把元素赋值到这个空位置上; c 例:向栈中压入元素C的过程 先使top指向了新的栈顶位置 再把C赋值到这个空位置上 《数据结构》
top 2、栈的插入和删除操作 栈的删除过程:每次从栈中弹出一个元素时,首先取出栈顶元素,然后使top减1。 例:取栈顶元素的过程 x 先取出栈顶元素 b 使top减1指向了当前栈顶 《数据结构》
。 顺序栈的几种状态(最大长度MaxSize为4) top=-1表示栈空, top=MaxSize-1表示栈满。 《数据结构》
2.基本运算在顺序存储结构下的实现 初始化栈InitStack(S) void InitStack(struct StackSq *S) { S->top=-1; } 压栈Push(S,x) void Push(struct StackSq *S,ElemType x) {if(S->top== S->MaxSize-1) againMalloc(S); S->top++; S->stack[S->top]=x; } 《数据结构》
删除栈顶元素并返回值 Elemtype Pop(struct StackSq *S) { if(S->top==-1){ printf(“\n 栈空,无元素出栈!"); exit(1); } S->top--; return S->stack[S->top+1]; } 《数据结构》
取栈顶元素Peek(S,x) ElemType Peek(struct StackSq *S) { if(S->top==-1){ printf(“\n 栈空,无元素出栈!"); exit(1); } return S->stack[S->top]; } 判栈空Empty(S) int EmptyStack(struct StackSq *S) { return (S->top==-1?1:0);} 《数据结构》
清除栈S中的所有元素,释放动态存储空间 void ClearStack(struct StackSq *S) { if(S->stack) { free(S->stack); S->stack=0; S->top=-1; S->MaxSize=0; } } 《数据结构》
void main() {struct StackSq s; int a[8]={3,8,5,17,9,30,15,22}; int i; initstack(&s,5); for(i=0; i<8;i++) Push(&s,a[i]); printf(“%d”,Pop(&s)); printf(“%d\n”,Pop(&s)); Push(&s,68); printf(“%d”,Peek(&s)); printf(“%d\n”,Pop(&s)); While(!EmptyStack(&s)); printf(“%d”,Pop(&s)); printf(“\n”); ClearStack(&s); } 《数据结构》
补充:栈的共享存储单元 两个栈共享大小为m的内存空间时,分配方法的示意图如下 两个栈的共享存储单元可用C语言描述如下: #define MaxSize <共享存储单元的最大长度> typedef struct{ ElemType data[MaxSize]; int top[2]; }STACK; 《数据结构》
(1)两个栈共享存储单元的压栈算法 int Push(STACK *S,ElemType x,int k) { if(S->top[0]+1==S->top[1]){ printf("\n stack is full!"); return 0; } if(k==0){ S->top[0]++; S->data[S->top[0]]=x;} else{ S->top[1]--; S->data[S->top[1]]=x; } return 1; } 《数据结构》
(2)两个栈共享存储单元的出栈算法 int Pop(STACK *S,int k,ElemType *x) { if((k==0)&&(S->top[0]==-1)){ printf("\n stack is free!"); return 0; } if((k==1)&&(S->top[1]==MaxSize)){ printf("\n stack is free!"); return 0; } if(k==0){ *x=S->data[S->top[0]];S->top[0]--; } else{ *x=S->data[S->top[1]];S->top[1]++; } return 1; } 《数据结构》
本 节 小 结 • 栈的定义 • 栈的基本运算 • 栈的顺序存储结构 • 栈的基本运算顺序存储结构下的实现 思考题:数据结构中的栈和汇编语言中的 讲述的堆栈有何区别? 《数据结构》
4.3 栈的链式存储结构及其基本运算的实现 1. 栈的链式存储结构 栈的链式实现是以链表作为栈的存储结构,并在这种存储结构上实现栈的基本运算。栈的链式实现称为链栈。 链栈结构示意图 链栈的C语言描述如下: typedef struct snode { ElemType data; struct snode *next; }LinkSTACK; 《数据结构》
链栈的几种状态 : 《数据结构》
2.基本运算在链式存储结构的实现 • 栈初始化 void InitStack(LinkSTACK **top) • { • *top=(LinkSTACK*)malloc(sizeof(LinkSTACK)); • (*top)->next=NULL; • } 《数据结构》
压栈运算 int Push (LinkSTACK **top,ElemType x) { LinkSTACK *s; s=(LinkSTACK *)malloc(sizeof(LinkSTACK)); s->data=x; s->next=(*top)->next; (*top)->next=s; return 1; } • 判断栈空 • int Empty(LinkSTACK **top) • { return ((*top)->next==NULL?1:0);} 《数据结构》
出栈运算 int Pop(LinkSTACK **top,ElemType *x) { LinkSTACK *s; if(Empty(top)){ printf("\n stack is free!"); return 0; } s=(*top)->next; *x=s->data; (*top)->next=s->next; free(s); return 1; } 《数据结构》
取栈顶元素 int GetTop(LinkSTACK **top,ElemType *x) { if(Empty(top)){ printf("\n stack is free!"); return 0; } *x=(*top)->next->data; return 1; } 《数据结构》
4.5 算术表达式的计算 “算符优先法” 的基本思想: 1)操作数栈置空,表达式起始符“@”为运算符栈的栈底元素。 2)从左到右扫描表达式,依次读入表达式中每个字符。若所读字符为“@”,且操作符的栈顶元素也为“@”时,则输出操作数栈中的栈顶数据,即为运算的结果,结束处理。否则,进行下面处理。 3)若为操作数,入操作数栈;若为操作符,则要将当前操作符和操作符栈中的栈顶操作符进行优先级比较。 《数据结构》
①如果当前操作符的优先级大于栈顶操作符的优先级,则①如果当前操作符的优先级大于栈顶操作符的优先级,则 将当前操作符压入操作符栈中;转第(2)步。 ②如果当前操作符的优先级等于栈顶操作符的优先级,则 将当前操作符栈顶的操作符出栈。转第(2)步。 ③如果当前操作符的优先级小于栈顶操作符的优先级,将当 前操作符栈顶的操作符出栈,并从操作数栈中顺次出栈两 个操作数(先退出作为运算符的右操作数,后退出作为运 算符的左操作数)。用刚出栈的操作符对两个操作数进行 计算求值,将所得值压入操作数栈中。转第(3)步。 《数据结构》
下图展示了算术表达式3×(7-2)求值的动态过程下图展示了算术表达式3×(7-2)求值的动态过程 《数据结构》
中缀表达式转换为等价的后缀表达式 中缀表达式等价后缀表达式 0.3/(5×2+1) 0.3 5 2×1+/ 16-9×(4+3) 16 9 4 3+×- 2×(x+y)/(1-x) 2 x y +×1 x-/ (25+x) ×(a×(a+b)+b) 25 x+a a b+×b+× 《数据结构》
4.6 栈与递归 递归是一种非常重要的数学概念和解决问题的方法,在计算机科学和数学等领域有着广泛的应用。在计算机科学中,许多数据结构,如广义表、树和二叉树等,由于其自身固有的递归性质,都可通过递归方式加以定义并实现许多问题的算法设计。在计算机内部,通过栈来实现递归算法。所以递归是栈的一个实际应用。 《数据结构》
1 n=0,1 fact (n)= n*fact(n-1) n>1 采用递归算法求解正整数n的阶乘n! • 数学定义 - • 求n的阶乘的递归函数算法如下: long fact(int n) { long f; if(n==0) f=1; else f=n*fact(n-1); return f; } 《数据结构》
进行fact(4)调用的系统栈的变化情况 《数据结构》
函数fact(4)的递归调用过程的执行流程 《数据结构》
4.7 队列 一、队列的定义 队列也是一种运算受限的线性表。在这种线性表上,插入限定在表的某一端进行,删除限定在表的另一端进行。允许插入的一端称为队尾,允许删除的一端称为队头。 特点:队列中数据元素的入队和出队过程是按照 “先进先出” 的原则进行的。因此,队列又 称为“先进先出”的线性表,简称FIFO表。 《数据结构》
二、队列的基本运算 • 队列初始化InitQueue(SQ):设置一个空队列SQ • 入队列EnQueue(SQ,x):将x插到队列SQ的队尾 • 出队OutQueue(SQ,x) 将队头元素赋给x,并删除队头元素 • 取队头元素GetHead(SQ,x):由x返回队头结点值 • 判队列空Empty (SQ):队列SQ是否为空。 若为空返回1,否则返回0。 《数据结构》
三、队列的顺序存储结构及其基本运算的实现 1.队列的顺序存储结构 队列的顺序存储结构简称顺序队。顺序队是用一维数组依次存放队列中的元素和分别指示队列的首端和队列的尾端的两个变量组成。这两个变量分别称为“队头指针”和“队尾指针”。 顺序队列的数据类型定义如下 #define MaxSize < 队列的最大容量> typedef struct{ ElemType data[MaxSize]; int front,rear; }SQUEUE; 《数据结构》
顺序队列(MaxSize=5)的几种状态 (a)表示空队列,rear=front=0。 (b)元素A入队后,rear=1,front=0。 (c)B,C依次入队后,rear=3,front=0。 (d)A,B,C依此出队后, rear=front=3。 (e)D入队后,rear=4, front=3。 (f)D出队后,rear=front=4。 《数据结构》
如图所示是具有五个存储单元的循环队列 (a)表示空队列,rear= front=0。 (b)元素A入队后,rear=1, front=0。 (c)B,C,D依次入队后,rear=4, front=0。 (d)A出队后, front=1 ,rear=4。 (e)B,C,D出队后, rear= front=4。 《数据结构》
2.基本运算顺序队列的实现 • 队列初始化 • void InitQueue(SQUEUE *SQ) • { SQ->rear=SQ->front=0;} • 入队列 • int EnQueue(SQUEUE *SQ,ElemType x) • { if((SQ->rear+1)%MaxSize==SQ->front){ • printf("\n Queue is full!"); • return 0;} • SQ->rear=(SQ->rear+1)%MaxSize; • SQ->data[SQ->rear]=x; • return 1;} 《数据结构》
出队 • int OutQueue(SQUEUE *SQ,ElemType *x) • { if(Empty(SQ)){ • printf("\n Queue is free"); • return 0; } • SQ->front=(SQ->front+1)%MaxSize; • *x=SQ->data[SQ->front]; • return 1; • } • 判队列空 • int Empty(SQUEUE *SQ) • { return(SQ->rear==SQ->front)?1:0;} 《数据结构》
四、队列的链式存储结构及其基本运算的实现 1. 队列的链式存储结构 队列的链式存储结构简称为链队。它实际上是一个同时带有首指针和尾指针的单链表。头指针指向表头结点,而尾指针则指向队尾元素。 链队结构示意图 《数据结构》
链队的数据类型定义如下 : typedef struct qnode{ //链队结点的类型 ElemType data; struct qnode *next; }QTYPE; typedef struct qptr{ //链队指针类型 QTYPE *front,*rear; }SQUEUE; SQUEUE LQ; 《数据结构》
链队运算指针变化情况 《数据结构》
2.基本运算链队的实现 • 队列初始化 void InitQueue(SQUEUE *LQ) { QTYPE *p; p=(QTYPE *)malloc(sizeof(QTYPE)); p->next=NULL; LQ->front= LQ->rear=p; } 《数据结构》
入队列 • int EnQueue(SQUEUE *LQ,ElemType x) • { QTYPE *s; • s=(QTYPE *)malloc(sizeof(QTYPE)); • s->data=x; • s->next=LQ->rear->next; • LQ->rear->next=s; LQ->rear=s; • return 1; • } • 判队空 • int Empty(SQUEUE *LQ) • { return(LQ->front==LQ->rear?1:0);} 《数据结构》
出队列 int OutQueue(SQUEUE *LQ,ElemType *x) { QTYPE *p; if(Empty(LQ)){ printf("\n Queue is free!"); return 0;} p=LQ->front->next; *x=p->data; LQ->front->next=p->next; if(LQ->front->next==NULL) LQ->rear=LQ->front; free(p); return 1; } 《数据结构》
取队头元素 int GetHead(SQUEUE *LQ,ElemType *x) { if(Empty(LQ)){ printf("\n Queue is free!"); return 0; } *x=LQ->front->next->data; return 1; } 《数据结构》