Foremost
。。。
Decription
给定一串含+,-,*,/的非负整数计算表达,计算该表达式的值
以0为结束点。
Sample
input
30 / 90 - 26 + 97 - 5 - 6 - 13 / 88 * 6 + 51 / 29 + 79 * 87 + 57 * 92
0
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;