簡單計算器(棧、中綴表示式轉字尾表達 詳解)
Foremost
老年鹹魚部落格因為一些原因又開始更新了,以後這裡可能就變成我的雜筆堆積站了O(∩_∩)O哈哈~
也不會像以前一樣放些競賽題目和比較難的題解了,那些都是青春啊(黑歷史)
然後呢,以後就放些奇奇怪怪的東西上來啦,還有別人寫的好東西,自己想記但又怕忘記的東西,真CSDN=免費網盤
(其實是懶得手寫)
Decription
給定一串含+,-,*,/的非負整數計算表達,計算該表示式的值
以0為結束點。
Sample
input
30 / 90 - 26 + 97 - 5 - 6 - 13 / 88 * 6 + 51 / 29 + 79 * 87 + 57 * 92
output
12178.21
Solution
其實這就是簡單的棧的應用。屬於經典題型,值得記錄。
而且自己也好久沒打過了,正好寫來玩一玩,理清一下思路。
題目給出的是中綴表示式,所以要計算它的值主要是兩個步驟:
- 中綴表示式轉字尾表示式
- 計算字尾表示式的值。
首先處理 中綴表示式轉字尾表示式
- 設立一個操作符棧(加減乘除),用以臨時存放操作符;
設立一個數組或者佇列,用以存放字尾表示式 - 從左至右掃描中綴表示式,如果碰到數字(注意:掃描的過程中要注意數的完整性,可能會有個位十位百位以此類推),就把數字加入到字尾表示式中。
- 如果碰到操作符op,就將其優先順序與操作符棧的棧頂操作符的優先順序比較。
如果op的優先順序高於
如果op的優先順序低於或等於棧頂操作符的優先順序,則將操作符棧的操作符不斷彈出到字尾表示式中,直到op的優先順序高於棧頂操作符的優先順序。 - 重複上述操作,直到中綴表示式掃描完畢,之後若操作符棧中仍有元素,則將它們依次彈出至字尾表示式中。
所謂操作符的優先順序就是它們的計算優先順序,乘法=除法>加法=減法,在具體實現上可以用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;