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