逆波蘭表達式

之前寫過計算一個字符串的表達式,當時都不知道有逆波蘭這東西,- -!真是孤陋寡聞了,不過勉強用其餘方法寫了出來,用到了遞歸,不過總體來講很羅索!ios

逆波蘭對這種字符串的表達式計算很容易,只要把一個普通的表達式轉換成逆波蘭後計算就輕鬆不少!函數

首先看一下普通表達式:(1+2)*(2-1)  轉成逆波蘭後變成 1 2 + 2 1 - *spa

下面說說怎麼轉成逆波蘭,而後再說計算逆波蘭!遞歸

 

假設要轉的表達式是一條合法的表達式(這個本身事先寫個代碼判斷表達式的合法性),那麼遵守下面的規則就能夠轉成逆波蘭了:ci

1.首先把普通的表達式按照運算符分離出來放在一個集合E中,好比1+2*3 分離後集合裏的元素就是 1 + 2 * 3 五個元素字符串

2.再定義一個集合R(最好是字符串類型的集合,省得後面要轉類型),主要是用來存放逆波蘭表達式的,還有定義一個堆棧(存儲運算符用),最後從左到右遍歷集合E     string

3.遍歷E的規則以下:it

   3.1若是該元素是數字,直接把它添加到集合R中io

   3.2不然它確定是運算符,那麼再進行判斷stream

        3.2.1若是該元素是左括號,或者當時棧爲空,那麼直接入棧

        3.2.2若是該元素是右括號,則把棧內的運算符出棧並添加到集合R中,直到遇到第一個左括號結束(左括號也出棧但不添加到R)

        3.2.3不然該元素是普通的運算符(也就是+-*/之類的),那麼用該運算符和棧內的運算符號比較優先級(至於怎麼比較它們的優

                先級,你能夠定義一個函數,傳一個運算符過去,返回一個int,int值越大優先級越高),若是該運算符的優先級比棧內的運

                算符優先級高 或者 棧爲空,則直接入棧,不然把棧內的運算符出棧並添加到R中,再判斷下個棧內的運算符優先級,直到遇

                棧內的運算符優先級<=該運算符或者棧爲空時再把該運算符入棧

   3.3整個過程完成後,再把棧內的全部運算符出棧並添加到R中

 

下面看着表達式來理解上面的步驟

運算符優先級:

(  )        1

+  -       2

*  /  %  3

值越大優先級越高,注意括號優先級是最低的

 

表達式:2*(1+2/2)

定義集合E儲存分離的表達式,E{ 2, *, (, 1, +, 2, /, 2, ) }

定義集合R儲存逆波蘭, R{...}

定義堆棧S儲存運算符, S{...}

從左到右遍歷E,開始判斷

E[0]是數字2,直接添加到R,R{ 2 }

E[1]是乘號,和棧內運算符比較優先級,這時棧爲空,全部直接入棧,S{*}

E[2]是左括號,直接入棧,S{ *, ( }

E[3]是數字1,直接添加到R,R{ 2, 1 }

E[4]是加號,比較棧頂運算符,棧頂爲"(",加號比它優先級高,全部直接入棧, S{ *, (, + }

E[5]是數字2,直接添加到R, R{ 2, 1 , 2 }

E[6]是除號,比較棧頂運算符,棧頂爲"+",除號>加號,直接入棧, S{ *, (, +, / }

E[7]是數字2,直接添加到R,R{2, 1, 2, 2}

E[8]是右括號,把棧內運算符出棧並添加到R直到遇到第一個左括號,R{ 2, 1, 2, 2, /, + }  S{ * },注意左括號出棧但不加到R

那麼整個過程就完畢,最後一步把棧內的全部元素添加到R

R{ 2, 1, 2, 2, /, +, * } S{...}

那麼表達式2*(1+2/2) 轉逆波蘭變成 2 1 2 2 / + *

 

計算逆波蘭很是簡單,也須要一個堆棧,規則是:

1.從左到右遍歷R

2.若是該元素是數字,直接入棧

3.若是該元素是運算符,出棧兩個數,計算結果再入棧,逆波蘭遍歷完後棧內的元素就是表達式的值了

 

如:R{ 2, 1, 2, 2, /, +, * }

1.數字就入棧,那麼S{ 2, 1, 2, 2 }

2.除號,出棧兩個2 / 2 = 1,再入棧 S{ 2, 1, 1 }

3.加號,出棧兩個1 + 1 = 2,再入棧S{ 2, 2 }

4.乘號,出棧兩個2 * 2 = 4,再入棧S{ 4 }

5.逆波蘭已遍歷完,棧內元素是4,對照下普通表達式的運算結果是否相同?

 

關於預處理問題

轉換成逆波蘭前必須先要處理一下

A.好比你要分離普通表達式的時候,負號和減號是相同的,得把減號用其餘符號代替,怎麼判斷是減號仍是負號?很簡單,從右往左遍歷,遇到"-"時再判斷它的前一個符,若是是數字或者右括號,那麼它就是減號!

 

B.若是表達式的第一個符是負號,也就是形如-(1+2),那麼根據上面第一條的判斷它爲負號而不是減號,分離這個表達式時就會變成 - ( 1 + 2 ),負號就會被獨立分離出來,這樣轉逆波蘭是沒有問題,可是計算逆波蘭就有問題,它會被轉成逆波蘭爲:- 1 2 +的樣子,由於負號會被認爲是運算符,因此把棧內元素出棧計算,這時棧根本就是爲空的,就會出異常!解決辦法是若是表達式的第一個符號爲負號,在整個表達式前面加一個0,就會變成

0-(1+2),這樣根據A判斷"-"就會被當成是減號而不是負號,0減去後面的數不就是負數了嘛!

 

C.相似第2條,看下形如1*-(-(-(1+2)))這樣的表達式,根據A的判斷"-"會被當成負號,顯然,和B同樣,也會出現同樣的錯誤,解決辦法仍是加0,首先判斷若是它是負號,再判斷它後面是數字仍是運算符,若是是運算符就在負號前面加個0,那麼整個下來表達式就會變成1*0-(0-(0-(1+2)))

 

 

以上,如發現有錯誤請指出,有更好的處理辦法請提出,謝謝!

 

#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<stack>
#include<math.h>
#include <string.h>
#define max 100
using namespace std;
char ex[max]; /*存儲後綴表達式*/

void trans()
{ /*將算術表達式轉化爲後綴表達式*/
    char str[max]; /*存儲原算術表達式*/
    char stack[max]; /*做爲棧使用*/
    char ch;
    int sum,i,j,t,top=0;
    printf("*****************************************\n");
    printf("*輸入一個求值的表達式,以#結束。*\n");
    printf("******************************************\n");
    printf("算數表達式:");
    i=0; /*獲取用戶輸入的表達式*/
    do{
        i++;
        //cin>>str[i];/*此步我用的是C++ C語言的話在後面 之因此用這個有一點區別 都*/
        scanf("%c",&str[i]);
      }while(str[i]!='#' && i!=max);
    sum=i;
    t=1;i=1;
    ch=str[i];i++;
//
    while(ch!='#')
    {
        switch(ch)
        {
            case '(': /*斷定爲左括號*/
                top++;stack[top]=ch;
                break;
            case ')': /*斷定爲右括號*/
                while(stack[top]!='(')
                {
                    ex[t]=stack[top];top--;t++;
                }
                top--;
                break;
            case '+': /*斷定爲加減號*/
            case '-':
                while(top!=0&&stack[top]!='(')
                {
                    ex[t]=stack[top];
                    top--;
                    t++;
                }
                top++;
                stack[top]=ch;
                break;
            case '*': /*斷定爲乘除號*/
            case '/':
                while(stack[top]=='*'||stack[top]=='/')
                {
                    ex[t]=stack[top];
                    top--;
                    t++;
                }
                top++;
                stack[top]=ch;
                break;
            case ' ':break;
            default:
                while(ch>='0'&&ch<='9')
                { /*斷定爲數字*/
                    ex[t]=ch;t++;
                    ch=str[i];i++;
                }
                i--;
                ex[t]=' ';t++;
        }
        ch=str[i];i++;
    }
    while(top!=0)
    {
        ex[t]=stack[top];
        t++;top--;
    }
    ex[t]=' ';
    printf("\n\t原來表達式:");
    for(j=1;j<sum;j++)
        printf("%c",str[j]);
    printf("\n\t逆波蘭式:",ex);
    for(j=1;j<t;j++)
        printf("%c",ex[j]);
}

void compvalue()
{ /*計算後綴表達式的值*/
    float stack[max],d; /*做爲棧使用*/
    char ch;
    int t=1,top=0; /*t爲ex下標,top爲stack下標*/
    ch=ex[t];t++;
    while(ch!=' ')
    {
        switch(ch)
        {
            case '+':
                stack[top-1]=stack[top-1]+stack[top];
                top--;
                break;
            case '-':
                stack[top-1]=stack[top-1]-stack[top];
                top--;
                break;
            case '*':
                stack[top-1]=stack[top-1]*stack[top];
                top--;
                break;
            case '/':
                if(stack[top]!=0) stack[top-1]=stack[top-1]/stack[top];
                else
                {
                    printf("\n\t除零錯誤!\n");
                    exit(0); /*異常退出*/
                }
                top--;
                break;
            default:
                d=0;
                while(ch>='0'&&ch<='9')
                {
                    d=10*d+ch-'0'; /*將數字字符轉化爲對應的數值*/
                    ch=ex[t];t++;
                }
                top++;
                stack[top]=d;
        }
        ch=ex[t];
        t++;
    }
    printf("\n\t計算結果:%g\n",stack[top]);
}

int main(){    trans();    compvalue();    system("pause");    return 0;}

相關文章
相關標籤/搜索