对于我们数学中常见的中缀表达式如 1+2 我们能很好的理解和判断优先级,但对于编程而言就显得非常的繁琐,但可以转化为后缀表达式也就是逆波兰式如 1+2变成1 2 + ,这样一来编写程序来计算就变得容易起来,计算逆波兰式在计算机上主要是利用栈结构来存储,对于一个逆波兰式如 1 2 + 3 *,(本来的中缀表达式应该是(1+2)*3),计算过程如下:假设现在是用字符串存储的逆波兰式,从头到尾遍历一遍,是数字就入栈,是运算符就出栈两个数进行对应的运算后再入栈,到最后栈中只剩下一个元素也就是结果了。具体过程为:1入栈,2入栈,遇到 +,2出栈,1出栈,1+2 = 3入栈,3入栈,遇到 *,出栈 3,出栈 3,入栈3*3 = 9,结束。这里没用到减法和除法,这两种运算需要考虑运算顺序,是后出栈的数除或减先出栈的数。
逆波兰式的计算很简单,难的是在于将普通式转化为逆波兰式
直接记住转化规则:
如果是数字,直接打印这个数字
如果是左括号也就是‘(‘,直接入栈。
如果是‘)‘,一直出栈并打印直到遇到‘(‘,这里‘)‘不再入栈。
如果是‘+‘ 或 ‘-‘,一直出栈并打印直到遇到‘(‘或者到栈底的时候停止出栈,然后‘+‘或‘-‘入栈。
如果是‘*‘或‘/‘,一直出栈并打印直到遇到‘+‘或‘-‘或‘(‘或栈底停止出栈,然后‘*‘或‘/‘入栈。
当结束时若栈不为空,出栈并打印栈中全部元素
举个例:(1+2)*3/4转化为逆波兰式
首先‘(‘入栈,栈中为‘(‘,打印为空。
1直接打印,栈中为‘(‘,打印为1。
‘+‘因为栈底为‘(‘,不需要出栈任何元素直接入栈,栈中为‘(‘,‘+‘,打印为1。
2直接打印,栈中为‘(‘,‘+‘,打印为1 2。
‘)‘出栈并打印‘+‘,然后遇到‘(‘停止出栈,接着出栈掉‘(‘,栈中为空,打印为1 2 +。
‘*‘因为栈为空直接入栈,栈中为‘*‘,打印为1 2 +。
3直接打印,栈中为空,打印为1 2 + 3。
‘/‘出栈并打印‘/‘,然后遇到栈底,结束出栈,入栈‘/‘,栈中为‘/‘,打印为1 2 + 3 *。
4直接打印,栈中为栈中为‘/‘,打印为1 2 + 3 * 4。
结束时栈中还有‘/‘,出栈并打印,栈中为‘/‘,打印为1 2 + 3 * 4 /。
结果就为1 2 + 3 * 4 /
逆波兰表达式的计算
从头到尾遍历一遍逆波兰表达式,如果是数字就直接入栈,如果是运算符就出栈两个数进行运算后结果入栈。
当遍历完以后栈中所剩的一个数就是最终结果。
接着上例:
1, 2依次入栈。
+出栈1 2执行+然后结果3入栈。
3入栈。
* 出栈3 3进行*然后入栈结果9.
4入栈。
/出栈9 4进行/然后入栈9/4。
结果就是9/4=2.25。
最后贴上代码:
- //因为是随手写来练习一下就不考虑小数和负数的情况了,那样会耽误很多时间
- #include<iostream>
- #include<cmath>
- #include<string>
- using namespace std;
- //虽然有现成的模板类stack但最后还是手写了一个
- template<typename anytype>
- class MyStack{
- struct element{
- anytype Data;
- element* last;
- };
- element* top;
- public:
- MyStack():top(NULL){}
- ~MyStack(){
- element* pointer;
- while(top!=NULL){
- pointer=top;
- delete pointer;
- }
- }
- void push(anytype x){
- element* pointer=new element;
- pointer->Data=x;
- pointer->last=top;
- top=pointer;
- }
- anytype pop(){
- if(top!=NULL){
- anytype PopData=top->Data;
- element* pointer=top;
- top=top->last;
- delete pointer;
- return PopData;
- }
- else{
- return 0;
- }
- }
- anytype GetTop(){
- if(top!=NULL){
- return top->Data;
- }
- else{
- return 0;
- }
- }
- };
- //定于一个函数用于将普通表达式转化为逆波兰表达式
- string change(string x){
- int len=x.size();
- string ans;
- MyStack<char> operators;
- for(int i=0;i<len;i++){
- if(x[i]>=‘0‘&&x[i]<=‘9‘){
- int j;
- for(j=i;x[j]>=‘0‘&&x[j]<=‘9‘;j++){
- ans+=x[j];
- }
- ans+=‘ ‘;
- i=j-1;
- }
- else if(x[i]==‘(‘){
- operators.push(x[i]);
- }
- else if(x[i]==‘*‘||x[i]==‘/‘){
- while(operators.GetTop()==‘*‘||operators.GetTop()==‘/‘){
- ans+=operators.pop();
- }
- operators.push(x[i]);
- }
- else if(x[i]==‘)‘){
- while(operators.GetTop()!=‘(‘){
- ans+=operators.pop();
- }
- operators.pop();
- }
- else if(x[i]==‘+‘||x[i]==‘-‘){
- while(operators.GetTop()!=‘(‘&&operators.GetTop()!=0){
- ans+=operators.pop();
- ans+=‘ ‘;
- }
- operators.push(x[i]);
- }
- }
- while(operators.GetTop()!=0){
- ans+=operators.pop();
- ans+=‘ ‘;
- }
- return ans;
- }
- //随手写的一个将字符串存储的数字转化为整型数的函数 这里就不考虑小数负数的情况了
- long long ToNum(string x){
- int len=x.size();
- long long ans=0;
- for(int i=0;i<len;i++){
- ans+=(x[i]-‘0‘)*pow(10.0,len-i-1)+0.5;//测试了几下pow函数可能会出现浮点数误差就四舍五入来保证精度
- }
- return ans;
- }
- double calculate(string x){
- x=change(x);
- string temp;
- MyStack<double> ans; //为了除法运算方便使用double
- int len=x.size();
- for(int i=0;i<len;i++){
- if(x[i]==‘ ‘)continue;
- if(x[i]>=‘0‘&&x[i]<=‘9‘){
- temp="";
- int j;
- for(j=i;x[j]>=‘0‘&&x[j]<=‘9‘;j++){
- temp+=x[j];
- }
- i=j-1;
- ans.push(ToNum(temp));
- }
- else{
- double a=ans.pop(),b=ans.pop();//对于减和乘需要考虑运算顺序所以额外定义两个变量
- switch(x[i]){
- case ‘+‘:{ans.push(a+b);break;}
- case ‘-‘:{ans.push(b-a);break;}
- case ‘*‘:{ans.push(a*b);break;}
- case ‘/‘:{ans.push(b/a);break;}
- }
- }
- }
- return ans.pop();
- }
- int main(){
- string x;
- cout<<"Please input data."<<endl<<"And don‘t use chinese brackets and don‘t input negative and decimals and space."<<endl;
- while(cin>>x){
- cout<<endl<<"answer = "<<calculate(x)<<endl<<endl<<"continue to input or input ctrl+z to break."<<endl;
- }
- }