1. 程式人生 > >表示式的值, 表示式計算器

表示式的值, 表示式計算器

 

問題描述

  輸入一個只包含加減乖除和括號的合法表示式,求表示式的值。其中除表示整除。

輸入格式

  輸入一行,包含一個表示式。

輸出格式

  輸出這個表示式的值。

樣例輸入

1-2+3*(4-5)

樣例輸出

-4

資料規模和約定

  表示式長度不超過100,表示式運算合法且運算過程都在int內進行。

 這道題很偏向應用,因為這是計算器的常見寫法。或者說底層計算器的常見寫法.

此程式碼不止限於計算此題,此程式碼還可計算小數型別。但負數以及異常處理,暫時還沒寫

需要用到來實現,涉及知識點為字尾表示式(逆波蘭表示式)。可以參考這幾位大俠的文章《

原表示式轉換為字尾表示式

《資料結構和演算法》之中綴表示式、字尾表示式轉換

 

此題的思路可以把資料轉為中綴表示式跟字尾表示式。然後進行計算。但是這樣有點太過麻煩。

我的思路是:

1.用兩個棧,一個存放資料, 一個存放運算子

2.資料的棧,按順序壓入。而運算子的棧是一個單調棧,棧底運算子優先順序是最低的, 棧頂優先順序最高

3.為保證單調棧的實現,如果棧頂元素優先順序高於當前運算子,則先以逆波蘭演算法計算表示式的值,直到棧頂元素優先順序低於當前運算子(注意判斷非空)

4. 遇到右括號)時,計算直到左括號我(為止的值。左擴號在壓棧時也需要特殊處理)

5.注意處理幾個符號連續的問題。

(本想在括號裡單獨處理,結果越寫越麻煩, 因為"(+"與“)+”兩個很不相同)

/*
    作者:貫穿真Sh
    時間:2018年7月25日23:15:46

	表示式計算:使用逆波蘭表示式(這裡為方便沒有轉中綴,字尾表示式) 
	模擬底層資料的運作方式 
	輸入一個表示式,如1+2*(3-4),輸出它的值 
	
	思路,使用兩個棧
	一個數據棧,一個運算子的單調棧
	遇到小括號時,計算小括號的再把新資料壓棧
	
	只有當前操作符的優先順序高於操作符棧棧頂的操作符的優先順序,才入棧,否則彈出操作符以及運算元進行計算直至棧頂操作符的優先順序低於當前操作符
	,然後將當前操作符壓棧。當所有的操作符處理完畢(即操作符棧為空時),運算元棧中剩下的唯一一個元素便是最終的表示式的值
	
	暫沒解決負數的問題,其中一個思路是把減號全當負號,然後需要時補充加號,變成加某個負數 
    此程式還沒有處理一些異常問題。只寫了一半
*/
#include<iostream>
#include<stack>
#include<string>

using namespace std;

stack<double> data;
stack<char> oper;

//處理運算子優先順序 
int priority(char symbol)
{
	switch(symbol)
	{
		case '+':
		case '-':
			return 1;
			
		case '*':
		case '/':
			return 2;
		case  '(':
			return 0;
		default:
			return -1;
		
	}
	
}

int calculate()
{
	char top = oper.top();
	oper.pop();
	//如果是'(', 返回0 
	if(top == '(')
		return 0; 
	
	double first = data.top();
	data.pop();
	double second = data.top();
	data.pop();
	
	if(top == '+')
		data.push(first + second);
	else if(top == '-')
		//由於倒過來了,second才是表示式中較前的數 
		data.push(second - first);
	else if(top == '*')
		data.push(first * second);
	else if(top == '/')
	{
	
		if(second == 0)
		{
			cout << "不能除以0" << endl;
			return -1;	
		} 
		data.push(second / first);
	}
	else
	{
		cout << oper.top();
		cout << "輸入符號有誤" << endl;
        return -1;
	}
	return 1;
} 


int main()
{
	string str;
	
	cin >> str;
	
	int len = str.length();
	double sum = 0;
	double carry = 10;//進位,可以用來控制小數以及整數
	bool open = true; //用於控制data的push開關。防止兩個符號連續時push 0的問題 
	for(int i = 0; i < len; i++)
	{
		//處理數字 
		if(str[i] <= '9' && str[i] >= '0')
		{
			sum = str[i] - '0' + sum * carry;
			//最後一個是數字的話要特殊處理 
			if(i == len - 1)
			{
				data.push(sum);
			}
			open = true;
		}
		//處理小數問題:遇到小數點後面每個位除以10 
		else if(str[i] == '.')
		{
			carry = 0.1;
		}
		else
		{	
			//處理兩個符號連續的問題 
			if(open)
			{
				data.push(sum);
				
			}
			open = false;
			sum = 0;
			
			//遇到)時,先處理小括號內的值 
			if(str[i] == ')') 
			{
				//計算括號裡的資料,:直到遇到左括號結束迴圈 
				while(calculate() != 0)
				{

				} 					
			}
			else
			{

				//當前運算子優先順序比棧頂元素值大時,入棧, 否則先將前面的表示式計算結果後在加入棧 , 直到操作符優先順序比棧頂大, '('括號優先順序設為最低是因為方便計算 
				while(str[i] != '(' && !oper.empty() && (priority(oper.top()) > priority(str[i])))
				{
					calculate(); 
				} 
				oper.push(str[i]);
				
			}
		}

		
	}
	while(!oper.empty()) 
		calculate();
	
	cout << data.top() << endl;
	
	return 0;
}