終極的表達式求值

之前的兩篇文章將表達式求值做了個分解動作。

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;
}


然後下一步是比較重要的將中綴表達式轉換爲後綴表達式(利用了一個char類型的堆棧),數字之間,數字與符號之間有空格隔開:

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#


沒錯吧!終於結束了,下次就得繼續學習其他知識了!謝謝




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