1、第二章 线性结构,一、栈的定义和运算,二、顺序栈,三、链栈,四、栈的应用,五、小结,第 2 节 栈,第二章 线性表,2.2 栈,一、栈的概念和运算,栈的定义 栈是限定只能在表的一端进行插入和删除操作的线性表。允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom)。 设栈s=(a1,a2,.,an),a1称为栈底元素,an称为栈顶元素。 栈中元素按a1,a2,.,an次序进栈,又按an,.,a2,a1次序退栈。因此栈的操作特点是:后进先出(LIFO)或先进后出(FILO); n=0时称为空栈。,第二章 线性表,2.2 栈,一、栈的概念和运算,2.栈的存储 栈的顺序存储顺序栈,栈的
2、链式存储链栈 用指针来实现的栈叫链栈。栈的容量事先不能估计时采用这种存储结构。,第二章 线性表,2.2 栈,一、栈的概念和运算,3.栈的基本运算 栈的初始化 判栈空 Empty 入栈 Push 出栈 Pop 读栈顶元素,栈顶指针top,指向实际栈顶后的空位置,初值为0,进栈,A,出栈,栈满,B,C,D,E,F,设一维数组长度为M top=0,栈空,此时出栈,则下溢(underflow) top=M,栈满,此时入栈,则上溢(overflow),栈空,2.2 栈,二、顺序栈 ( 实现:一维数组sM ),2.2 栈,二、顺序栈 1.栈结构,typedef struct datatype dataMA
3、X;int top; SeqStack;,创建一个栈: SeqStack *s;,2.2 栈,二、顺序栈 2.栈的初始化,SeqStack *StackList() /构造一个空栈 SeqStack *s;s=(SeqStack*)malloc(sizeof(SeqStack);s-top=0; /约定top始终指向新数据元素将存放的位置return s; ,2.2 栈,二、顺序栈 3.判栈空,int Empty(SeqStack *s) if( s-top = 0) return 1; / 栈空,返回else return 0; / 栈非空,返回 ,2.2 栈,二、顺序栈 4.入栈,int
4、Push(SeqStack *s,datatype x) if( s-top = Max) return 0; / 栈满,返回else s-datas-top=x; /x值入栈s-top+; /指示器加1return 1; /成功,返回 ,2.2 栈,二、顺序栈 .出栈且读栈顶元素,int Pop(SeqStack *s,datatype *x) if( Empty(s) return 0; / 栈空,返回else s-top-; /指示器减1*x=s-datas-top ; /出栈return 1; /成功,返回 ,2.2 栈,三、链栈,top问题?若top指向最后一个元素,则入栈和出栈操作
5、时top的值都要变化,在C中参数传值方式难实现! 解决办法:用增加头结点方式,2.2 栈,三、链栈,.结点定义 typedef struct Node datatype data;struct Node *next; StackNode;,栈空: 若有头结点时top-next=Null为空栈; 若无头结点时top=Null为空栈,建立一个链栈: StackNode *top;,如何判栈空?,2.2 栈,三、链栈,2.链栈初始化 StackNode *Creat( ) StackNode *top; / 创建头结点top= (StackNode *)malloc(sizeof(StackNode
6、);top-next=Null; / 设置头结点return top; /返回头指针 ,2.2 栈,三、链栈,3.入栈头插法 void Push(StackNode *s, datatype x ) StackNode *p; / 创建一个新结点pp= (StackNode *)malloc(sizeof(StackNode);p-data=x; / 设置p结点p-next=s-next; / p结点加入链栈中s-next=p; /头结点next指向新的栈顶结点p ,x,StackNode *p;,p= (StackNode *)malloc(sizeof(StackNode);,p-data
7、=x;,p-next=s-next;,s-next=p;,2.2 栈,三、链栈,.出栈 int Pop(StackNode *s, datatype *x ) StackNode *p; / 创建一个新结点pif (s-next=Null ) return 0; /栈空,返回失败*x= s-next-data; / 传回栈顶结点的数据p=s-next; / p结点指向栈顶结点s-next=p-next; /头结点next域指向p下一个结点free(p); / 释放p结点删除栈顶结点return 1; /返回成功 ,2.2 栈,四、栈的应用, 数制转换 函数的递归调用 表达式求值 迷宫求解, 数
8、制转换,2.2 栈,算法见书第55页,自学,2.2 栈, 表达式求值1. 高级语言中的表达式是用人们熟悉的公式形式书写的, 如:a+b*c-d 中缀表达式 为解决运算顺序问题把运算符分成若干等级级别高的先运算。 . 后缀表达式(也称逆波兰表达式RPN) 如:abc*+d- 运算符在运算对象的后面,无需判断运算符优先级,遇到运算符就计算运算对象,简单方便。,2. 栈,.中缀表达式求值 为解决运算顺序问题把运算符分成若干等级,称为优先数。 运算符: * / * + - ( ) 界限符: ; 优先级: 4 3 3 2 2 1 1 0 输入:表达式符号序列 输出:表达式值 y 为进行表达式的翻译,需设
9、立两个栈,分别存放操作数(NS)和运算符(OS), 初始化栈 NS、OS:在OS中放入表达式结束符“;” 。,1.中缀表达式求值 A*B + C/D;,自左至右扫描表达式,对扫描的每个符号w作如下处理: 1) 若w为操作数,将其压入NS栈,继续扫描 2) 若w为运算符,需看当前OS的栈顶元素优先数: 若w大于OS栈顶运算符,则将w压入OS栈,继续扫描。 若w为“;”,且OS栈顶也为“;”,则表示表达式处理结束,此时NS栈顶的元素即为此表达式的结果值。 若w为“)”,且OS栈顶也为“(”,从OS栈中弹出“(”,继续扫描. 若w小于或等于OS栈顶运算符,则从NS栈中弹出两个操作数,设为x、y,再从
10、OS栈中弹出一个运算符,设为,构成一条指令:y x T,将结果T送入NS栈。继续与OS栈顶元素比较,2. 栈,例 A*B + C/D;,2. 后缀表达式求值 只需一个操作数栈,步骤: 1) 读入表达式一个字符 2) 若是操作数,压入栈,转4 3) 若是运算符,从栈中弹出2个数,将运算结果再压入栈 4) 若表达式输入完毕,栈顶即表达式值;若表达式未输入完,转1,例 计算 4+3*5,转换为后缀表达式:435*+,后缀表达式计算简单方便,如何完成由中缀到后缀的转换呢?,中缀表达式到后缀表达式的转换:需用一个运算符栈,请把 a+(b*c+d)/e 转换为后缀表达式,输入:中缀表达式 输出:后缀表达式
11、 从左至右扫描中缀表达式,每个符号做如下判断: 1)若是变量则作为新表达式的变量; 2)若是“(”就入栈; 3)若是运算符则检查栈,如果栈空,则运算符进栈,否则,与栈顶运算符比较:(1) 若小于或等于栈顶运算符级别,则栈顶元素出栈,写入新表达式,继续比较下一个栈顶运算符;(2) 反之,将新运算符入栈; 4)若是“)”,则将栈顶运算符一一出栈,写入新表达式中,直到读出相应的“(”为止,然后删去“(”。,abc*d+e/+,例中缀表达式A * B + C / D ; 求等价的后缀表达式?,*,2. 栈, 过程嵌套和递归调用过程嵌套和递归调用是程序设计中很重要的应用。当过程调用子程序时,必须把断点的
12、信息及地址保存起来,当子过程执行完毕返回时,取用这些信息,找到返回地址,从断点处继续执行。当程序中出现多重嵌套调用时,必须开辟一个栈,将各层断点信息依次入栈,当各层子过程返回时,又以相反的次序从栈顶取出,这样才能保证程序的正常执行。,函数的递归调用,1. 定义: 在调用一个函数的过程中直接或间接地调用该函数本身。 直接调用 int f(int x) int y,z;z=f(x);return (2*z); ,f 函数,调用 f函数,int f1(x) int x; int y,z;z=f2( y);return (2*z); ,int f2(t) int t; int a,c;c=f1(a);
13、return (3+c); ,间接调用,特点是无终止的递归调用,因此,应该给定一个限制递归次数的条件。,long fac ( int n) long s;if ( n= =1) s=1;else s=n*fac(n-1);return(s); ,例如:写一函数求n!,以求4的阶乘为例:,fac(4)=4*fac(3),fac(3)=3*fac( 2),fac(2)=2*fac( 1),fac(1)=1,fac(4)=4*3*2*1,fac(2)=2*1,fac(3)=3*2*1,下 推,回 代,利用栈实现递归调用,s,long fac ( int n) long s;if ( n= =1) s=1;else s=n*fac(n-1);return(s); ,main() printf(“%d”fac(4); ,回文游戏:顺读与逆读字符串一样(不含空格),1.读入字符串 2.去掉空格(原串) 3.压入栈 4.原串字符与出栈字符依次比较若不等,非回文若直到栈空都相等,回文,字符串:“madam im adam”,第二章 线性表,2. 栈,五、小结,1、理解栈的概念和特点 2、掌握进/出栈的算法(顺序栈和链栈) 3、了解栈的应用 4、掌握表达式求值,第二章 线性表,2. 栈,六、作业与实验, 教材第70页习题中的一14 实验:二、,本节结束,返回目录,