题目描述:输入合法的算术表达式(中缀表达式),输出原始字符串,转换后的后缀表达式,以及计算结果。
题目考察点:栈的应用,掌握栈先入后出的特点。
算法思路:中缀表达式转后缀的算法采用调度场算法。当读入一个数字,就将数字输出;当读入一个运算符时,如果此时运算符栈为空就将运算符压栈,如果运算符栈不为空,就比较当前运算符和栈顶运算符的优先级,如果当前运算符优先级大于栈顶运算符,则将当前运算符压栈,如果优先级小于等于当前运算符,则将栈顶运算符弹出放至输出队列后,继续比较当前运算符与栈顶运算符优先级,重复上述过程,直到当前运算符被压栈。(事实上,这只针对左结合性的运算符,而加减乘除都是左结合性)。如果有括号,读到左括号就压栈,当读到右括号就将在左括号之后压栈的所有运算符依次弹出,当然也将左括号弹出。当读取结束后,栈中还有运算符就将剩余的运算符依次弹出。此时得到的即为输出队列。
在这里放上一个维基百科的例子。
输入 | 动作 | 输出 (逆波兰表示法) | 运算符栈 | 提示 |
---|---|---|---|---|
3 | 将符号加入输出队列 | 3 | ||
+ | 将符号压入操作符堆栈 | 3 | + | |
4 | 将符号加入输出队列 | 3 4 | + | |
* | 将符号压入操作符堆栈 | 3 4 | * + | *号的优先级高于+号 |
2 | 将符号加入输出队列 | 3 4 2 | * + | |
/ | 将堆栈中元素弹出,加入输出队列 | 3 4 2 * | + | /号和*号优先级相同 |
将符号压入操作符堆栈 | 3 4 2 * | / + | /号的优先级高于+号 | |
( | 将符号压入操作符堆栈 | 3 4 2 * | ( / + | |
1 | 将符号加入输出队列 | 3 4 2 * 1 | ( / + | |
− | 将符号压入操作符堆栈 | 3 4 2 * 1 | − ( / + | |
5 | 将符号加入输出队列 | 3 4 2 * 1 5 | − ( / + | |
) | 将堆栈中元素弹出,加入输出队列 | 3 4 2 * 1 5 − | ( / + | 循环直到找到(号 |
将堆栈元素弹出 | 3 4 2 * 1 5 − | / + | 括号匹配结束 | |
^ | 将符号压入操作符堆栈 | 3 4 2 * 1 5 − | ^ / + | ^号的优先级高于/号 |
2 | 将符号加入输出队列 | 3 4 2 * 1 5 − 2 | ^ / + | |
^ | 将符号压入操作符堆栈 | 3 4 2 * 1 5 − 2 | ^ ^ / + | ^号为从右至左求值 |
3 | 将符号加入输出队列 | 3 4 2 * 1 5 − 2 3 | ^ ^ / + | |
END | 将栈中所有数据加入输出队列 | 3 4 2 * 1 5 − 2 3 ^ ^ / + |
至于计算就比较简单,只需要对后缀表达式进行计算即可。将后缀表达式依次压栈,读到数字就压栈,读到运算符就从栈中弹出两个元素做运算,再把结果弹入栈。这里需要注意的一个问题是,运算时需要用第二个弹出的元素对第一个弹出的元素做运算,比如后缀表达式为21-,此时2压栈,1压栈,读到-号时,先弹出1,再弹出2,则运算2-1.(即次栈顶元素op栈顶元素)
代码如下:
#include<iostream>
#include<stack>
#include<string>
#include<cctype>
using namespace std;
bool isNumber(char s)//用于判定是否是数字
{
if(isdigit(s))
{
return true;
}
return false;
}
//用数字表示优先级,把‘+’和‘-’定义为1,‘*’和‘/’定义为2,数字大代表优先级大
int priorOfOperator(char operator_s)
{
int temp = 0;
if(operator_s == '+'||operator_s == '-')
{
temp = 1;
}
else if(operator_s == '*' || operator_s == '/')
{
temp = 2;
}
return temp;
}
/*处理运算符,遇到运算符比栈顶优先级高的,则压栈
否则替代栈顶的运算符,将栈顶运算符加入输出队列*/
void handleStackOperator(string& result, char currOper, stack<char>& operators)
{
if(operators.size() == 0)
{
operators.push(currOper);
return;
}
if(priorOfOperator(currOper) > priorOfOperator(operators.top()))
{
operators.push(currOper);
}
else
{
cout<<operators.top();
result += operators.top();
operators.pop();
return handleStackOperator(result, currOper, operators);
}
}
//有左括号时直接入栈,然后正常操作,遇到右括号时,将左右括号之间所有运算符出栈
void handleOperator(string& result, char currOper, stack<char>& operators)
{
if(operators.size() == 0)
{
operators.push(currOper);
return;
}
if(currOper == '+' || currOper == '-' ||
currOper == '*' || currOper == '/')
{
handleStackOperator(result, currOper, operators);
}
else if(currOper == '(')
{
operators.push(currOper);
}
else if(currOper == ')')
{
while(operators.top()!='(')
{
cout<<operators.top();
result += operators.top();
operators.pop();
}
operators.pop();
return;
}
}
//计算结果
float caclResult(string& s)
{
stack<int> result;
for(int i = 0; i < (int)s.length(); i++)
{
if(isNumber(s[i]))
{
result.push(s[i] - '0');
}
else
{
int temp2 = result.top();
result.pop();
int temp1 = result.top();
result.pop();
if(s[i] == '+')
{
result.push(temp1+temp2);
}
else if(s[i] == '-')
{
result.push(temp1-temp2);
}
else if(s[i] == '*')
{
result.push(temp1*temp2);
}
else if(s[i] == '/')
{
result.push(temp1/temp2);
}
}
}
return result.top();
}
int main()
{
string test = "";
string result = "";//用来存后缀表达式,便于最后做计算
cin>>test;//读入合法的算术表达式
cout<<test<<endl;;
stack<char> test_stack;
for(int i = 0; i<(int)test.length();i++)
{
if(isNumber(test[i]))
{
cout<<test[i];
result += test[i];
}
else
{
handleOperator(result, test[i],test_stack);
}
}
while(test_stack.size() != 0)//运算符栈不为空则将剩下的全部弹出
{
cout<<test_stack.top();
result += test_stack.top();
test_stack.pop();
}
cout<<endl;
cout<<caclResult(result)<<endl;//计算结果
return 0;
}