棧的應用——計算器專題

​ 棧的現實應用很多,這裏記錄下用棧解決四則運算表達式求值的相關題目。

​​ 平常見到的公式如“7+5*4”,稱作中綴表達式,因爲所有的運算符號都在兩數字的中間,而對於計算機來說,中綴表達式是無法直接解析的。針對這個問題,20世紀50年代,波蘭邏輯學家 Jan ukasiewicz,創造了一種不需要括號的後綴表達法,稱爲逆波蘭( Reverse Polish Notation,RPN)表示。這種後綴表示法,是表達式的一種新的顯示方式,非常巧妙地解決了程序實現四則運算的難題。

​​ 以“9+(3-1)×3+10÷2”爲例,如果要用後綴表示法應該是2:“9 3 1-3 * + 1 0 2 / +”,這樣的表達式稱爲後綴表達式,叫後綴的原因在於所有的符號都是在要運算數字的後面出現。這種表達不適宜人去使用,但是卻很適合計算機。

一、中綴表達式轉後綴表達式

以上式爲例,中綴表達式“9+(3-1)×3+10÷2”轉化爲後綴表達式“9 3 1 - 3 * + 1 0 2 / +"。

  1. 初始化一空棧,用來對符號進出棧使用。

  2. 第一個字符是數字9,輸出9,後面是符號“+",進棧。步驟1 2如下圖所示

image-20200617132926912

  1. 第三個字符是“(”,依然是符號,因其只是左括號,還未配對,故進棧。。

  2. 第四個字符是數字3,輸出,總表達式爲93,接着是“-”,進棧。步驟 3 4如下圖所示。

image-20200617133023824

  1. 接下來是數字1,輸出,總表達式爲931,後面是符號“)”,此時,我們需要去匹配此前的“(",所以棧頂依次出棧,並輸出,直到“(”出棧爲止。此時左括號上方只有“一”,因此輸出“-”。總的輸出表達式爲931-。。

  2. 接着是數字3,輸出,總的表達式爲931-3.緊接着是符號“×”,因爲此時的棧頂符號爲“+”號,優先級低於“×”,因此不輸出,“*”進棧。步驟5 6如下圖所示。

image-20200617133108419

  1. 7.之後是符號“+",此時當前棧頂元素"""*"比這個“+”的優先級高,因此棧中元素出棧並輸出(沒有比“+”號更低的優先級,所以全部出棧),總輸出表達式爲"9313+""9\quad3\quad 1 - 3 * +"。然後將當前這個符號“+”進棧。也就是說,前6張圖的棧底的“+”是指中綴表達式中開頭的9後面那個“+",左圖中的棧底(也是棧頂)的“+”是指“9+(3-1)×3+”中的最後一個“+

  2. 緊接着數字10,輸出,總表達式變爲931-3*+10.後是符號“÷”,所以“/”進棧。步驟7 8如下圖所示。

image-20200617133552117

  1. 最後一個數字2,輸出,總的表達式爲9 3 1 - 3 * + 1 0 2.如圖49-10的左圖所示。*

  2. 因已經到最後,所以將棧中符號全部出棧並輸出。最終輸出的後綴表達式結果爲9 3 1 - 3 * + 1 0 2 / +。步驟9 10如下圖所示。

image-20200617133552117

​  要想讓計算機具有處理我們通常的標準(中綴)表達式的能力,最重要的就是兩步:

​ ​ 1.將中綴表達式轉化爲後綴表達式(棧用來進出運算的符號)。
​ ​ 2.將後綴表達式進行運算得出結果(棧用來進出運算的數字)。

二、後綴表達式計算

以上式爲例,後綴表達式計算的流程如下:

  1. 初始化一個空棧。此棧用來對要運算的數字進出使用。
  2. 後綴表達式中前三個都是數字,所以9、3、1進棧,如下圖所示

image-20200617132048232
3. 接下來是“-”,所以將棧中的1出棧作爲減數,3出棧作爲被減數,並運算3-1得到2,再將2進棧,
4. 接着是數字3進棧,步驟3 4如下圖所示。

image-20200617132220142
5. 後面是“*”,也就意味着棧中3和2出棧,2與3相乘,得到6,並將6進棧。
6. 下面是“+”,所以棧中6和9出棧,9與6相加,得到15,將15進棧,步驟 5 6如下圖所示。

image-20200617132312008
7. 接着是10與2兩數字進棧。
8. 接下來是符號“/”,因此,棧頂的2與10出棧,10與2相除,得到5,將5進棧,步驟7 8如下圖所示。

image-20200617132424135
9. 最後一個是符號“+",所以15與5出棧並相加,得到20,將20進棧,
10. 結果是20出棧,棧變爲空,步驟9 10 如下圖所示。
image-20200617132514751

三、示例(後續會更新)

1. poj 2694 逆波蘭表達式(遞歸)

描述

逆波蘭表達式是一種把運算符前置的算術表達式,例如普通的表達式2 + 3的逆波蘭表示法爲+ 2 3。逆波蘭表達式的優點是運算符之間不必有優先級關係,也不必用括號改變運算次序,例如(2 + 3) * 4的逆波蘭表示法爲* + 2 3 4。本題求解逆波蘭表達式的值,其中運算符包括+ - * /四個。

輸入

輸入爲一行,其中運算符和運算數之間都用空格分隔,運算數是浮點數。

輸出

輸出爲一行,表達式的值。
可直接用printf("%f\n", v)輸出表達式的值v。

樣例輸入

* + 11.0 12.0 + 24.0 35.0

樣例輸出

1357.000000

提示

可使用atof(str)把字符串轉換爲一個double類型的浮點數。atof定義在math.h中。
此題可使用函數遞歸調用的方法求解

代碼:

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;

char a[100];
double expression(){
    scanf("%s",a);
    switch (a[0]){
        case '+': return expression()+expression();
        case '-': return expression()-expression();
        case '*': return expression()*expression();
        case '/': return expression()/expression();
        default:  return atof(a);
    }
}

int main(){
    double ans=expression();
    printf("%lf\n", ans);
}

2. LeetCode 面試題 16.26. 計算器

給定一個包含正整數、加(+)、減(-)、乘(*)、除(/)的算數表達式(括號除外),計算其結果。

表達式僅包含非負整數,+, - ,*,/ 四種運算符和空格 。 整數除法僅保留整數部分。

示例 1:

輸入: "3+2*2"
輸出: 7

示例 2:

輸入: " 3/2 "
輸出: 1

示例 3:

輸入: " 3+5 / 2 "
輸出: 5

說明:

你可以假設所給定的表達式都是有效的。
請不要使用內置的庫函數 eval。

因爲不用考慮括號,只有乘除法大與加減法的運算邏輯,代碼要簡潔很多,簡潔代碼原出處

class Solution {
public:
    int calculate(string s) {
        char op = '+';
        int val;
        istringstream iss(s);
        stack<int> st;

        while(iss>>val){
            if(op=='+'){
                st.push(val);
            }else if(op=='-'){
                st.push(-val);
            }else{
                int val2 = st.top(); st.pop();
                if(op=='*') st.push(val*val2);
                else if(op=='/') st.push(val2/val);
            }
            iss>>op;
        }

        int res = 0;
        while(st.size()){
            res += st.top(); st.pop();
        }
        return res;
    }
};

參考:《大話數據結構》

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