堆棧-中綴表達式求值-分析並附完整源代碼

中綴表達式,顧名思義,可以理解成操作符夾在操作數中間的表達式,也是人類廣泛使用的表達式形式,因爲簡單,粗暴,通俗,易懂,然而...電腦並不懂怎麼處理這種算式啊,(電腦:臣妾做不到啊),因爲中綴表達式中的運算符具有優先級,任何一個上過小學二年級的人都不該把1+2*3算成9,因爲都知道乘除號的優先級是比加減號高的,然後還有括號裏的表達式最先計算的規則,所以括號在某些情況下優先級比乘除號還高,比如1*(2+3),這裏雖然乘號比加號優先級高,但是因爲括號神隊友給加號加了個buff,所以加號獲得了強化...先參與計算了。

那麼,如何解決表達式裏運算符的優先級呢,查表是一個比較容易想到的思路,建立一個二維數組,每個符號分別對應一個角標,然後二維數組中的元素(例如是int型),那可以把1記爲操作符a>b,0記爲相等,-1記爲a<b。但這裏給出另外一種方案,用函數來判斷,兩種方案都比較容易實現。

因爲計算中綴表達式的值要使用到先入後出的數據結構,所以需要用到堆棧,堆棧可以自己構造一個簡單的數據結構,也可以使用C++的STL庫。

然後簡要分析一下,理一理思路:

如果給出了算式3+5,從頭開始掃描,掃描到第一個字符,3,判斷是數字,當前操作符棧爲空,無法操作,所以只能進入操作數棧,然後繼續向後掃描,這次掃描到的字符是+,是操作符,但是操作數棧只有一個,也沒法操作,扔進操作符棧,繼續掃描,掃描到了5,是數字,扔進操作數棧,同時發現已經沒有下一個元素了,表達式已經掃描到了句尾,所以需要計算,這時取出操作數棧裏的兩個元素,取出操作符棧裏的+號,計算出的值存進操作數棧,判斷髮現已經讀到句尾,返回操作數棧頂元素。

現在難度加大,如果給出,3+5*1,從頭開始掃描,直到掃描到5,和上述操作都相同,但是掃描到5的時候讀下一個字符不是空白,即不是句尾,所以不能急於計算3+5,掃描到乘號的時候,需要看一下操作符棧頂元素優先級,是加號,比現在的操作符低,拋開代碼,按照常識,3+5*這個算式肯定不能算的,必須還要看下一個數是什麼,所以這裏的操作應當是把乘號入棧,而不是取出加號計算3+5。

if (i < j)//棧外操作符優先級高
{
	OPERATOR.push(temp);//操作符入棧
}
繼續向下掃描,掃到了數字1,加入操作數,而且讀到了句尾,這時乘號兩邊都有數了,一本滿足,所以取出操作數棧最頂上的兩個元素1和5,取出操作符棧頂的*,計算1*5得到了5,加入堆棧。

1  
5 *
3 +
現在堆棧情況變成了:

5  
3 +
再取出操作數棧頂的兩個元素,取出操作符棧頂的+號,計算3+5,得到8,放進操作數棧。

8 ^
(^表示空)
這時操作符棧空,表達式讀取完畢,計算完成,所以返回操作數棧頂元素,即8。

繼續加大難度呢?

給出3*(5+1),掃描到3,進棧

3 ^
掃描到*

3 *
掃描到(,左括號在沒有進棧時,擁有最高優先級,按照常識,看到一個左括號,肯定說明這是一個要先計算的片段,但是左括號在進入操作符棧後優先級會被降到最低,此時左括號沒有進棧,擁有最高優先級,大於*號,所以進棧,且左括號不參與計算。

  (
3 *
掃描到5,進棧。

5 (
3 *
掃描到+,因爲此時左括號已經在棧中,所以優先級最低,加號優先級大於左括號,所以進棧。

  +
5 (
3 *
掃描到1,進棧。

1 +
5 (
3 *
掃描到),且讀到句尾,注意,在沒有進棧時,右括號優先級最低,按照先前的規則,噹噹前的操作符優先級低於棧頂操作符時,應該取出棧頂操作符,所以取出+號,取出操作數棧頂兩元素1和5,計算1+5=6(以下代碼片段開頭else接上代碼片段if)

else
{
	OPERATOR.pop();
	if (temp2 != '('&&i == j || i > j)
	{
		ElemTp a, b;
		b = NUM.top(); NUM.pop();
		a = NUM.top(); NUM.pop();
		NUM.push(Calculate(a, temp2, b));//計算值併入棧
		continue;
	}
}


把6放入操作數棧,變爲

6 (
3 *
因爲左括號不參與計算,所以直接退棧,取出*號,取出6和3,計算6*3=18,放入操作數棧,得到

18 ^
此時操作符棧空,返回操作數棧頂元素18,即爲該表達式結果。

根據上面的簡單的例子,我們能夠得到一些規律,左括號在沒有進棧時優先級最高,乘除號其次,加減號再其次,右括號最低,棧頂操作符則是左括號優先級最低,加減號高一級,乘除號再高一級,右括號最高,代表讀取到了一對配對括號,應當計算括號內部表達式的值,所以可以寫出。

int Operator_Top(char ch)//給出棧頂操作符優先級
{
	if (ch == ')')	return 4;
	else if (ch == '*' || ch == '/')	return 3;
	else if (ch == '+' || ch == '-')	return 2;
	else if (ch == '(')	return 1;
	else return 0;
}
int Operator_Now(char ch)//給出當前掃描到的操作符優先級
{
	if (ch == ')')	return 1;
	else if (ch == '*' || ch == '/')	return 3;
	else if (ch == '+' || ch == '-')	return 2;
	else if (ch == '(')	return 4;
	else return 0;
}
求值函數:ElemTp爲宏定義,可修改數據類型

ElemTp Calculate(ElemTp a, char ch, ElemTp b)//計算棧頂兩操作數對應雙目運算符的值
{
	switch (ch)
	{
	case '+':
		return a + b; break;
	case '-':
		return a - b; break;
	case '*':
		return a * b; break;
	case '/':
		return a / b; break;
	default:
		cout << "操作符非法" << endl;
		break;
	}
}
以下爲源代碼(注:該代碼並沒有處理異常功能,必須保證輸入中綴表達式合法且必須以#結尾,#表示句尾,方便判斷)
#include<iostream>
#include<stack>
#include<cstring>
using namespace std;
#define ElemTp double
stack <ElemTp> NUM;//操作數棧
stack <char> OPERATOR;//操作符棧
bool IsOperator(char ch)//判斷是否是操作符
{
	return (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(' || ch == ')' || ch == '#');
}
ElemTp Calculate(ElemTp a, char ch, ElemTp b)//計算棧頂兩操作數對應雙目運算符的值
{
	switch (ch)
	{
	case '+':
		return a + b; break;
	case '-':
		return a - b; break;
	case '*':
		return a * b; break;
	case '/':
		return a / b; break;
	default:
		cout << "操作符非法" << endl;
		break;
	}
}
int Operator_Top(char ch)//給出棧頂操作符優先級
{
	if (ch == ')')	return 4;
	else if (ch == '*' || ch == '/')	return 3;
	else if (ch == '+' || ch == '-')	return 2;
	else if (ch == '(')	return 1;
	else return 0;
}
int Operator_Now(char ch)//給出當前掃描到的操作符優先級
{
	if (ch == ')')	return 1;
	else if (ch == '*' || ch == '/')	return 3;
	else if (ch == '+' || ch == '-')	return 2;
	else if (ch == '(')	return 4;
	else return 0;
}
ElemTp Value_Calculate(char str[100])//計算處理後的表達式值
{
	while (!NUM.empty())	NUM.pop();//每次計算前清空操作數棧
	while (!OPERATOR.empty())   OPERATOR.pop();//每次計算前清空操作符棧
	OPERATOR.push('#');
	char temp = *str++;
	while (temp != '#' || OPERATOR.top() != '#')
	{
		if (!IsOperator(temp))
		{
			NUM.push(temp - '0');//是數字
		}
		else
		{
			char temp2 = OPERATOR.top();//獲取操作符棧頂元素
			int i = Operator_Top(temp2), j = Operator_Now(temp);//獲取兩操作符優先級
			if (i < j)//棧外操作符優先級高
			{
				OPERATOR.push(temp);//操作符入棧
			}
			else
			{
				OPERATOR.pop();
				if (temp2 != '('&&i == j || i > j)
				{
					ElemTp a, b;
					b = NUM.top(); NUM.pop();
					a = NUM.top(); NUM.pop();
					NUM.push(Calculate(a, temp2, b));//計算值併入棧
					continue;
				}
			}
		}
		temp = *str++;
	}
	return NUM.top();//計算完成後操作數棧應當只剩一個元素,返回棧頂元素
}
int main()
{
    char str[100];
    while(cin >> str && strcmp(str,"0")!=0)
    {
        cout << Value_Calculate(str) << endl;
    }
    return 0;
}















發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章