1. 程式人生 > 其它 >【BUAA 18級資料結構第二題】字尾表示式轉中綴表示式&表示式計算

【BUAA 18級資料結構第二題】字尾表示式轉中綴表示式&表示式計算

題面

  • 【問題描述】
    從控制檯輸入一合法的字尾表示式,其中的運算子只包括+、-、*、/,運算數都是大於等於 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
    (58+42)外應該加上小括號,否則兩個表示式計算順序不一致。
  • 字尾轉中綴演算法提示
    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是可以同步進行的。

總體規劃有了,我們需要考慮的細節是:

  1. 怎麼加括號
  2. 怎麼組合新的表示式
  • 加括號其實就是考慮先算誰,先算乘除再算加減肯定沒問題,如果遇到優先順序高一級的就直接加括號,但注意大於或等於右運算物件運算子優先順序
    這一句中的等於,就是在問你從左往右算的時候先算誰(給誰加括號),優先順序相等的情況有:--、++、+-、**、//、*/ ,如果是前5種,那麼運算順序沒有影響,所以不用加括號,而最後一種 5*6/3和(5*6)/3結果是不一樣的,所以為了方便我們可以認為*的優先順序低於/;
    這樣只要判斷表示式的運算子(表示式的運算子需要在每次組合完後更新)的優先順序是否低於當前運算子,低於就加;
  • 組合新表示式嘛,本蒻蒟笨笨的複製貼上了一遍,懶得動腦子了
    基於這樣的考慮,就可以有這樣的一個表示式棧的結構,
    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--;
}