之前的兩篇文章將表達式求值做了個分解動作。
1、將中綴表達式轉換爲後綴表達式(逆波蘭表達式)。
2、利用逆波蘭表達式求出最終的結果。
表達式求值歷來有幾個比較難搞的問題:
1、帶括號的優先級問題(轉換爲逆波蘭表達式完美解決此問題)。
2、大於10的數字做表達式計算。
3、帶小數點的問題。
將中綴表達式轉換爲後綴表達式後,括號問題就已經迎刃而解。2、3兩個問題利用點小技巧就ok了。 其實表達式求值是比較基礎的問題,解決該問題可以很好的理解棧這個重要的數據結構,初學者可以好好的研究這個問題。
前面都是利用c語言寫的代碼,本文中由於需要兩個棧(char,double),所以用c++風格的代碼,利用了template這個概念,可以節省很多的代碼!
首先是聲明和定義一個棧:
#include<iostream>
using namespace std;
const int MAXSTACKSIZE=30;
//聲明棧
template<class T>
class SqStack
{
public:
SqStack();
void Pop(T * e);
void Push(T e);
T * Top();
bool isEmpty(){return top==base;}
bool isFull(){return top-base>=stacksize;}
private:
T * base;
T * top;
int stacksize;
};
//棧初始化
template<class T>
SqStack<T>::SqStack()
{
base=new T[MAXSTACKSIZE];
if(!base)exit(0);
top=base;
stacksize=MAXSTACKSIZE;
}
//壓棧
template<class T>
void SqStack<T>::Push(T e)
{
if(isFull())exit(0);
*(++top)=e;
stacksize++;
}
//出棧
template<class T>
void SqStack<T>::Pop(T * e)
{
if(isEmpty())return;
*e=*(top--);
stacksize--;
}
template<class T>
T * SqStack<T>::Top()
{
if(isEmpty())return NULL;
return top;
}
void RevToPolish(char str[])
{
SqStack<char> s;
char c; //待輸入的字符
int i=0; //將後綴表達式存入str[]時的數組下標
char temp; //操作過程中需要的臨時變量
cout<<"請輸入正確的中綴表達式,以#結束:"<<endl;
cin>>c;
cout<<endl;
while (c!='#')
{
if(isdigit(c) || c=='.') //半段是否爲數字和小數點,是則直接存入字符數組
{
while(isdigit(c) || c=='.')
{
str[i++]=c;
cin>>c;
}
str[i++]=' ';
str[i]='\0';
}
if(c=='#')break; //如果不加此處判斷,如果#前面是數字則會陷入死循環
if (c=='('||s.isEmpty()||*s.Top()=='(') //如果空棧或者左括號,或者棧頂是左括號,則直接壓棧
{
s.Push(c);
}
else
{
if(c==')') //遇到右括號,講棧中左括號之後的符號彈出棧存入數組,最後將左括號出棧
{
temp=*s.Top();
while (temp!='(')
{
s.Pop(&temp);
str[i++]=temp;
str[i++]=' ';
str[i]='\0';
temp=*s.Top();
}
s.Pop(&temp);
}
else
{ //否則,判斷棧頂的符號和當前c誰的優先級更高,judg()函數完成此功能,實現在下面
s.Pop(&temp);
if (judg(temp,c)) //如果棧頂的優先級高,則棧頂存入數組,c與當前棧頂繼續比較,直到遇到c優先級高於棧頂時結束
{
str[i++]=temp;
str[i++]=' ';
str[i]='\0';
continue;
}
else //直到遇到c優先級高於棧頂時,雙雙壓棧
{
s.Push(temp);
s.Push(c);
}
}
}
cin>>c;
}
bool judg(char c1,char c2)
{
switch(c1)
{
case '+':
if (c2=='+' || c2=='-')
{
return true;
}
else
{
return false;
}
case '-':
if (c2=='+' || c2=='-')
{
return true;
}
else
{
return false;
}
case '*':
return true;
case '/':
return true;
}
}
最後,將後綴表達式求出最終的值,如果你仔細發現,求值和轉換的代碼方法特別像!是的,正是利用堆棧後進先出的性質!請看代碼:
void getRes(char expr[])
{
SqStack<double> s;
int j=0; //i和j都是用來管理數組下標的,i主要管理傳遞進來參數的!
int i=0;
char num[10]; //這個數組名起的有點SB,是用來存放單個數字的,包括小數點!
double c1,c2;
while (expr[i]!='#')
{
while (isdigit(expr[i])||expr[i]=='.') //如果是數字,遇到空格前的存放到臨時的num字符數組中
{
if (expr[i]!=' ')
{
num[j++]=expr[i];
num[j]='\0';
i++;
}
if(expr[i]==' ') //如果遇到空格,一個數字輸入結束,利用atof函數轉換成double類型
{
c1=atof(num);
s.Push(c1);
j=0;
break;
}
}
switch(expr[i]) //判斷表達式符號,根據相應的表達式符號,棧棧頂的2個元素的運算。
{
case '+':
s.Pop(&c1);
s.Pop(&c2);
s.Push(c1+c2);
break;
case '-':
s.Pop(&c1);
s.Pop(&c2);
s.Push(c2-c1);
break;
case '*':
s.Pop(&c1);
s.Pop(&c2);
s.Push(c1*c2);
break;
case '/':
s.Pop(&c1);
s.Pop(&c2);
s.Push(c2/c1);
break;
}
i++;
}
s.Pop(&c1); //最終棧中的元素就是結果
cout<<endl;
cout<<"最終的結果爲:"<<c1<<endl;
}
int main(void)
{
char expr[50];
RevToPolish(expr);
getRes(expr);
system("pause");
return 0;
}
輸入:10-(1.2+0.8)*1.5+4/2#
沒錯吧!終於結束了,下次就得繼續學習其他知識了!謝謝