题目:

http://acm.hdu.edu.cn/showproblem.php?pid=1237

题意:

Problem Description
读入一个只包含 +, -, *, / 的非负整数计算表达式,计算该表达式的值。

Input
测试输入包含若干测试用例,每个测试用例占一行,每行不超过200个字符,整数和运算符之间用一个空格分隔。没有非法表达式。当一行中只有0时输入结束,相应的结果不要输出。

Output
对每个测试用例输出1行,即该表达式的值,精确到小数点后2位。

Sample Input
1 + 2
4 + 2 * 5 - 7 / 11
0

Sample Output
3.00
13.36

思路:

这题直接计算的话,因为有优先级的问题,会很麻烦,但是用逆波兰表达式会很简单。

但原文有个小错误,在这里已更正,用加粗字体标出
中缀表达式转换为后缀表达式(逆波兰表达式):
从左至右遍历一个给定的中序表达式,也就是我们常规的数学计算的表达式。
1. 如果遇到的是数字,我们直接加入到栈S1中;
2. 如果遇到的是左括号,则直接将该左括号加入到栈S2中;
3. 如果遇到的是右括号,那么将栈S2中的运算符一次出栈加入到栈S1中,直到遇到左括号,但是该左括号出栈S2并不加入到栈S1中;
4. 如果遇到的是运算符,包括单目运算符和双目运算符,我们按照下面的规则进行操作:
- 1. 如果此时栈S2为空,则直接将运算符加入到栈S2中;
- 2. 如果此时栈S2不为空,当前遍历的运算符的优先级大于栈顶运算符的优先级,那么直接入栈S2;
- 3. 如果此时栈S2不为空,当前遍历的运算符的优先级小于等于栈顶运算符的优先级,则将栈顶运算符一直出栈加入到栈S1中,直到栈为空或者遇到一个运算符的优先级小于当前遍历的运算符的优先级,此时将该运算符加入到栈S2中;
5. 直到遍历完整个中序表达式之后,栈S2中仍然存在运算符,那么将这些运算符依次出栈加入到栈S1中,直到栈为空。

求逆波兰表达式的值:
维护一个数据结果栈S3,我们将会看到该栈中最后存放的是最终的表达式的值。我们从左至右的遍历栈S1,然后按照下面的规则进行操作栈S3.
1. 如果遇到的是数字,那么直接将数字压入到S3中;
2. 如果遇到的是单目运算符,那么取S3栈顶的一个元素进行单目运算之后,将结果再次压入到栈S3中;
3. 如果遇到的是双目运算符,那么取S3栈顶的两个元素进行,首先出栈的在右,后出栈的在左,进行双目运算符的计算,将结果再次压入到S3中。
按照上面的三个规则,遍历完整个栈S1,那么最后S3中的值就是逆波兰表达式的值了

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef pair<int, int> P;
const int N = 1010;

int precedence(char ch)
{
    if(ch == '+' || ch == '-') return 1;
    else if(ch == '*' || ch == '/') return 2;
    else if(ch == '%' || ch == '^') return 3;
    else return 0;
}
void reverse_polish(char s[], char sta[])
{
    char ts[N];
    int k = 0, top = 0;
    ts[top++] = '@'; //在栈底设置一个哨兵位
    for(int i = 0; s[i]; i++)
    {
        if(s[i] == '(') ts[top++] = s[i]; //遇到左括号直接入栈
        else if(s[i] == ')')
        { //遇到右括号,把ts栈中的元素弹出压到sta栈中,直到遇到左括号,左括号出栈但不压入sta栈
            while(ts[top-1] != '(') sta[k++] = ts[--top];
            --top;
        }
        else if(s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/')
        { //把当前运算符与ts栈顶的运算符比较,栈顶的运算符优先级大于等于当前运算符,就弹出压到sta栈中,直到不符合条件
            while(precedence(ts[top-1]) >= precedence(s[i])) sta[k++] = ts[--top];
            ts[top++] = s[i]; //把当前运算符压到ts栈中
        }
        else if(s[i] >= '0' && s[i] <= '9') //数字直接压到sta栈中
        {
            while((s[i] >= '0' && s[i] <= '9') || s[i] == '.') sta[k++] = s[i++];
            i--;
            sta[k++] = ' ';//加空格间隔数字
        }
    }
    while(ts[top-1] != '@') sta[k++] = ts[--top]; //把st栈中剩余元素压到sta栈中
    sta[k++] = '\0';
}
double Operator(double num1, double num2, char ch)
{
    if(ch == '+') return num1 + num2;
    if(ch == '-') return num1 - num2;
    if(ch == '*') return num1 * num2;
    if(ch == '/') return num1 / num2;
}
void solve(char s[])
{
    double ts[N];
    int top = 0;
    for(int i = 0; s[i]; i++)
    {
        if(s[i] >= '0' && s[i] <= '9')
        {//数字直接压到st栈中
            int num = 0;
            while(s[i] >= '0' && s[i] <= '9') num = num * 10 + s[i] - '0', i++;
            i--;
            ts[top++] = num;
        }
        else if(s[i] != ' ')
        { //遇到二目运算符,从栈中取出两个数进行运算
            double num1 = ts[--top], num2 = ts[--top];
            ts[top++] = Operator(num2, num1, s[i]);
        }
    }
    printf("%.2f\n", ts[0]);
}

int main()
{
    char s[N], sta[N];
    while(true)
    {
        gets(s);
        if(s[0] == '0' && strlen(s) == 1) break;
        reverse_polish(s, sta);
        solve(sta);
    }
    return 0;
}

2017/5/3
突然发现用两个栈不转换成后缀表达式,而是直接算,这样更简单
建两个栈,一个运算符栈,一个数字栈,定义右括号的优先级小于所有的运算符,左括号的优先级小于右括号的优先级。遇到数字就加入到数字栈中;遇到运算符时,比较当前运算符和栈顶运算符的优先级,若栈顶运算符的优先级大于等于当前运算符时,就取出栈顶运算符,再从数字栈中取出两个数字,运算后得出结果再加入数字栈,重复这个过程,直到栈顶运算符小于当前运算符为止,然后把当前运算符入栈。最后输出数字栈顶即可

#include <bits/stdc++.h>
using namespace std;

const int N = 1010;
int precedence(char ch)
{
    if(ch == '+' || ch == '-') return 3;
    else if(ch == '*' || ch == '/') return 4;
    else if(ch == ')') return 2;
    else if(ch == '(') return 1;
}
double work(double a, double b, char op)
{
    double res;
    if(op == '+') res = a + b;
    else if(op == '-') res = a - b;
    else if(op == '*') res = a * b;
    else if(op == '/') res = a / b;
    return res;
}
int main()
{
    char s[N];
    while(gets(s+1))
    {
        int len = strlen(s+1);
        if(s[1] == '0' && len == 1) break;
        s[0] = '(', s[++len] = ')';
        stack<char> st1;
        stack<double> st2;
        for(int i = 0; i <= len; i++)
        {
            if(s[i] == '(') st1.push(s[i]);
            else if(s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/')
            {
                while(precedence(st1.top()) >= precedence(s[i]))
                {
                    double a = st2.top(); st2.pop();
                    double b = st2.top(); st2.pop();
                    double ans = work(b, a, st1.top());
                    st1.pop();
                    st2.push(ans);
                }
                st1.push(s[i]);
            }
            else if(s[i] == ')')
            {
                while(precedence(st1.top()) >= precedence(s[i]))
                {
                    double a = st2.top(); st2.pop();
                    double b = st2.top(); st2.pop();
                    double ans = work(b, a, st1.top());
                    st1.pop();
                    st2.push(ans);
                }
                st1.pop();
            }
            else if(isdigit(s[i]))
            {
                int val = 0;
                while(isdigit(s[i])) val = val*10 + s[i]-'0', i++;
                i--;
                st2.push(val);
            }
        }
        printf("%.2f\n", st2.top());
    }
    return 0;
}