知识补充:from wiki
调度场算法(Shunting Yard Algorithm)是一个用于将中缀表达式转换为后缀表达式的经典算法,由艾兹格·迪杰斯特拉引入,因其操作类似于火车编组场而得名。
https://zh.wikipedia.org/wiki/%E8%B0%83%E5%BA%A6%E5%9C%BA%E7%AE%97%E6%B3%95
更详细的例子
输入 | 动作 | 输出 (逆波兰表示法) | 运算符栈 | 提示 |
---|---|---|---|---|
3 | 将符号加入输出队列 | 3 | ||
+ | 将符号压入操作符堆栈 | 3 | + | |
4 | 将符号加入输出队列 | 3 4 | + | |
* | 将符号压入操作符堆栈 | 3 4 | * + | *号的优先级高于+号 |
2 | 将符号加入输出队列 | 3 4 2 | * + | |
/ | 将堆栈中元素弹出,加入输出队列 | 3 4 2 * | + | /号和*号优先级相同 |
将符号压入操作符堆栈 | 3 4 2 * | / + | /号的优先级高于+号 | |
( | 将符号压入操作符堆栈 | 3 4 2 * | ( / + | |
1 | 将符号加入输出队列 | 3 4 2 * 1 | ( / + | |
− | 将符号压入操作符堆栈 | 3 4 2 * 1 | − ( / + | |
5 | 将符号加入输出队列 | 3 4 2 * 1 5 | − ( / + | |
) | 将堆栈中元素弹出,加入输出队列 | 3 4 2 * 1 5 − | ( / + | 循环直到找到(号 |
将堆栈元素弹出 | 3 4 2 * 1 5 − | / + | 括号匹配结束 | |
^ | 将符号压入操作符堆栈 | 3 4 2 * 1 5 − | ^ / + | ^号的优先级高于/号 |
2 | 将符号加入输出队列 | 3 4 2 * 1 5 − 2 | ^ / + | |
^ | 将符号压入操作符堆栈 | 3 4 2 * 1 5 − 2 | ^ ^ / + | ^号为从右至左求值 |
3 | 将符号加入输出队列 | 3 4 2 * 1 5 − 2 3 | ^ ^ / + | |
END | 将栈中所有数据加入输出队列 | 3 4 2 * 1 5 − 2 3 ^ ^ / + |
详细的算法
- 当还有记号可以读取时:
- 读取一个记号。
- 如果这个记号表示一个数字,那么将其添加到输出队列中。
- 如果这个记号表示一个函数,那么将其压入栈当中。
- 如果这个记号表示一个函数参数的分隔符(例如,一个半角逗号
,
):
- 从栈当中不断地弹出操作符并且放入输出队列中去,直到栈顶部的元素为一个左括号为止。如果一直没有遇到左括号,那么要么是分隔符放错了位置,要么是括号不匹配。
- 如果这个记号表示一个操作符,记做o1,那么:
- 只要存在另一个记为o2的操作符位于栈的顶端,并且
如果o1是左结合性的并且它的运算符优先级要小于或者等于o2的优先级,或者
如果o1是右结合性的并且它的运算符优先级比o2的要低,那么
将o2从栈的顶端弹出并且放入输出队列中(循环直至以上条件不满足为止);
- 然后,将o1压入栈的顶端。
- 如果这个记号是一个左括号,那么就将其压入栈当中。
- 如果这个记号是一个右括号,那么:
- 从栈当中不断地弹出操作符并且放入输出队列中,直到栈顶部的元素为左括号为止。
- 将左括号从栈的顶端弹出,但并不放入输出队列中去。
- 如果此时位于栈顶端的记号表示一个函数,那么将其弹出并放入输出队列中去。
- 如果在找到一个左括号之前栈就已经弹出了所有元素,那么就表示在表达式中存在不匹配的括号。
- 当再没有记号可以读取时:
- 如果此时在栈当中还有操作符:
- 如果此时位于栈顶端的操作符是一个括号,那么就表示在表达式中存在不匹配的括号。
- 将操作符逐个弹出并放入输出队列中。
- 退出算法。
上面都是抄wiki百科的东西,我自己没看明白那个算法,今天没时间研究了,以后知识储备够了再回来研究一下吧。
下面时小王八老师的简单实现。
#include <stdio.h>
#include <stdlib.h>
#define STACK_INIT_SIZE 20
#define STACKINCREMENT 10
typedef char ElemType;
typedef struct
{
ElemType *base;
ElemType *top;
int stackSize;
}sqStack;
//初始化
InitStack(sqStack *s)
{
s->base = (ElemType *)malloc(STACK_INIT_SIZE * sizeof(ElemType));
if( !s->base )
exit(0);
s->top = s->base;
s->stackSize = STACK_INIT_SIZE;
}
//入栈
Push(sqStack *s, ElemType e)
{
// 栈满,追加空间
if( s->top - s->base >= s->stackSize )
{
s->base = (ElemType *)realloc(s->base, (s->stackSize + STACKINCREMENT) * sizeof(ElemType));
if( !s->base )
exit(0);
s->top = s->base + s->stackSize;
s->stackSize = s->stackSize + STACKINCREMENT;
}
*(s->top) = e; // 存放数据
s->top++;
}
//出栈
Pop(sqStack *s, ElemType *e)
{
if( s->top == s->base )
return;
*e = *--(s->top); // 将栈顶元素弹出并修改栈顶指针
}
//求栈的长度
int StackLen(sqStack s)
{
return (s.top - s.base);
}
int main()
{
sqStack s;//操作符栈,用来存放操作符
char c, e;
InitStack( &s );
printf("请输入中缀表达式,使用英文字符,以#作为结束标志:");
scanf("%c", &c);
while( c != '#' )
{
while( isdigit(c) )
{
printf("%c", c);
scanf("%c", &c);
if( c<'0' || c>'9' )
{
printf(" ");//隔开数字字符
}
}
if( c==')')
{
Pop(&s, &e);
while( e != '(')
{
printf("%c ", e);
Pop(&s, &e);
}
}
else if( '+'==c || '-'==c )
{
if( !StackLen(s) )//空栈
{
Push(&s, c);
}
else
{
do//非空
{
Pop(&s, &e);//弹出栈顶元素
if( e == '(')
{
Push(&s, e);
}
else
{
printf("%c ", e);
}
}while( StackLen(s) && '('!=e );//当栈非空并且'('!=e继续循环
Push(&s, c);
}
}
else if( '*'==c || '/'==c || '('==c )
{
Push(&s, c);
}
else if( '#'== c ) //结束标志
{
break;
}
else //输入不是数字和规定的操作符则错误提示
{
printf("\n出错:输入格式错误!\n");
return -1;
}
scanf("%c", &c);
}
//输出剩余的操作符栈
while( StackLen(s) )
{
Pop(&s, &e);
printf("%c ", e);
}
// 快开学了,哎
return 0;
}