07_001 |
void conversion() { InitStack(S); scanf("%d",N); while(N) { Push(S,N%8); N = N/8; } while(!StackEmpty(S)) { Pop(S,e); printf("%d",e); } } //conversion |
例二、括号匹配的检查
([]())或[([][])]等为正确的格式,[()]或([()]或(()))均为不正确的格式。检验括号是否匹配的方法用“期待的急迫程度”这个概念来描述。例如考虑下列括号序列:
[ |
( |
[ |
] |
[ |
] |
) |
] |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
分析可能出现的不匹配的情况:
到来的右括弧非是所“期待”的;
到来的是“不速之客”;
直到结束,也没有到来所“期待”的。
算法的设计思想:
1) 凡出现左括弧,则进栈;
2) 凡出现右括弧,首先检查栈是否空,若空则进栈,表明“右括弧”多了,否则和栈顶元素比较,若匹配,则“左括弧出栈”否则不匹配。
3) 表达式检验结束时,若栈空,则匹配正确,否则表明“左括弧”多了
07_002 |
Status matching(string & exp) { int state = 1; while(i <= Length(exp) && state) { switch of exp[i] { case 左括弧: { Push(S,exp[i]); i ++; break; } case 右括弧: { if(NOT StackEmpty(S) && GetTop(S) = "右括弧") { Pop(S,e); i ++; } else { state = 0; } break; } //... ... } if(StackEmpty(S) && state) return OK; else return ERROR; } } |
例三、行编辑程序问题
如何实现?
“每接受一个字符即存入”{不恰当。}
建立一个输入缓冲区,用以接受用户输入的一行字符,然后逐行存入用户数据区。
用户输入一行的过程中,允许用户输入出差错,并在发现有误时可以及时更正。
假设从终端接受了这样两行字符:
whli##ilr#e(s#*s)
outcha@putchar(*s=#++);
则世家有效的是下列两行:
while(*s)
putchar(*s++);
07_003 |
while(ch != EOF) { //EOF为全文结束符 while(ch != EOF && ch != '\n') { switch(ch) { case '#': Pop(S,c); break; case '@': ClearStack(S); break; //重置S为空栈 default: Push(S,ch); break; } ch = getchar(); //从终端接收下一个字符 } //将从栈底到栈顶的字符传送至调用过程的数据区; ClearStack(S); //重置S为空栈 ... } |
例四、迷宫求解
通常用的是“穷举求解”的方法
求迷宫路径算法的基本思想是:
若当前位置“可通”,则纳入路径,继续前进
若当前位置“不可通”,则后退,换向探索
若四周均“不可通”,则路径中删除
求迷宫中一条从入口到出口的路径的算法:
设定当前位置的初值为入口位置;
do
{
若当前位置可通,则
{
将当前位置插入栈顶;若该位置是出口位置,则结束;否则切换当前位置的东邻方块为新的当前位置
}
否则
{
…
若栈不空且栈顶位置尚有其它方向未被探索,则设定新的当前位置为:沿顺时针方向旋转找到栈顶位置的下一个邻块;
若栈不空但栈顶位置的四周均不可通,则
{
删去栈顶位置;从路径中删去该通道块,若栈不空,则重新测试新的栈顶位置,直到找到一个可通的相邻块或出栈至栈空;
}
}
}while(栈不空);
08
例五、表达式求值
限于二元运算符的表达式定义:
表达式::=(操作数)+(运算符)+(操作时)
操作数::=简单变量|表达式
简单变量::=标识符|无符号整数
在计算机中,表达式可以有三种不同的标识方法
设Exp=S1+OP+S2,则称OP+S1+S1为表达是的的前缀表示法;称S1+OP+S1为表达式的中缀表示法;称S1+S1+OP为表达式的后缀表示法。可见,它以运算符所在不同位置命名的。
例如: Exp=a*b+(c-d/e)*f
前缀式:+*ab*-c/def
中缀式:a*b+c-d/e*f
后缀式:ab*cde/-f*+
表达式 Exp=a*b+(c-d/e)*f |
||
前缀式演算: +[a*b][(c-d/e)*f] +*ab[*(c-d/e)f] +*ab*-c[d/e]f +*ab*-c/def |
中缀式演算: [a*b]+[(c-d/e)*f] a*b+[c]-[d/e]*f a*b+c-d/e*f |
后缀式演算: [a*b][(c-d/e)*f]+ ab*[c-d/e]f*+ ab*c[d/e]-f*+ ab*cde/-f*+ |
结论:
1) 操作数之间的相对次序不变;
2) 运算符的相对次序不同;
3) 中缀式丢失了括弧信息,致使运算的次序不确定;
4) 前缀式的运算规则为:连续出现的两个操作数和在它们之前且紧靠他们的运算符构成一个最小表达式;
5) 后缀式的运算规则为:运算符在式中出现的顺序为表达式的运算顺序;每个运算符和在它之前出现且紧靠它的两个操作数构成一个最小表达式
如何从后缀式求值?
先找运算符,再找操作数
如何从原表达式求得后缀式?
分析“原表达式”和“后缀式”中的运算符
原表达式:a+b*c-d/e*f
后缀式: abc*+de/f*+
每个运算符的运算次序要由它之后的一个运算符来定,在后缀式中,优先数高的运算符领先于数低的运算符。
从原表达式求得后缀式的规律为:
1) 设立操作数栈
2) 设表达式的结束符为“#”,予设操作符的栈底为“#”
3) 若当前字符是操作数,则直接发送给后缀式
4) 若当前运算符对优先数高于栈顶运算符,则进栈
5) 否则,退出栈顶运算符发送给后缀式
6) “(”对它前后的运算符起隔离作用,“)”可视为字相应左括弧开始的表达式
08_001 |
void transform(char suffix[], char exp[]) { InitStack(S); Push(S, '#'); p = exp; ch = *p; while(! StackEmpty(S)) { if(! IN(ch, OP)) { Pass(Suffix, ch); } else { switch(ch) { case '(': Push(S, ch); break; case ')': { Pop(S, c); while(c != '(') { Pass(Stuffix, c); Pop(S, c); } } break; default: { while(! Gettop(S, c) && (precede(c, ch))) { Pass(Stuffix, c); Pop(S, c); } if(ch != '#') { Push(S, ch); } }// defult break; }//switch }//else if(ch != '#') { p ++; ch = *p; } }//while }//transform |