【BUAA 18級資料結構第二題】字尾表示式轉中綴表示式&表示式計算
阿新 • • 發佈:2022-03-13
題面
- 【問題描述】
從控制檯輸入一合法的字尾表示式,其中的運算子只包括+、-、*、/,運算數都是大於等於 0 的整數(除數不為零),按要求輸出計算結果,或輸出計算結果和相對應的中綴表示式。輸出中綴表示式時只包含最少數目的圓括號(即在生成的中綴表示式中若去掉一對括號,則其將不能夠轉換回輸入的字尾表示式)。輸出計算結果時,小數點後保留兩位,例如:10/3 的結果為 3.33
假如輸入的字尾表示式為:
100 25 + 27 25 - / 248 + 201 -
其相對應的中綴表示式為:
(100+25)/(27-25)+248-201
計算結果為 109.50。 - 【輸入形式】
首先從控制檯輸入一個合法的字尾表示式(長度不超過 200 個字元),其中的運算子、運算數之間都以一個空格分隔。然後在下一行輸入一個整數 1 或 2 表示計算要求(1 表示只輸出計算結果;2 表示輸出對應的中綴表示式和計算結果)。
【輸出形式】
若輸入的計算要求為 1,則只將計算結果輸出到控制檯,小數點後保留兩位;若輸入的計算要求為 2,則先將字尾表示式對應的中綴表示式輸出到控制檯(其中新增的小括號都為英文小括號,表示式中不包含任何空白符),然後在下一行輸出計算結果,小數點後保留兩位。
【樣例 1 輸入】
100 25 + 27 25 - / 248 + 201 -
1
【樣例 1 輸出】
109.50
【樣例 2 輸入】
100 25 + 27 25 - / 248 + 201 -
2
【樣例 2 輸出】
(100+25)/(27-25)+248-201
109.50
【樣例 1 和 2 說明】兩樣例輸入了相同的字尾表示式。按計算要求,樣例 1 只輸出了計算結果;樣例 2 輸出了轉換後的(包含最少括號的)中綴表示式和計算結果。按照字尾表示式的計算語義,當轉換為中綴表示式時,前兩個運算子連成的表示式100+25 和 27-25 都要加上小括號。
【樣例 3 輸入】
100 25 + 2 58 42 + * /
2
【樣例 3 輸出】
(100+25)/(2(58+42))
0.63
【樣例 3 說明】按照字尾表示式的計算語義,生成中綴表達時表示式 2 -
字尾轉中綴演算法提示:
1、每步進行字尾表示式計算時,除了要儲存計算結果外,還應儲存對應的(以字串形式表示的)運算子和中綴表示式。
2、在進行字尾表示式計算時,當前運算子的優先順序大於左運算物件運算子優先順序,則生成對應的中綴表示式時左運算物件對應的中綴表示式應加括號;當前運算子的優先順序大於或等於右運算物件運算子優先順序時,則生成對應的中綴表示式時右運算物件對應的中綴表示式應加括號。其它情況則不用加括號。
以樣例 2 為例,在進行字尾表示式計算時:
• 第一次進行'+'運算時,左運算物件為 100,右為 25,運算結果為 125、運算子為'+'、對應中綴表示式為 100+25;
• 第二次進行'-'運算時,左運算物件為 27,右為 25,運算結果為 2、運算子為'-'、對應中綴表示式為 27-25;
• 第三次進行'/'運算時,左運算物件為 125、對應運算子為'+'、對應中綴表示式為 100+25,由於'/'優先順序高於'+',因此生成對應中綴表示式時 100+25應加括號;右運算物件為 2、對應運算子為'-'、對應中綴表示式為 27-25,由於'/'優先順序高於'-',因此生成對應中綴表示式時 27-25 也應加括號。該步運算結果為 62.5、運算子為'/'、對應中綴表示式為(100+25)/(27-25);
• 以此類推
思路分析
- 首先1情況很好實現,搞一個數據棧,從頭開始讀表示式,因為是字尾表示式,所以可以直接莫伊。即遇到數字就壓入棧,遇到運算子就將棧頂的兩個數字出棧進行運算,運算結果入棧。
-
情況2折磨我良久QAQ注意到演算法提示中的描述,應該意識到這也是一個棧操作,區別在於棧內的元素是表示式,上述描述中左運算物件對應次棧頂元素,右運算物件對應棧頂元素,(單獨的數也看作是一個表示式)每遇到運算子就將棧頂的兩個元素彈出與運算子重新組合,組合結果入棧,這個過程和1是可以同步進行的。
總體規劃有了,我們需要考慮的細節是:
- 怎麼加括號
- 怎麼組合新的表示式
- 加括號其實就是考慮先算誰,先算乘除再算加減肯定沒問題,如果遇到優先順序高一級的就直接加括號,但注意大於或等於右運算物件運算子優先順序
這樣只要判斷表示式的運算子(表示式的運算子需要在每次組合完後更新)的優先順序是否低於當前運算子,低於就加; - 組合新表示式嘛,本蒻蒟笨笨的複製貼上了一遍,
懶得動腦子了
基於這樣的考慮,就可以有這樣的一個表示式棧的結構,struct infix { char item[MAXSIZE*2]; char flag; } ans_stack[MAXSIZE];
更多細節寫註釋裡了~
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXSIZE 205
struct infix
{
char item[MAXSIZE*2];
char flag;
} ans_stack[MAXSIZE];
double numSTACK[MAXSIZE];
int num[MAXSIZE],cnt; /*這個num陣列用來儲存輸入中的順序,
因為在表示式棧中為了簡潔操作用'n'來代替數字,
而後綴表示式轉為中綴的過程中數字順序沒有變化,
所以可以輸出的時候遇到n直接從數學序列中對應輸出*/
char opSTACK[MAXSIZE];
int numTOP,ansTop;
void Init()
{
numTOP = -1;
ansTop = -1;
}
void CAL(char op); //計算
int isLowerThan(char a,char b)//判斷a的優先順序是否小於b
{//判斷優先順序 注意*和/
return ((a == '+' || a == '-') && (b == '*' || b == '/'))||(a=='*'&&b=='/');
}
void addBrackets(char flag, char s[],char c);
int main()
{
Init();
char in[300];
char c;
int i, aim, now = 0, flag = 0;
gets(in);
scanf("%d", &aim);
for (i = 0; in[i]; i++)
{
c = in[i];
if (c >= '0' && c <= '9')
{//讀入數字常用手段
now *= 10, now += c - '0';
flag = 1;
}
else if (c == ' ')
{
if (flag)//但注意如果沒讀入的話now一直是0 都入棧了 會有一些離譜的輸出所以要判斷有沒有讀數字
{ numSTACK[++numTOP] =(double) now;
ans_stack[++ansTop].item[0] = 'n';//用n代替數字
ans_stack[ansTop].flag = '#';//如果是單個數字作為表示式就不用加括號,直接標記
num[cnt++] = now;//作為輸出的對應表
}
now = 0;
flag = 0;
}
else
{
//先給左右運算物件加括號
addBrackets(ans_stack[ansTop - 1].flag, ans_stack[ansTop - 1].item, c);
addBrackets(ans_stack[ansTop].flag, ans_stack[ansTop].item, c);
//然後組合,左 運算子 右連起來
int len = strlen(ans_stack[ansTop - 1].item);
ans_stack[ansTop - 1].item[len] = c;
len++;
for (int j = 0;ans_stack[ansTop].item[j];j++)
{
ans_stack[ansTop - 1].item[len++] = ans_stack[ansTop].item[j];
}
ans_stack[ansTop - 1].item[len] = '\0';
ans_stack[ansTop - 1].flag = c;//重置當前表示式的運算子
memset(ans_stack[ansTop].item, '\0', sizeof(ans_stack[ansTop].item));
//注意出棧一定要清空陣列
ansTop--;
CAL(c);
}
}
cnt = 0;
if (aim == 2)
{
for (int i = 0; ans_stack[0].item[i];i++)
{
if(ans_stack[0].item[i]=='n')
printf("%d", num[cnt++]);
else
printf("%c", ans_stack[0].item[i]);
}
printf("\n");
}
printf("%.2lf", numSTACK[0]);
return 0;
}
void addBrackets(char flag, char s[],char c)
{
if(flag!='#'&&isLowerThan(flag,c))
{
int len = strlen(s);
for (int j = len-1; j >= 0;j--)
s[j + 1] = s[j];
s[0] = '(';
s[len + 1] = ')';
s[len + 2] = '\0';
}
return;
}
void CAL(char op)
{
if (op == '*')
numSTACK[numTOP - 1] = numSTACK[numTOP - 1] * numSTACK[numTOP];
else if (op == '/')
numSTACK[numTOP - 1] = numSTACK[numTOP - 1] / numSTACK[numTOP];
else if (op == '+')
numSTACK[numTOP - 1] = numSTACK[numTOP - 1] + numSTACK[numTOP];
else if (op == '-')
numSTACK[numTOP - 1] = numSTACK[numTOP - 1] - numSTACK[numTOP];
numTOP--;
}