使用栈解决的一类经典问题:表达式转换及求值;中缀表达式;前缀表达式,后缀表达式,中缀转前缀;中缀转后缀;后缀表达式求值;波兰式,逆波兰式
文章目錄
- 背景知識
- 表達式轉換問題(考研經典)
- 一:手工轉換
- (1)中綴轉前綴和中綴轉后綴
- (2)前綴轉中綴和后綴轉中綴
- 二:用棧實現表達式轉換
- (1)中綴轉后綴
- (2)中綴轉前綴
- 表達式計算問題(使用棧實現)
背景知識
中綴表達式:a+b
前綴表達式(波蘭式):+ab
后綴表達式(逆波蘭式):ab+
表達式轉換問題(考研經典)
一:手工轉換
考研中有一類經典的問題就是表達式的轉換問題,經常要把一個類型的表達式轉換為另一個類型的表達式
所以這里我們主要說一下中綴轉前綴,前綴轉中綴,中綴轉后綴和后綴轉中綴即可。這樣這三種形式任意兩個之間的轉換都可以借助上面四種轉換直接或間接完成
所用表達式的三種形式如下
(1)中綴轉前綴和中綴轉后綴
首先每遇到兩個操作數,一個運算符就把他們用括號扣起來(用括號括起來的可以看做一個操作數)
如下
把上面的結果全部抄一遍(對應位置,不要錯亂),抄的時候不要抄操作數,把操作數的位置空出來。然后依據原來的結果,把運算符移出到括號前面面,注意只需要移動到該運算符原來所在的括號前面就可以了,不要一次移動多層
如下
轉化為后綴表達式時,只需要移動到括號后面就可以了,由于非常簡單,這里我就不演示了,結果在最上面
(2)前綴轉中綴和后綴轉中綴
前綴轉中綴時,從后向前掃描前綴表達式,每遇到兩個操作數一個運算符就把運算符放到兩個操作數中間,注意有的時候可能會連續遇到三個及以上的操作數,這時就不要管后面的操作數了,你只需要向前找,一直找到運算符然后結合后面的兩個操作數組合在一起
如下,其中紅色部分表示待處理的運算符和操作數
對于后綴轉中綴,和前綴轉中綴實則是一樣的,只不過后綴轉中綴的時候需要從前向后掃描,由于也比較簡單這里就不演示了
二:用棧實現表達式轉換
表達式轉換是棧的應用的一個非常典型的例子,主要利用的就是棧的先進后出的特性,代碼的邏輯不復雜,但是就是比較多一點
這里用棧實現表達式轉換主要指的是中綴轉前綴或者后綴,后其他形式的表達式的轉換沒有具體的意義,因為轉為前綴和后綴的目的就是讓計算機進行求值,所以這里主要講的就是中綴轉換為前綴或后綴的代碼。由于C語言中處理棧比較麻煩,所以這里采用C++,省去一些造輪子的步驟
(1)中綴轉后綴
中綴式轉后綴式需要兩個棧完成,然后從后向前掃描中綴表達式,先不討論怎樣入棧,記住:所有的操作數入S1棧,所有的括號和運算符入S2棧
如下,首先由于S2空,此時掃描到了左括號,遇到左括號直接入棧
接著此時掃描到了操作數a,直接入S1,所有操作數直接入S1
接著此時掃描到了“+”號,此時S2棧頂元素為左括號,任何運算符遇到左括號直接入棧
繼續進行,掃描到b直接入S1。然后此時掃描到了右括號,當掃描到右括號時將S2中從棧頂元素到左括號之間的元素依次出S2,入S1,但是左括號忽略掉,也就是不要了
接著掃描到“*”,入S2,然后掃描到操作數“c”,入S1。此時掃描到“+”,由于掃描到的元素符優先級小于等于S2棧頂運算符優先級,故將棧頂運算符出棧入S1,然后接著再拿掃描到的運算符與新的棧頂元素比較,如果還是小于等于重復上述步驟,如果是大于則直接入棧,這里由于“*”出棧后,S2為空,故直接入“+”
接著再入操作數d。此時來到運算符-,由于掃描到的運算符的優先級小于等于棧頂元素優先級,故重復
剩余部分就不再演示了,此時大家觀看S1棧,是不是已經有了后綴式的雛形了呢
代碼如下
#include <iostream> #include <stack> #include <string> using namespace std;int getpriority(char c) {if (c == '+' || c == '-'){return -1;//加減優先級小}else{return 1;//乘除優先級大} }void convert(string& express, stack<char>& s1, stack<char>& s2) {int i = 0;while (express[i] != '\0')//掃描中綴表達式{if ('0' <= express[i] && express[i] >= '9')//如果掃描到了操作數,直接入s1{s1.push(express[i++]);}else if (express[i] == '(')//如果掃描到了左括號,直接入s2{s2.push(express[i++]);}else if (express[i] == '+' || express[i] == '-' || express[i] == '*' || express[i] == '/')//掃描到運算符進行優先級判斷{if (s2.empty() || s2.top() == '(' || getpriority(express[i]) > getpriority(s2.top()))//如果此時S2為空或者棧頂元素為左括號,或者掃描到的運算符優先級大于棧頂運算符優先級,則S2{s2.push(express[i++]);}else//反之優先級如果是小于等于的話,那么就要把運算符出棧然后入S1{char temp = s2.top();s2.pop();s1.push(temp);}}else if (express[i] == ')')//最后一種情況就是掃描到了右括號,那么就把S2從棧頂到左括號的元素依次出棧入棧{while (s2.top() != '('){char temp = s2.top();s2.pop();s1.push(temp);}//注意最后停止循環的時候S2的棧頂元素是左括號,但是不要把左括號入棧,所以直接丟掉左括號s2.pop();i++;//不要忘記后移}}while (!(s2.empty()))//如果S2沒有空,那么依次出S2,入S1{char temp = s2.top();s2.pop();s1.push(temp);}}int main() {stack<char> s1;//結果棧,入操作數stack<char> s2;//輔助棧,入運算符和括號stack<char> result;//輸出用string expression("(a+b)*c+d-(e+g)*h");cout << "轉換前為中綴式:" << expression << endl;convert(expression, s1, s2);cout << "轉換為中綴式:";while (!(s1.empty())){char temp = s1.top();s1.pop();result.push(temp);}while (!(result.empty())){cout << result.top();result.pop();}}(2)中綴轉前綴
中綴轉前綴基本和中綴轉后綴一致,不同點如下
- 掃描時從后向前掃描中綴式
- 遇到右括號直接入棧,遇到左括號進行出棧
- 掃描到的運算符優先級如果大于等于棧頂運算符優先級,直接入棧
表達式計算問題(使用棧實現)
我們轉換表達式的目的就在于讓計算機實現表達式的求值,或者說,計算機是不認識也不清楚中綴式的含義的,只有人能讀懂,但是我們轉換為前綴或者后綴后卻可以借助棧的性質來完成計算
這里我們就以LeetCode 150:逆波蘭式求值,也就是后綴表達式求值為例,前綴式基本一致
求解時,準備一個棧,從左向右掃描后綴表達式,遇到操作數就入棧,遇到運算符則出棧兩個操作數,先出棧的作為右操作數,后出棧的作為左操作數,然后運算之后把結果繼續壓入棧內,重復以上步驟,最后棧中的棧頂元素就是結果
前綴表達式和這個基本一致,不同點就是要從后向前掃描,然后先出棧的是左操作數,后出棧的是右操作數
總結
以上是生活随笔為你收集整理的使用栈解决的一类经典问题:表达式转换及求值;中缀表达式;前缀表达式,后缀表达式,中缀转前缀;中缀转后缀;后缀表达式求值;波兰式,逆波兰式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于自己
- 下一篇: iOS开发 贝塞尔曲线UIBezierP