基本概念就不多介绍了,直接开始介绍思路以及容易踩的坑。
思路要点
1. 整个过程建立在栈的基础上实现。
需要准备的栈相关的函数:
Push: 压栈
Pop: 弹栈
Top: 查看栈顶元素
isEmpty: 判断栈是否为空建议用数组结构实现栈的结构体声名,书上说这样更符合未来实际开发。
关于实现方式的细节可以查看: 三种栈的实现方式
2. 我们需要准备一个栈和两个字符型的数组
- 准备的栈的目的是改变原中缀表达式中符号的出场顺序,以一种适合计算机运算的出场顺序(后缀表达式)让计算机得到结果。这个栈内只能进出运算符号,跟数字没关系。
- 第一个字符组用来记录用户输入的中缀表达式 "input"
第二个字符组用来记录用户输出的后缀表达式 "Postfix"
3. 清晰划分各种符号的等级
等级用于决定一个新的符号即将入栈时是否将符号栈中的原有的符号弹出
- level 2 <kbd>*</kbd> 和 <kbd>/</kbd>
- level 1 <kbd>+</kbd> 和 <kbd>-</kbd>
- level 0 <kbd>(</kbd> 和 <kbd>)</kbd>
4. 中缀表达式的读取
- 读到数字直接写入后缀表达式 Postfix 的字符组
- 读到字符(运算符)将字符交给处理字符比较的函数 “OperatorCmp”
5. OperatorCmp 字符比较
- 传入两个参数 now 和 check
now 是在读取中缀表达式时识别到的字符,也是即将入栈的字符。
check 是通过Top函数返回的栈顶的元素。
这个函数目的在于通过now的级别和运算符种类,决定下一步栈中元素的动态。
-
栈中元素的改变(按照优先级顺序叙述,用else if)
- 第一种情况:栈中没有元素,第一个运算符直接入栈(这种情况要在函数外进行特殊情况处理,因为函数需要传入栈顶元素值,栈空时强制传入值可能造成数组越界)
- 第二种情况:now是<kbd>(</kbd>,直接将now压栈
- 第三种情况:now是<kbd>)</kbd>,在阐述完后两种情况后再回来讲述。
- 第四种情况:now.level <= check.level
意味着这是<kbd></kbd>或<kbd>/</kbd>,这里我们要和栈顶元素进行比较。
如果 now.level <= check.level 我们要将栈顶元素弹栈,将弹栈元素加入 Postfix 字符组中。
这还没完!我们的比较还没有结束,根据后缀表达式的定义,我们很有可能掉进只将前面一个元素弹出的坑*,我们的比较只有两个等级而且最后我们还会执行统一清栈的操作,如果这个位置不连续对栈顶元素进行比较,会将本该在这部分弹栈的元素在最后清栈的操作中弹出,由于我们只有两个等级,所以对最终计算结果没有影响,但如果等级变多(例如出现了^)或者某个OJ要求你输出后缀表达式,那就GG了。(测试:2 + 3 * 4 - 5 结果: 2 3 4 * + 5 -)
所以,我们要循环多次比较栈顶元素,直到 now.level > check.level 循环跳出。
最后不要忘记,将now压栈!结束。 - 第五种情况:now.level > check.level
将now压栈,结束。 - 回过头来我们介绍第三种情况。识别到<kbd>)</kbd>时,我们应该做的操作是循环弹栈,直到识别到栈顶元素是<kbd>(</kbd>,那么问题来了,我们需不需像第四种和第五种情况一样考虑栈顶元素的等级决定是否弹栈呢?答案是不用,后括号前的元素已经帮你解决了这个难题,不然我们又要开启递归了。每个压栈元素触发的比较已经可以保证逆序弹栈此时栈中剩余元素,将弹栈元素加入 Postfix 字符组中就可以正常生成后缀表达式。
所以我们要做的操作就是识别到<kbd>(</kbd>之前,循环弹栈。
循环结束后完成。
示例
6. 清空字符栈
- 将中缀表达式读取完后,后缀表达式 Postfix 还没有生成完毕,因为栈中会有剩余运算符元素,此处的处理方法等同于上面的第三种情况,可以想象将用户输入的中缀表达式两侧加上括号,自然解决办法相同。
逆序弹栈此时栈中剩余元素,将弹栈元素加入 Postfix 字符组中。
至此完成全部操作。
代码
这里是一个要求根据中缀表达式生成后缀表达式并计算出结果的结果代码。以供参考。有一些细节写的繁琐的是因为适应我们校奇怪的oj系统...
#include<stdio.h>
#include<stdlib.h>
typedef struct Operator
{
char moperator;
int level;
}Operator;
Operator op[10];
typedef struct LinkStack
{
Operator opNodes[100000];
int top;
}LinkStack;
LinkStack opstack;
char Postfix[100000];
int PostfixNum = 0;
void InitOperater(){
op[0].moperator = '+';
op[0].level = 1;
op[1].moperator = '-';
op[1].level = 1;
op[2].moperator = '*';
op[2].level = 2;
op[3].moperator = '/';
op[3].level = 2;
op[4].moperator = '(';
op[4].level = 0;
op[5].moperator = ')';
op[5].level = 0;
}
int isEmpty(LinkStack &stack)
{
return (stack.top == -1);
}
void pop(LinkStack &stack)
{
stack.top--;
}
void push(LinkStack &stack, Operator pushed)
{
stack.top++;
stack.opNodes[stack.top] = pushed;
}
Operator Top(LinkStack &stack)
{
return stack.opNodes[stack.top];
}
void operatorCmp(LinkStack &opstack,Operator now,Operator check){
if(now.moperator == '('){
push(opstack,now);
}
else if(now.moperator == ')'){
now = Top(opstack);
pop(opstack);
check = Top(opstack);
while(check.moperator != '('){
Postfix[PostfixNum] = now.moperator;
PostfixNum++;
pop(opstack);
now = check;
check = Top(opstack);
}
Postfix[PostfixNum] = now.moperator;
PostfixNum++;
pop(opstack);
}
else if(check.level >= now.level){
while(check.level >= now.level){
Postfix[PostfixNum] = check.moperator;
PostfixNum++;
pop(opstack);
if(isEmpty(opstack))break;
check = Top(opstack);
}
push(opstack,now);
}
else if(check.level < now.level)
push(opstack,now);
}
double FindSolution(char* Postfix){
double Digit[100000];
int DigitNum = 0;
for(int i=0;i<PostfixNum;i++){
if(Postfix[i]>='0'&&Postfix[i]<='9'){
Digit[DigitNum] = Postfix[i]-'0';
DigitNum++;
}
if(Postfix[i]=='+'){
int firstNum = DigitNum - 1;
int secondNum = DigitNum - 2;
double first = Digit[firstNum];
double second = Digit[secondNum];
Digit[secondNum]=second+first;
DigitNum--;
}
if(Postfix[i]=='-'){
int firstNum = DigitNum - 1;
int secondNum = DigitNum - 2;
double first = Digit[firstNum];
double second = Digit[secondNum];
Digit[secondNum]=second-first;
DigitNum--;
}
if(Postfix[i]=='*'){
int firstNum = DigitNum - 1;
int secondNum = DigitNum - 2;
double first = Digit[firstNum];
double second = Digit[secondNum];
Digit[secondNum]=second*first;
DigitNum--;
}
if(Postfix[i]=='/'){
int firstNum = DigitNum - 1;
int secondNum = DigitNum - 2;
double first = Digit[firstNum];
double second = Digit[secondNum];
Digit[secondNum]=second/first;
DigitNum--;
}
}
return Digit[0];
}
int main(){
InitOperater();
opstack.top = -1;
char s[100000];
scanf("%s",s);
int i=0;
while(s[i]!='\0'){
if (s[i] >= '0'&&s[i] <= '9') {
Postfix[PostfixNum] = s[i];
PostfixNum++;
}
else{
for(int j = 0; j<= 5; j++){
if (s[i] == op[j].moperator){
if(!isEmpty(opstack))operatorCmp(opstack,op[j],Top(opstack));
else push(opstack,op[j]);
}
}
}
i++;
}
while(!isEmpty(opstack)){
Postfix[PostfixNum] = Top(opstack).moperator;
PostfixNum++;
pop(opstack);
}
double solution = FindSolution(Postfix);
printf("%.2lf\n",solution);
for(int i=0;i<PostfixNum;i++){
printf("%c ",Postfix[i]);
}
return 0;
}