中綴表達式轉換為前綴表達式
在《前綴表達式的計算》中,我們討論了對前綴表達式如何計算:設置一個操作數棧,對前綴表達式從右到左掃描,遇到操作數直接入棧,遇到操作符則從操作數棧彈棧,先彈left值后彈right值,根據操作符進行相應的計算,並將計算結果壓入到操作數棧中,最終將整個前綴表達式掃面完畢。這時操作數棧中只有一個元素,該元素的值即為前綴表達式的值。
在《中綴表達式轉換為后綴表達式》中,我們討論了如何將一個中綴表達式轉換為其對應的后綴表達式。其思想為:設置一個操作符棧,如果遇到操作數,則直接將操作數放進后綴表達式中,如果遇到非操作數,則:如果是左括號,則將左括號入棧;如果是右括號,則從操作符棧中將操作符彈棧,放入后綴表達式中,直至棧空或遇到棧中的左括號,並將左括號彈棧;如果是其他操作符,則比較其優先級與棧中操作符優先級情況,如果棧中的操作符的優先級大於等於當前操作符,則將棧中操作符彈棧,直至棧空,或棧中操作符優先級小於當前操作符的優先級,將當前操作符壓棧。當從左到右順序掃描完整個中綴表達式后,檢測操作符棧,如果非空,則依次彈棧,將彈出的操作符依次壓入到后綴表達式中。最終,得到中綴表達式對應的后綴表達式。如果還想計算后綴表達式的值,則可以參考《后綴表達式的計算》。
本文我們是討論如何將中綴表達式轉換為前綴表達式。
我們先給出中綴表達式轉換前綴表達式的程序,然后再對程序進行相關講解,之后在與中綴表達式轉換后綴表達式的過程進行比較,分析其中的差異存在於哪里。
// 中綴表達式轉換為前綴表達式 #include <iostream> #include <vector> #include <string> #include <sstream> #include <map> #include <stack> #include <algorithm> using namespace std; void GetInfix(vector<string>& infix) { infix.clear(); string line; getline(cin, line); istringstream sin(line); string tmp; while (sin >> tmp) { infix.push_back(tmp); } } // 初始化操作符 void InitOperators(map<string, int>& opers) { opers.clear(); opers["("] = 100; opers[")"] = 900; opers["+"] = 100; opers["-"] = 100; opers["*"] = 200; opers["/"] = 200; } bool IsOperator(const string& op, const map<string, int>& opers) { auto cit = opers.find(op); if (cit != opers.end()) { return true; } else { return false; } } void InfixToPrefix(const vector<string>& infix, vector<string>& prefix, map<string, int>& opers) { prefix.clear(); stack<string> stk; // 操作符棧 for (int i = infix.size() - 1; i >= 0; --i) // 從右到左掃描 { if (!IsOperator(infix[i], opers)) // 如果是操作數 { prefix.push_back(infix[i]); } else // 如果是操作符 { if (infix[i] == ")") // 如果是右括號,則直接入棧 { stk.push(infix[i]); } else if (infix[i] == "(") // 如果是左括號 { // 依次彈出棧中的操作符,直至遇到右括號 while (!stk.empty()) { if (stk.top() == ")") { stk.pop(); break; } else { prefix.push_back(stk.top()); stk.pop(); } } } else // 如果是其他操作符 { while (!stk.empty() && stk.top() != ")" && opers[stk.top()] > opers[infix[i]]) // 棧頂操作符優先級大於當前操作符優先級 { prefix.push_back(stk.top()); stk.pop(); } // 將當前操作符入棧 stk.push(infix[i]); } } } // 檢測操作符棧是否為空 while (!stk.empty()) { prefix.push_back(stk.top()); stk.pop(); } // 將prefix翻轉 reverse(prefix.begin(), prefix.end()); return; } void Display(const vector<string>& fix) { for (auto i = 0; i != fix.size(); ++i) { cout << fix[i] << ' '; } cout << endl; } int main() { map<string, int> opers; InitOperators(opers); while (true) { vector<string> infix, prefix; GetInfix(infix); Display(infix); InfixToPrefix(infix, prefix, opers); Display(prefix); cout << endl; } return 0; }
首先說明的是,我們的中綴表達式輸入是用空白符間隔的,而沒有對中綴表達式進行詞法分析,對中綴表達式的詞法分析可以參考《四則運算的詞法分析》。
我們首先實現了中綴表達式的輸入、操作符及其優先級的初始化、判斷是否為操作符。然后重點在中綴表達式轉換為前綴表達式的函數:InfixToPrefix。
中綴表達式轉換前綴表達式的操作過程為:
首先設定一個操作符棧,從右到左順序掃描整個中綴表達式,如果是操作數,則直接歸入前綴表達式;如果是操作符,則檢測器是否是右括號,如果是右括號,則直接將其入棧;如果是左括號,則將棧中的操作符依次彈棧,歸入前綴表達式,直至遇到右括號,將右括號彈棧,處理結束;如果是其他操作符,則檢測棧頂操作符的優先級與當前操作符的優先級關系,如果棧頂操作符優先級大於當前操作符的優先級,則彈棧,並歸入前綴表達式,直至棧頂操作符優先級小於等於當前操作符優先級,這時將當前操作符壓棧。
當掃描完畢整個中綴表達式后,檢測操作符棧是否為空,如果不為空,則依次將棧中操作符彈棧,歸入前綴表達式。最后,將前綴表達式翻轉,得到中綴表達式對應的前綴表達式。
下面,我們結合中綴表達式轉后綴表達式的過程,比較中綴轉前綴與中綴轉后綴的聯系和區別。
|
中綴轉前綴 |
中綴轉后綴 |
棧 |
操作符棧 |
操作符棧 |
掃描順序 |
從右到左 |
從左到右 |
遇到操作數 |
直接歸入 |
直接歸入 |
遇到右括號 |
直接入棧 |
將棧中操作符依次彈棧,歸入,直至遇到左括號,將左括號彈棧,處理完畢 |
遇到左括號 |
將棧中操作符依次彈棧,歸入,直至遇到右括號,將右括號彈棧,處理完畢 |
直接入棧 |
遇到其他操作符 |
檢測棧頂操作符優先級與當前操作符優先級關系,如果棧頂大於當前,則出棧,歸入,直至棧頂小於等於當前,並將當前操作符入棧 |
檢測棧頂與當前優先級關系,如果棧頂大於等於當前則出棧,歸入,直至棧頂小於當前,並將當前操作符入棧 |
操作符棧中的優先級 |
從棧底到棧頂操作優先級:非遞減。即:棧頂可以大於或等於下面的 |
從棧底到棧頂優先級:遞增。即:棧頂必須大於下面的 |
是否翻轉 |
翻轉 |
無需翻轉 |
通過上表,我們可以看出中綴轉前綴與中綴轉后綴的最大區別在於兩點:掃描順序和操作符棧中操作符優先級的排列關系。
總結
以上我們主要討論了中綴表達式轉換前綴表達式的過程,並與中綴表達式轉換后綴表達式進行了比較,找出其中的差異點。通過本文,將中綴表達式轉換為前綴表達式,以及之前有《前綴表達式的計算》,這樣我們可以通過前綴表達式,計算中綴表達式的值了。即先將中綴表達式轉換為前綴表達式,然后對前綴表達式進行計算,計算結果即為中綴表達式的值。
下一步,我們將討論如何將前綴表達式轉換中綴表達式,如何將后綴表達式轉換為中綴表達式,以及前綴表達式和后綴表達式之間的直接相互轉換。