本博客为作业分享,不保证答案的绝对正确以及在时间和储存空间上的最高效做法,仅使用online judge判断为可行的方案即可。
目录
Problem: Reverse Polish Notation
Description
Reverse Polish notation (RPN, or inverse Polish notation), is a mathematical expression introduced by Polish mathematician Jan Vukaszewicz in 1920, in which all operators are placed after the operands, hence the name suffix notation. Inverse Polish notation does not require parentheses to identify the priority of operators.
For example, expression 2 + 3 is expressed as 2 3 +. Expression(1-2)+(3-4)*(-5) is expressed as1 2 - 3 4 - -5 * +.
You will be designing a program that calculates the value of RPN expressins.
Input
The input start withthe number of expressions M.
The rest of input contains several lines. Each line contains an RPN expression. There are four possible operators:+,-,*and/. The operands are integers (-1000000 <= N < 1000000−1000000<=N<1000000). The operators and operands are separated by space.
The / represents integer division.
Output
The output contains several lines. Each line corresponds to the value of RPN expression. Note that the expressions may be illegal. For example, 2 3 +is an legal expression but 2 3 or 3 + +are not. Also, 0 cannot be used as a divisor. In these cases, output an empty line instead. The last line of output should be an empty line.
也就是说,报错情况:
- operator不能进行运算
- zero division
- 有数值但是没有operator
Example
//Input
11 //M
2 3 +
2 3 *
2 3 -
3 2 /
2 3 /
- -
2 2
2 -2 +
-5 3 /
-2 -3 /
1 2 - 3 4 - -5 * +
//output
5
6
-1
1
0
err
err
0
-1
0
Idea
逆波兰表达式当中,number总是比operator多一个,用于判定输入是否有效,可以节省如果判断到一半结果expression无效的情况带来的时间开支。
对于stack的知识,详见之前的数据结构笔记:【学习笔记】CH4 Lists, Stacks and Queues | Data Structure& Algorithm Analysis in C++(ed. 3)。
输入(除了第一个数字是expression的个数M)以外,都是输入整段的expression。用string保存输入的expression。
用循环讲expression拆开,按照空格拆开,如果遇到空格则继续下一个,如果遇到数字或者operator则保存,并进行push和pop操作。
- 方法一:stack宏包调用,我参考了博客:c++ stack用法详解。
- 方法二:自己写class继承(一个Stack类,一个LStack类继承Stack,一个Link节点类)
用stack结构对每个expression进行处理:遇到数字,则新建Link(节点)类,push into stack,如果遇到operator,则pop前两个一起,进行运算,并且将结果push into stack。
其实stack当中只需要储存数字即可,因为碰到operator的输入就需要pop之前的数字结果进行运算,运算完了之后将数字再次push。
输入expression的异常检查
那么我们对这些异常值进行处理,idea如下:
设置一个报错flag:
int err_key = 0;
operator不能进行运算报错
对于输入:
//两个减号
- -
- 8
这样连续两个operator输入,那么没有东西会被push到stack里面,同理,一个operator和一个数字是无法进行计算的,这里也需要报错err。
这种报错在设计上,其实只需要,当输入为一个operator的时候,判定stack当中是不是至少有2个储存了的数值供计算,没有的话就报错。
if ("+"==v_expression[k]){
if (my_stack.size()<2){cout<<"err"<<endl;err_key = 1;break;}
/*the rest implementation*/
}
zero division报错
else if ("/"==v_expression[k]){
if (my_stack.size()<2){cout<<"err"<<endl;err_key = 1;break;}
num1=my_stack.top();
/*zero division error*/
if (num1==0){cout<<"err"<<endl;err_key = 1;break;}
my_stack.pop();
num2=my_stack.top();
my_stack.pop();
my_stack.push(num2/num1);
}
有数值但是没有operator报错
对于这种报错,可以在前面运算之后,对stack当中留存情况进行检查。只留下一个数值,即最终答案数值,是对的,其余情况都是错的。显然,比如“2 2”的输入报错属于这种情况。
由于我们之前对这种情况并没有设置err_key的改变,所以判断条件当中需要加上,以防其他错误。
if (my_stack.size()!=1 && err_key==0){cout<<"err"<<endl;}
对输入字符串进行按照空白分割
这里我借鉴博客:C++中将string按照空白字符分割的新方法。
使用如下代码将字符串保存在vector当中
/*需要调用的额外宏包*/
# include <vector>
# include <sstream>
string result;//建立一个暂存string
stringstream input(expression);
while (input>>result) v_expression.push_back(result);//将这个字符放进vector新的格子里面
同时,为了储存expression,这里使用vector储存。需要注意及时清空vector的size和capacity,借鉴博客【C++】vector的清理回收的方法。
使用如下代码保留vector但是清空size和capacity:
vector<string>().swap(v_expression);//对于string类vector
stack结构在逆波兰表达式的使用
在理解了如何排除,以及为何要排除上面的一些异常情况后,看看实现思路。
遇到operator输入就返回去拿出stack当中前两个数值,进行计算,将计算结果重新push回stack当中,也就是:
/*这里用乘法举例*/
num1=my_stack.top();//取出最上面的值
my_stack.pop();//删除最上面的值
num2=my_stack.top();//取出接下来的值
my_stack.pop();//同样删除这个值
my_stack.push(num2*num1);//进行相应计算
这里需要注意,stack内置的pop()函数是不会返回被拿出来的值,与数据结构课本当中不同,没有返回值,所以需要top()函数先返回一个最上方的值。
那么理论上来说,用正确的输入形式得到的结果一定是stack当中只有一个值,即:
my_stack.size()==1
注意事项
需要调用的宏包:
# include <iostream>
# include <stack>
# include <string>
# include <cstdio>
# include <vector>
# include <sstream>
using namespace std;
输入带空格的string, getline(cin,my_str)
使用getline(cin,my_str)。但是这里对于输入流当中的之前的残余值非常敏感,所以建议在程序 输入完需要循环多少次(即M)之后,对istream有一次清理。
输入M、清理和输入带空格string代码:
/*需要循环的次数*/
int nExpression;
cin>>nExpression;
/*清理输入流*/
cin.clear();
cin.ignore();
for (int i=0; i<nExpression; i++){
getline(cin,expression);//输入回车停止
/*the rest implementation*/
}
每次重新计算新的expression需要清空vector和stack
清空vector的方法有很多种,但是我们希望这个vector的size和capacity同时被清零。
vector<string>().swap(v_expression);//clear the vector, maintaining the empty case
同时,也要把stack清空
while (!my_stack.empty())my_stack.pop();
完整代码(含注释)
/* reverse_po.cpp
* SJTU 519260910013 Alix (SPEIT)
* created on 2022-03-17
*/
# include <iostream>
# include <stack>
# include <string>
# include <cstdio>
# include <vector>
# include <sstream>
using namespace std;
int main(){
int nExpression; //first input the number of expression we want to process
string expression; // string
int err_key = 0;
int num1=0;
int num2=0;
stack<int> my_stack;
vector<string> v_expression;//vector to store the expression by separating with space
//cout<<"Input the number of expressions you want to process: "<<endl;;
cin>>nExpression;
//cout<<nExpression<<endl;//checkpoint++
cin.clear();
cin.ignore();
for (int i=0; i<nExpression; i++){//number of expressions
//清空输入流,防止后续干扰
// cin.clear();
// cin.ignore();
vector<string>().swap(v_expression);//clear the vector, maintaining the empty case
while (!my_stack.empty())my_stack.pop();
//cout<<"inputting expression..."<<endl;
getline(cin,expression);//输入回车停止
//cout<<expression<<endl;
string result;
stringstream input(expression);
while (input>>result) v_expression.push_back(result);
//结束分割expression,开始检查并且计算
for (int k=0;k<v_expression.size();k++)
{
err_key=0;
if ("+"==v_expression[k]){
//if (my_stack.size()<2){err_key = 1;break;}
if (my_stack.size()<2){cout<<"err"<<endl;err_key = 1;break;}
num1=my_stack.top();
my_stack.pop();
num2=my_stack.top();
my_stack.pop();
my_stack.push(num2+num1);
}
else if ("-"==v_expression[k]){
//if (my_stack.size()<2){err_key = 1;break;}
if (my_stack.size()<2){cout<<"err"<<endl;err_key = 1;break;}
num1=my_stack.top();
my_stack.pop();
num2=my_stack.top();
my_stack.pop();
my_stack.push(num2-num1);
}
else if ("*"==v_expression[k]){
//if (my_stack.size()<2){err_key = 1;break;}
if (my_stack.size()<2){cout<<"err"<<endl;err_key = 1;break;}
num1=my_stack.top();
my_stack.pop();
num2=my_stack.top();
my_stack.pop();
my_stack.push(num2*num1);
}
else if ("/"==v_expression[k]){
//if (my_stack.size()<2){err_key = 1;break;}
if (my_stack.size()<2){cout<<"err"<<endl;err_key = 1;break;}
num1=my_stack.top();
if (num1==0){cout<<"err"<<endl;err_key = 1;break;}
my_stack.pop();
num2=my_stack.top();
my_stack.pop();
my_stack.push(num2/num1);
}
else{//it is a number
my_stack.push((stoi(v_expression[k])));
}
}
//if (err_key==1){cout<<"err=1"<<endl;}
if (my_stack.size()!=1 && err_key==0){cout<<"err"<<endl;}
if (my_stack.size()==1 && err_key==0){
cout<<my_stack.top()<<endl;
my_stack.pop();
}
}
return 0;
}