1. 程式人生 > 其它 >簡單計算器(棧、中綴表示式轉字尾表達 詳解)

簡單計算器(棧、中綴表示式轉字尾表達 詳解)

技術標籤:模板與演算法佇列堆疊stack

Foremost

老年鹹魚部落格因為一些原因又開始更新了,以後這裡可能就變成我的雜筆堆積站了O(∩_∩)O哈哈~
也不會像以前一樣放些競賽題目和比較難的題解了,那些都是青春啊(黑歷史)

然後呢,以後就放些奇奇怪怪的東西上來啦,還有別人寫的好東西,自己想記但又怕忘記的東西,真CSDN=免費網盤
(其實是懶得手寫)

Decription

給定一串含+,-,*,/的非負整數計算表達,計算該表示式的值
以0為結束點。

Sample

input

30 / 90 - 26 + 97 - 5 - 6 - 13 / 88 * 6 + 51 / 29 + 79 * 87 + 57 * 92

0

output

12178.21

Solution

其實這就是簡單的棧的應用。屬於經典題型,值得記錄。
而且自己也好久沒打過了,正好寫來玩一玩,理清一下思路。

題目給出的是中綴表示式,所以要計算它的值主要是兩個步驟:

  1. 中綴表示式轉字尾表示式
  2. 計算字尾表示式的值。

首先處理 中綴表示式轉字尾表示式

  1. 設立一個操作符棧(加減乘除),用以臨時存放操作符
    設立一個數組或者佇列,用以存放字尾表示式
  2. 從左至右掃描中綴表示式,如果碰到數字(注意:掃描的過程中要注意數的完整性,可能會有個位十位百位以此類推),就把數字加入到字尾表示式中。
  3. 如果碰到操作符op,就將其優先順序與操作符棧的棧頂操作符的優先順序比較。
    如果op的優先順序高於
    棧頂操作符的優先順序,則壓入操作符棧
    如果op的優先順序低於或等於棧頂操作符的優先順序,則將操作符棧的操作符不斷彈出到字尾表示式中,直到op的優先順序高於棧頂操作符的優先順序。
  4. 重複上述操作,直到中綴表示式掃描完畢,之後若操作符棧中仍有元素,則將它們依次彈出至字尾表示式中。

所謂操作符的優先順序就是它們的計算優先順序,乘法=除法>加法=減法,在具體實現上可以用map建立操作符和優先順序的對映,優先順序可以用數字表示。

Q1:為什麼當op高於棧頂時就要壓入操作符棧?
A1:這裡舉個例子:對於中綴表示式3+25,顯然如果先計算加法3+2會引起錯誤,必須先計算乘法25.當從左到右掃描時,加號先進入操作符棧,而由於乘號優先順序大於加號,其必須先計算,因此在後綴表示式中乘號必須在加號前面,於是在棧中乘號要比加號更靠近棧頂,以讓其優先於加號進入字尾表示式。

Q2:為什麼當op等於棧頂時不能直接壓入操作符棧
A2:這裡舉個例子:對於中綴表示式 2/3*4,如果設定優先順序相等時直接壓入操作符棧,那麼演算法步驟如下:
a) 2 進入字尾表示式,當前字尾表示式為2
b)/進入操作符棧,當前操作符棧為/
c)3進入字尾表示式,當前字尾表示式為23
d)*與操作符棧的棧頂元素/比較,相等,壓入操作符棧,當前操作符棧為/ *
e)4進入字尾表示式,當前字尾表示式為234
f)中綴表示式掃描完畢,操作符棧非空,將其全部彈入字尾表示式,最終字尾表示式變為234 * /
g)計算該字尾表示式,發現其實變成了2 / (3 * 4),顯然和原來中綴表示式的計算結果完全不同。

括號的情況要特殊處理
如果遇到的操作符是左括號"(”,就直接將該操作符輸出到堆疊當中。該操作符只有在遇到右括號“)”的時候移除。

如果掃描到的操作符是右括號“)”,將堆疊中的操作符匯出(pop)到後置表示式中,直到遇見左括號“(”。將堆疊中的左括號移出堆疊(pop )。繼續掃描下一個字元

然後計算字尾表示式

從左到右掃描字尾表示式,如果是數字,就壓入棧;如果是操作符,就連續彈出兩個操作符(**注意:**後彈出的是第一運算元,先彈出的是第二運算元),然後進行操作符的操作,生成的新數重新壓入棧中。反覆直到字尾表示式掃描完畢,這時棧中會只存在一個數,就是最終答案。

CODE

(STL version)

#include <bits/stdc++.h>

using namespace std;

struct node
{
	double num;
	char op;
	bool flag; // true -> number false -> op
};

string str;
stack <node> S; //op stack
queue <node> Q; //Postfix Expression
map<char,int> OP;

void InfixToPostfix()
{
	double num;
	node a;
	for (int i = 0;i < str.length();)
	{
		if (str[i] >= '0' && str[i] <= '9')
		{
			a.flag = true;
			a.num = str[i ++] - '0';
			while (i < str.length() && str[i] >= '0' && str[i] <= '9')
			{
				a.num = a.num * 10 + (str[i] - '0');
				i ++;
			} 	
			Q.push(a);
		}	
		else
		{
			a.flag = false;
			while (! S.empty() && OP[str[i]] <= OP[S.top().op])
			{
				Q.push(S.top());
				S.pop();
			}
			a.op = str[i];
			S.push(a);
			i ++;
		}
	}
	while (! S.empty())
	{
		Q.push(S.top());
		S.pop();
	}
}

double CalcPostfix()
{
	double a,b;
	node head,c;
	while (!Q.empty())
	{
		head = Q.front();
		Q.pop();
		if (head.flag == true) S.push(head);
		else
		{
			b = S.top().num;
			S.pop();
			a = S.top().num;
			S.pop();
			c.flag = true;
			switch(head.op)
			{
				case '+' : c.num = a + b; break;
				case '-' : c.num = a - b; break;
				case '*' : c.num = a * b; break;
				case '/' : c.num = a / b; break;
			}
			S.push(c);
		}
	}
	return S.top().num;
}

int main()
{
	freopen("data.in","r",stdin);
	OP['+'] = OP['-'] = 1;
	OP['*'] = OP['/'] = 2;
	while (true)
	{
		getline(cin,str);
		if (str == "0") break;
		for (string :: iterator it = str.begin(); it != str.end();it ++) 
			if (*it == ' ') str.erase(it);
		while (! S.empty()) S.pop();
		InfixToPostfix();
		printf("%.2f\n",CalcPostfix());
	}	
}

手寫棧佇列 version

#include <bits/stdc++.h>

using namespace std;

const int N = 100005;

struct node
{
	double num;
	char op;
	bool flag; // true -> number false -> op
};

string str;
node Stack[N];
int top = 0;
//stack <node> S; //op stack
//queue <node> Q; //Postfix Expression
node Queue[N];
map<char,int> OP;
int l = 0 , r = 0;

void InfixToPostfix()
{
	node a;
	for (int i = 0;i < str.length();)
	{
		if (str[i] >= '0' && str[i] <= '9')
		{
			a.flag = true;
			a.num = str[i ++] - '0';
			while (i < str.length() && str[i] >= '0' && str[i] <= '9')
			{
				a.num = a.num * 10 + (str[i] - '0');
				i ++;
			} 	
			Queue[++ r] = a;
		}	
		else
		{
			a.flag = false;
			while (top > 0 && OP[str[i]] <= OP[Stack[top].op]) 
			{
				Queue[++ r] = Stack[top --];
			}
			a.op = str[i];
			Stack[++ top] = a;
			i ++;
		}
	}
	while (top > 0)
	{
		Queue[++ r] = Stack[top --];
	}
}

double CalcPostfix()
{
	double a,b;
	node head,c;
	//while (!Q.empty())
	while (l <= r)
	{
		head = Queue[l ++];
	//	head = Q.front();
	//	Q.pop();
		if (head.flag == true) Stack[++ top] = head; 
		//S.push(head);
		else
		{
			b = Stack[top--].num;
			a = Stack[top--].num; 
			//b = S.top().num;
			//S.pop();
			//a = S.top().num;
			//S.pop();
			c.flag = true;
			switch(head.op)
			{
				case '+' : c.num = a + b; break;
				case '-' : c.num = a - b; break;
				case '*' : c.num = a * b; break;
				case '/' : c.num = a / b; break;
			}
			Stack[++ top] = c;
		//	S.push(c);
		}
	}
	return Stack[top].num;
	//return S.top().num;
}

int main()
{
	freopen("data.in","r",stdin);
	OP['+'] = OP['-'] = 1;
	OP['*'] = OP['/'] = 2;
	while (true)
	{
		getline(cin,str);
		if (str == "0") break;
		for (string :: iterator it = str.begin(); it != str.end();it ++) 
			if (*it == ' ') str.erase(it);
		top = 0;
		//while (! S.empty()) S.pop();
		InfixToPostfix();
		printf("%.2f\n",CalcPostfix());
	}	
}

這裡在貼一下手寫棧和佇列的模板

手寫棧

struct stack
{
	const int N = 100000 + 100;
    int a[N], top = 0;
    void push(int x)
    {
        a[++ top] = x;
    }
    int front()
    {
        return a[top];
    }
    void pop()
    {
        top--;
    }
    int empty()
    {
        return top >= 0 ? 1 : 0;
    }
}Stack;

手寫佇列

struct queue
{
	const int N = 100000 + 100;
    int l = 0,r = 0,a[N];
    void push(int x)
    {
        a[++r] = x;
    }
    int front()
    {
        return a[l];
    }
    void pop()
    {
        l++;
    }
    int empty()
    {
        return l > r ? 1 : 0;
    }
}q;