咱们今天广泛使用计算器,在初级的计算器中,因为计算机可没有人那么聪明,很难可以准确得判断运算的优先级,因此在写计算机的计算器的时候,咱们须要将得到的四则运算的表达式改写为逆波兰式,方便计算机进行运算。ios
所谓的逆波兰表示法(Reverse Polish notation, RPN, 或者逆波兰记法),这是一种数学表达式方式,在逆波兰记发中,全部操做符置于操做数的后面,所以也被称为后缀表示法。算法
就像是树的搜索方式,有前序、中序、后序遍历,在一棵树中,咱们将操做符放在节点处,将操做数放在叶子处,优先级越高的操做越靠下,咱们普通四则运算的表达式就是使用中序遍历获得的式子,而逆波兰式则是经过后序遍历获得的,举个例子:中缀表达式(a+b)*c-(a+b)/e的逆波兰式是ab+c*ab+e/-。学习
要使用逆波兰式进行运算,首先咱们须要知道如何将一个普通的四则运算表达式转换为一个标准的逆波兰式,在算法书上都有讲的,也已是一个很成熟,很方便的算法了。spa
在理解逆波兰式的时候,咱们使用了树的中序遍历辅助咱们理解,可是在正式使用的时候,你们千万不要想着先将普通的四则表达式生成一棵树,而后再进行后序遍历生成逆波兰式,这是二逼作的事哈。我帮你们找了一个很简洁,很易懂的哈:code
Step 1:咱们使用两个栈构建逆波兰式,栈S1用于临时储存运算符号,运算符在栈内遵循越往栈顶优先级越高的原则;栈S2用于输入逆波兰式。因为咱们须要判断式子是否读完,因此先在S1内放入一个优先级最低的运算符,你们随意哈;orm
Step 2:从表达式的左端开始逐个读取字符x,进行以下步骤:get
一、 若x是1到9内的数,那么继续读,直到读出完整的操做数,而后将操做数压入栈S2;数学
二、 若x是运算符,则分状况讨论:string
若x是'(',则直接压入栈s1;it
若x是')',则将距离栈s1栈顶的最近的'('之间的运算符,逐个出栈,依次压入栈s2,此时抛弃'(';
若x是除'('和')'外的运算符,则再分以下状况讨论:
若当前栈s1的栈顶元素为'(',则将x直接压入栈s1;
若当前栈s1的栈顶元素不为'(',则将x与栈s1的栈顶元素比较,若x的优先级大于栈s1栈顶运算符优先级,则将x直接压入栈s1。否者,将栈s1的栈顶运算符弹出,压入栈s2中,直到栈s1的栈顶运算符优先级别低于(不包括等于)x的优先级,或栈s2的栈顶运算符为'(',此时再则将x压入栈s1;
Step 3:在进行完(2)后,检查栈s1是否为空,若不为空,则将栈中元素依次弹出并压入栈s2中(不包括最末尾的标志符);
Step 4:完成上述步骤后,栈s2便为逆波兰式输出结果。可是栈s2应作一下逆序处理,由于此时表达式的首字符位于栈底;
能顺利生成逆波兰式得话基本就大功告成了,相对于生成波兰式,使用波兰式计算那就是再简单不过的了,建议使用一个栈存取操做数,而后顺序读就能够了,在下面的代码中,有实现,你们能够看一下。
这里附上作练习时写的一个计算器的代码,逆波兰式的练习哈:
#include<iostream> #include<stack> using namespace std; class Calculator { // 整数的加减乘除运算器 private: string Polish; long int result; public: Calculator() { } long int& getResult(string exp) { Polish.clear(); stack<char> s1,s2; s1.push('#'); //以#号做为标记 char temp; for(int i=0; i<exp.length(); i++) { //利用两个堆栈生成逆波兰式 if (exp[i] >= '0' && exp[i] <= '9') { s2.push(exp[i]); } else if (exp[i] == '+' || exp[i] == '-' ||exp[i] == '*' ||exp[i] == '/') { s2.push('#'); temp = s1.top(); if(temp == '#'||(temp == '+' || temp == '-')&&(exp[i] == '*' ||exp[i] == '/')) { s1.push(exp[i]); } else { while (!(temp=='#'||temp == '(' || (temp == '+' || temp == '-') && (exp[i] == '*' ||exp[i] == '/'))) { s2.push(temp); s1.pop(); temp = s1.top(); } s1.push(exp[i]); } } else if(exp[i] == ')'|| exp[i] == '(') { if(exp[i] == '(') { s1.push(exp[i]); } else { temp = s1.top(); while (temp != '(' ) { s2.push(temp); s1.pop(); temp = s1.top(); } s1.pop(); } } } while(s1.top()!='#') { s2.push(s1.top()); s1.pop(); } for(;s2.size()>=1;) { Polish.push_back(s2.top()); s2.pop(); } long int temp2; long int temp3; long int temp1=0; stack<long int> s3; bool sign =0; for(int i = Polish.length()-1; i >= 0; i--) { //进行逆波兰式的运算 if(Polish[i] >= '0' && Polish[i] <= '9') { temp1 = temp1*10 + Polish[i]-'0'; sign = 1; } if((Polish[i] == '#'||Polish[i] == '+'||Polish[i] == '-'||Polish[i] == '*'||Polish[i] == '/')&&(sign == 1)||(i == 0&&sign == 1)) { if(sign == 1){ s3.push(temp1); temp1 = 0; sign = 0; } } if(Polish[i] == '+'||Polish[i] == '-'||Polish[i] == '*'||Polish[i] == '/') { temp2 =s3.top(); s3.pop(); temp3 =s3.top(); s3.pop(); switch(Polish[i]) { case '+': s3.push(temp3+temp2);break; case '-': s3.push(temp3-temp2);break; case '*': s3.push(temp3*temp2);break; case '/': s3.push(temp3/temp2);break; } } } result = s3.top(); return result; } }; int main() { Calculator c; //对各类极端状况进行验证 cout<<c.getResult("3")<<endl; cout<<c.getResult("((3+4)*5+6)*7")<<endl; cout<<c.getResult("1+2*3")<<endl; cout<<c.getResult("2*5+10")<<endl; cout<<c.getResult("3*(5+4)")<<endl; cout<<c.getResult("(4+5)*(2+2)")<<endl; cout<<c.getResult("1/2+1/2")<<endl; cout<<c.getResult("0+0")<<endl; cout<<c.getResult("4*5-7*8")<<endl; cout<<c.getResult("378+456-500*12/2")<<endl; cout<<c.getResult("((11*(12+13)*(14+15))+(16+17))*(18+19)")<<endl; cout<<c.getResult("4+5")<<endl; cout<<c.getResult("4")<<endl; cout<<c.getResult("77+44-22*33/11")<<endl; return 0; }
一点点学习心得:也许你们都不太喜欢研究这些看似比较脑残的东西哈,可是,亲身写了以后,我以为这货真是考验一我的的思惟的清晰程度,并且,若是仅仅是看算法,我以为不论背得多熟,也老是云里雾里的,仍是须要本身动手写一写,感觉感觉。反正代码嘛,多写写总没什么坏处,特别是这种特别绕人的东西,写了后再看其它的算法,理解起来事半功倍哈。