1、1,栈与队列上机题讲解,一、 表达式的后缀式计算(9较难),二、 回文游戏,三、 地图四染色(8,选做),四、 n皇后问题(6),五、 k阶斐波那契序列(5),2,表达式 := (操作数) + (运算符) + (操作数)操作数 := 简单变量 | 表达式简单变量 :=标识符 | 无符号整数,二元运算符的表达式的三种标识方法,3,表达式的三种标识方法:,设 Exp = S1 + OP + S2,则称 OP + S1 + S2 为前缀表示法,S1 + OP + S2 为中缀表示法,S1 + S2 + OP 为后缀表示法,4,例如: Exp = a b + (c d / e) f 前缀式: 中缀式:
2、 后缀式:,结论:,1)操作数之间的相对次序不变;,2)运算符的相对次序不同;,3)中缀式丢失了括弧信息,致使运算的次序不确定;,+ a b c / d e f a b + c d / e f a b c d e / f +,5,5)后缀式的运算规则为:每个运算符和在它之前出现 且紧靠它 的两个操作数构成一个最小表达式;运算符在表达式中出现的顺序恰为表达式的运算顺序。,4)前缀式的运算规则为:连续出现的两个操作数和在它们之前且紧靠它们的运算符构成一个最小表达式;,6,如何从后缀式求值?,先找运算符,再找操作数,例如:a b c d e / f +,ab,d/e,c-d/e,(c-d/e)f,7
3、,如何从原表达式求得后缀式?,每个运算符的运算次序要由它之后的一个运算符来定,在后缀式中,优先数高的运算符领先于优先数低的运算符。,分析 “原表达式” 和 “后缀式”中的运算符: 原表达式: a + b c d / e f 后缀式: a b c + d e / f ,8,从原表达式求得后缀式的规律为:,1) 设立运算符栈;,2) 设表达式的结束符为“#”, 预设运算符栈的栈底为“#”;,3) 若当前字符是操作数,则直接发送给后缀式。,9,4) 若当前运算符的优先数高于栈顶运算符,则进栈;,5) 否则,退出栈顶运算符发送给后缀式;,6) “(” 对它之前后的运算符起隔离作用,“)”可视为自相应左
4、括弧开始的表达式的结束符。,从原表达式求得后缀式的规律为:,10,例如: Exp = a b + (c d / e) f#,后缀式: Suffix =,a,d,c,f,+,b,+,(,-,e,/,/,11,void transform(char suffix , char exp )/从原表达式求得后缀式/s是运算符栈,s栈底预设 #,OP是运算符集合 / CrtExptree,InitStack(S); Push(S, #); p = exp; ch = *p;while (!StackEmpty(S) if (!IN(ch, OP) / 若ch是操作数Pass( Suffix, ch);
5、else A: / 若ch是运算符if ( ch!= # ) p+; ch = *p; else Pop(S, ch); Pass(Suffix, ch); / while, ,12,A: switch (ch) case ( : Push(S, ch); break;case ) : Pop(S, c);while (c!= ( ) Pass( Suffix, c); Pop(S, c) break; defult : while(Gettop(S, c) / switch,若ch的优先级比c低,为真,13,1) 设立操作数栈S;,2) 设表达式的结束符为“#”;,3)读入表达式一个字符,若
6、当前字符是操作数,则压入栈中,转4)。,求后缀式的值:,14,若当前字符是运算符optr,从栈中弹出2个数,将运算结果再压入栈,即:x2=POP(S); x1=POP(S);PUSH(S,(x1 optr x2);,读下一字符,重复3)和4)直至读到结束符#;,栈顶,x=POP(S)即后缀式相应的表达式的值。,15,int cal(char suffix-exp )/求后缀式表达式的值/s是运算数栈,OP是运算符集合 /cal,InitStack(S); i = 0; ch = suffix-exp0; while (ch#) if (!IN(ch, OP) / 若ch是操作数Push( S,
7、 ch); else / 若ch是运算符 x2=POP(S); x1=POP(S);/取出两个操作数PUSH(S,(x1 ch x2); i+; ch= suffix-expi; / while,16,算法思路: 1.读入字符串 2.去掉空格(原串) 3.压入栈 4.原串字符与出栈字符依次比较若不等,非回文若直到栈空都相等,回文,字符串:“madam im adam”,回文游戏: 顺读与逆读字符串一样(不含空格),17,int IsHuiwen( char *S), SeqStack T;int i , n; char t1;InitStack( / 比较完毕均相等则返回 1,18,地图四染色
8、问题,“四染色”定理是计算机科学中著名的定理之一。 使地图中相邻的国家或行政区域不重色,最少可用四种颜色对地图着色。 证明此定理的结论,利用栈采用回溯法对地图着色。 思想:对每个行政区编号:1-7对颜色编号;a、b、c、d; 从第一号行政区开始逐一染色,每一个区域逐次用四种颜色进行试探,若所取颜色与周围不重,则用栈记下来该区域的色数,否则依次用下一色数进行试探。若出现a-d均与周围发生重色,则需退栈回溯,修改当前栈顶的色数。,19,1,2,2,3,4,1,4,3,3,4,2,3,1# 紫色 2# 黄色 3# 红色 4# 蓝色,地图四染色举例,SK,20,Void mapcolor(int R,
9、int n,int s), s1=1; / 1号区域染1色a=2; J=1; / a为区域号,J为染色号while ( a4) a=a-1; J=sa+1 /回溯,修改颜色,21,设n皇后问题的解为 (x1, x2, x3, ,xn), 约束条件为:其中任意两个xi 和xj不能位于棋盘的同行、同列及同对角线。,如何表示棋盘中放置的棋子?由于每行、列及对角线上只能有一个棋子,因而对每列来说,只需记录该列中棋子所在的行号,故用一维数组即可。,皇后问题求解,22,设四皇后问题的解为 (x1, x2, x3, x4), 其中: xi (i=1,2,3,4) Si=1, 2, 3, 4 约束条件为:其中
10、任意两个xi 和xj不能位于棋盘的同行、同列及同对角线。,按回溯法的定义,皇后问题求解过程为: 解的初始值为空;首先添加 x1=1, 之后添加满足条件的 x2=3,由于对所有的 x31,2, 3, 4都不能找到满足约束条件的部分解(x1, x2, x3), 则回溯到部分解(x1), 重新添加满足约束条件的x2=4, 依次类推(按行存列号)。,按四皇后问题求解举例,23,24,void queen(int i, int n) / 进入本函数时,在nn棋盘前i-1行已放置了互不攻/ 击的i-1个棋子。现从第 i 行起继续为后续棋子选择 / 满足约束条件的位置。当求得(in)的一个合法布局/ 时,输
11、出之。if (in) 输出棋盘的当前布局;else for (j=1; j=n; +j) 在第 i 行第 j 列放置一个棋子;if (当前布局合法) queen(i+1, n);移去第 i 行第 j 列的棋子; / trial,25,#include #define n 8 / n为皇后个数,m为摆法计数 int m=0,an; / ai存放第i个皇后放置的行号, int ok(int i, int j) /检查(i,j)上能否放棋子 j1=j; i1=i; ok1=1; /1. 检查第i行上能否放棋子while( (j11)return ok1 ,26,Void queen(int j) /
12、从第j列开始逐个试探 if (jn) m+; printf(“m=%d “,m);for (i=1;i=n;i+) printf(“ %d“,ai);printf(“n”); else for( i=1; i=n;i+)if(ok(i,j) /检查(i,j)上能否放棋子 aj=i; /在(i,j)上放一个棋子queen(j+1) ; main()queen(1);,27,k阶斐波那契序列,试利用循环队列编写求k阶斐波那契序列中前n+1项(f0, f1 , f2 , fn )的算法,要求满足fn max而fn+1 max ,其中max为某个约定的常数。(注意本题所用循环队列的容量仅为k,则在算法
13、执行结束时,留在循环队列中的元素应是k阶斐波那契序列中的最后k项fn-k+1 , fn ) 。,28,f0=0 , f1=0 , , fk-2=0 , fk-1=1, fn = fn-1 + fn-2 + fn-k (n=k,k+1,),fi = fi-1 + fi-2 + fi-k fi+1 = fi + fi-1 + fi-2 + fi-k+1 两式相减得: fi+1 = 2*fi - fi-k,k阶斐波那契序列,29,void fb(int k,int max) /方法一,队列的容量为k for(i=0;imax) n=n-2; else n=n-1;if (max=1) n=k;fk=1; ,30,方法二,Void fb(int k,int max) for(i=0;imax) n=n-2; else n=n-1;if (max=1) n=k;fk=1;if (max=0) n=k-2; ,利用fi+1 = 2*fi - fi-k ,队列的容量为k+1,