题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=467
解题思路:
经典的逆波兰法求表达式的值,以前是用栈模拟的,但是没有仔细研究,现在对表达式求值方法有了一定的了解。
其实表达式求值的通法就是建立一棵表达式树,则,前缀、中缀、后缀表达式分别对应表达式树的前序、中序、后序遍历。
则,通过表达式树就可以对这个问题做一个全面的分析了。另外还有一种方法是基于编译原理的自顶向下分析的方法,还没有研究,等研究好了再重新写吧。
相关知识可以点这里:
中缀转后缀法:http://zh.wikipedia.org/wiki/Shunting_yard%E7%AE%97%E6%B3%95
代码如下:
/*
*表达式求值的经典算法
*编写代码对算术表达式求值的经典方法由 Donald Knuth 描述于 1962 年。
*
*Knuth 将此概括为三个步骤:
*1、对中缀表达式进行语法分析
*2、中缀表达式到后缀表达式的转换
*3、对后缀表达式求值
*
*注意到我们谈到的这个经典算法有些简化:算术表达式只包含操作数、二元操作符和一种括号。
*此外,对于每个操作数和操作符,只用单个字符表示,使语法分析直观。
*表达式表示法:
*算术表达式中最常见的表示法形式有 中缀、前缀和 后缀表示法。
*中缀表示法是书写表达式的常见方式,而前缀和后缀表示法主要用于计算机科学领域。
*
*中缀表示法:
*中缀表示法是算术表达式的常规表示法。称它为中缀表示法是因为每个操作符都位于
*其操作数的中间,这种表示法只适用于操作符恰好对应两个操作数的时候
*(在操作符是二元操作符如加、减、乘、除以及取模的情况下)。
*对以中缀表示法书写的表达式进行语法分析时,需要用括号和优先规则排除多义性。
*Syntax: operand1 operator operand2
*Example: (A+B)*C-D/(E+F)
* 前缀表示法
* 前缀表示法中,操作符写在操作数的前面。这种表示法经常用于计算机科学,
* 特别是编译器设计方面。为纪念其发明家 ― Jan Lukasiewicz,
* 这种表示法也称 波兰表示法。
*Syntax : operator operand1 operand2
*Example : -*+ABC/D+EF
*
*后缀表示法
* 在后缀表示法中,操作符位于操作数后面。后缀表示法也称 逆波兰表示法
* (reverse Polish notation,RPN),因其使表达式求值变得轻松,所以被普遍使用。
*Syntax : operand1 operand2 operator
*Example : AB+C*DEF+/-
*前缀和后缀表示法有三项公共特征:
*操作数的顺序与等价的中缀表达式中操作数的顺序一致
*不需要括号
*操作符的优先级不相关
*
*中缀转化为后缀算法:
*a.得到一操作符或操作数;
*b.若输入为操作数,则输出到数组,转a;
*c. 若输入为'(',压栈,转a;
*d.若输入为')',栈顶出栈,输出到数组,直至栈顶为'(',抛弃'(', 转a;
*e.若输入为操作符,
* 若栈空或栈顶为'('或操作符.忧先级大于栈顶操作符,压栈,转a
* 若操作符优先级小于栈顶操作符,则出栈,输出至数组,转e;
*d.若输入结束,出栈,输出到数组,直至栈空。
*数组SNode中即为后缀表达式;
*
*优先级可以根据需要分为:栈内优先级和栈外优先级
*
*后缀表达式求值
*对后缀表达式求值比直接对中缀表达式求值简单。在后缀表达式中,不需要括号,
*而且操作符的优先级也不再起作用了。您可以用如下算法对后缀表达式求值:初始化一个空堆栈
*从左到右读入后缀表达式,如果字符是一个操作数,把它压入堆栈。如果字符是个操作符,
*弹出两个操作数,执行恰当操作,然后把结果压入堆栈。如果您不能够弹出两个操作数,
*后缀表达式的语法就不正确。到后缀表达式末尾,从堆栈中弹出结果。
*若后缀表达式格式正确,那么堆栈应该为空。
*/
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<climits>
#include<vector>
#include<stdlib.h>
#include<algorithm>
using namespace std;
const int N = 1010;
char str[N];
vector<double> num; //操作数
vector<char>ope; //运算符
int judge_oper(const char ch) //运算符优先级
{
switch(ch)
{
case '=' : return 1;
case '+' : return 2;
case '-' : return 2;
case '*' : return 3;
case '/' : return 3;
case '(' : return 0;
case ')' : return 0;
};
}
int main()
{
int ncase;
scanf("%d", &ncase);
int len; //字符串长度
double temp; //字符串中提取操作数
int length; //操作数位数
while(ncase--)
{
memset(str, '\0', sizeof(str));
num.clear();
ope.clear();
ope.push_back('=');
getchar();
scanf("%s", str);
len = strlen(str) - 1;
for(int i = 0; i < len; ++i)
{
if(str[i] >= '0' && str[i] <= '9') //操作数
{
sscanf(&str[i], "%lf%n", &temp, &length);
num.push_back(temp);
for(int j = i; j < i + length; ++j)
printf("%c", str[j]);
printf(" ");
i += length - 1;
}
else //运算符
{
if(str[i] == '(' || judge_oper(str[i]) > judge_oper(ope.back()))
ope.push_back(str[i]);
else
{
if(str[i] == ')')
{
while(ope.back() != '(')
{
printf("%c ", ope.back());
ope.pop_back();
}
ope.pop_back();
}
else
{
while( judge_oper(str[i]) <= judge_oper( ope.back() ) && ope.back() != '(')
{
printf("%c ", ope.back());
ope.pop_back();
}
ope.push_back(str[i]);
}
}
}
}
while(!ope.empty())
{
printf("%c ", ope.back());
ope.pop_back();
}
printf("\n");
}
return 0;
}