中綴表達式轉後綴表達式的計算思路及代碼實現

摘要:來實驗室已經兩週了,用師兄的話說,基本處於自嗨狀態。張老師問C++學的怎麼樣了?能不能編一個簡單的計算器出來?我想這個計算器確定不能簡單到只算加減乘除,因而想來想去想寫一個能處理括號的計算器,最好能寫個簡單的UI出來。在思考過程當中發現,這個計算器的難點就是如何把中綴表達式轉換爲後綴表達式,以及如何計算後綴表達式。ios

計算思路

人的思路

若是隻是用於解題的話,這種方法是最快最準確的。可是它不適用於計算機。下面以a+b*c+(d*e+f)*g爲例子講如下人應該怎麼把中綴表達式轉換成後綴表達式。c++

  1. 按先加減後乘除的原則給表達式加括號小程序

    結果:((a+(b*c))+(((d*e)+f)*g))函數

  2. 由內到外把每一個括號裏的表達式換成後綴post

    最終結果:abc*+de*f+g*+測試

這樣就獲得了中綴表達式轉後綴表達式的最終結果。此法應付考試有神效。ui

計算機的思路

畢竟計算機跟人不同,它「笨」啊!人的思路它用不了。那麼它該怎麼把中綴表達式轉換成後綴表達式呢?spa

計算機的思路須要用到,先來明確中綴表達式轉後綴表達式的規則:code

1)若是遇到操做數,咱們就直接將其輸出。cdn

2)若是遇到操做符,則咱們將其放入到棧中,遇到左括號時咱們也將其放入棧中。

3)若是遇到一個右括號,則將棧元素彈出,將彈出的操做符輸出直到遇到左括號爲止。注意,左括號只彈出並不輸出。

4)若是遇到任何其餘的操做符,如**(「+」, 「*」,「(」)**等,從棧中彈出元素直到遇到發現更低優先級的元素(或者棧爲空)爲止。彈出完這些元素後,纔將遇到的操做符壓入到棧中。有一點須要注意,只有在遇到" ) "的狀況下咱們才彈出" ( ",其餘狀況咱們都不會彈出" ( "。

5)若是咱們讀到了輸入的末尾,則將棧中全部元素依次彈出。

下面以a+b*c+(d*e+f)*g爲例子來說講計算機的轉換過程。下面在描述棧的狀況是直接用文字描述了,由左到右爲棧底到棧頂表示棧空

  1. 由左向右遍歷表達式,首先遇到a,直接將其輸出。

    此時輸出爲:a

    棧的狀況爲:空

  2. 繼續遍歷,遇到+,將其放入棧中。

    此時輸出爲:a

    棧的狀況爲:+

  3. 繼續遍歷,遇到b,直接將其輸出。

    此時輸出爲:ab

    棧的狀況爲:+

  4. 繼續遍歷,遇到*,由於*的優先級大於棧頂的+,因此將*放入棧內。

    此時輸出爲:ab

    棧的狀況爲:+*

  5. 繼續遍歷,遇到c,直接將其輸出。

    此時輸出爲:abc

    棧的狀況爲:+*

  6. 繼續遍歷,遇到+,由於+的優先級低於棧頂的*,故將*彈出;而後新的棧頂元素的+與這個+優先級相同,故也要彈出如今棧頂的+;而後棧空了,將如今這個+放入棧中。

    此時輸出爲:abc*+

    棧的狀況爲:+

  7. 繼續遍歷,遇到(,直接將其放入棧中,不遇到)不會將(彈出。

    此時輸出爲:abc*+

    棧的狀況爲:+(

  8. 繼續遍歷,遇到d,直接將其輸出。

    此時輸出爲:abc*+d

    棧的狀況爲:+(

  9. 繼續遍歷,遇到*,由於棧頂爲(,不遇到)不將(彈出,故直接將*放入棧中。

    此時輸出爲:abc*+d

    棧的狀況爲:+(*

  10. 繼續遍歷,遇到e,直接將其輸出。

    此時輸出爲:abc*+de

    棧的狀況爲:+(*

  11. 繼續遍歷,遇到+,由於+比棧頂*的優先級低,故將*彈出;新的棧頂元素爲(,不遇到)不彈出(,故將+放入棧中。

    此時輸出爲:abc*+de*

    棧的狀況爲:+(+

  12. 繼續遍歷,遇到f,直接將其輸出。

    此時輸出爲:abc*+de*f

    棧的狀況爲:+(+

  13. 繼續遍歷,遇到),直接將棧中元素依次彈出並輸出直到遇到(爲止,注意:(彈出但不輸出

    此時輸出爲:abc*+de*f+

    棧的狀況爲:+

  14. 繼續遍歷,遇到*,由於*的優先級大於棧頂元素+的優先級,故直接將*入棧。

    此時輸出爲:abc*+de*f+

    棧的狀況爲:+*

  15. 繼續遍歷,遇到g,直接將其輸出。

    此時輸出爲:abc*+de*f+g

    棧的狀況爲:+*

  16. 繼續遍歷,爲空,遍歷結束。將棧內元素依次彈出。

    此時輸出爲:abc*+de*f+g*+

    棧的狀況爲:空

至此,中綴表達式轉後綴已經所有完成,結果爲abc*+de*f+g*+

代碼實現

源代碼

代碼是用C++寫的,不過仍是用的面向過程的思路。代碼以下:

//中綴表達式轉後綴

#include<iostream>
#include<string>
#include<stack>

using namespace std;

int prio(char op) {                 //給運算符優先級排序
	int priority;
	if (op == '*' || op == '/')
		priority = 2;
	if (op == '+' || op == '-')
		priority = 1;
	if (op == '(')
		priority = 0;
	return priority;
}
bool Trans(string &str,string &str1) {   //引用傳遞
	stack<char> s;                   //定義一個char類型的棧s
	int i;
	for (i = 0; i<str.size(); i++) {
		if (str[i] >= '0' && str[i] <= '9') {    //若是是數字,直接入棧
			str1+=str[i];
		}
		else {                        //不然不是數字
			if (s.empty())            //棧空則入站
				s.push(str[i]);
			else if (str[i] == '(')   //左括號入棧
				s.push(str[i]);
			else if (str[i] == ')') {  //若是是右括號,只要棧頂不是左括號,就彈出並輸出
				while (s.top() != '(') {  
					str1+= s.top();
					s.pop();
				}
				s.pop();                 //彈出左括號,但不輸出
			}
			else {
				while (prio(str[i]) <= prio(s.top())) { //棧頂優先級大於等於當前運算符,則輸出
					str1+= s.top();
					s.pop();
					if (s.empty())      //棧爲空,中止
						break;
				}
				s.push(str[i]);   //把當前運算符入棧
			}
		}
	}
	while (!s.empty()) {      //最後,若是棧不空,則彈出全部元素並輸出
		str1+= s.top();
		s.pop();
	}
	return true;
}
int main() {                //主程序
	string infix;
	string postfix;
	cout << "請輸入中綴表達式:" << infix << endl;
	cin >> infix;
	Trans(infix,postfix);
	cout << "後綴表達式爲:" << postfix << endl;
	return 1;
}
複製代碼

程序測試

這裏咱們就用a+b*c+(d*e+f)*g的實例1+2*3+(4*5+6)*7來測試咱們的程序,可見運行結果爲123*+45*6+7*+,計算正確。

總結

在寫這段小程序時有一些不小的收穫。畢竟是一個初學者,犯的錯誤都是很低級的知識性錯誤,理解不到位,基礎不紮實。這裏要感謝個人本科同窗白洋耐心地教我。

1)string類的對象在沒有初始化大小的狀況下不要用其下標運算,會出現string subscript out of range(下標越界)的錯誤。在第三次執行循環時出現,大概是由於string對象的默認大小爲2個字節。

2)string類的對象不以'\0'結尾,要與C語言區分開來,判斷其結束時用size()函數。size() 函數返回的是其大小,而後下標是以0開始的,因此最大到size-1。

3)函數調用進行形實結合時要用引用傳遞。

4)#include<stack>的使用。

相關文章
相關標籤/搜索