迟到一个月的算法——四则混合运算

本次专栏文章是写的以前实现的一个算法。

大一结束的时候,我们计科班开展实践周活动,其中有一个内容就是让我们做一个web应用,我当时做的是一个web计算器,当时,我特别想实现四则混合运算的功能,然后回到寝室,想了一个晚上也没有想出一个合适的算法。那个时候,还没怎么接触到堆、栈、队列相关内容。

然后,暑假留校,参加了ACM集训,从中学到了不少东西。

再回过头来,十分钟内就把当时的四则混合运算实现了。

迟到了一个月的算法,但我依旧很兴奋。

题目意思是这样:输入一行字符串,例如:4 + 2 * 5 - 7 / 11 然后直接输出其计算结果。

解题思路:首先需要定义两个栈(至于栈是什么东西,小白就把它想象成一个可以放东西的杯子吧)S1, S2。我们规定,S1用来存放符号,S2用来存放数字。将表达式遍历一下,遇到数字,压进栈S2中,遇到字符,压进栈S1中,但是我们始终保持S1最多只有一个运算符,多余的话,就把之前的拿出来计算一下。碰到乘法、除法这种优先级高的,就先进行运算,然后将结果入栈S2中。

就拿上面这个例子来说吧

依次遍历

1)4是个数字,放进S2中。(S2里面有:4)

2)‘+’是个符号,放进S1中。(S1里面有:‘+’)

3)2是个数字,放进S2中。(S2里面有:4, 2)

4)“*”是个符号,而且优先级高,所以,将前面S2里面的最后一个元素2取出来,与“*”后面那个元素相乘运算,将得到的结果(10)放进S2中。(S2里面有:4, 10)

5)“-”是个符号,而且之前的S1里面已经有了一个符号(“+”),我们把加法运算先给做了,得到结果14放进S2,然后把“-”放进S1里面(S1里面有:“-”, S2里面有:14)

6)7是个数字,放进S2里面。(S2里面有:14, 7)

7)“/”是个符号,优先级高,所以,将除法运算做了,得到的0.64放入S2(S2里面有:14, 0.64)

8)最后,将S2中两个元素按照S1里面的运算符(“-”)进行运算,得到最终结果13.36

附上代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<stack>
#include<cstring>
using namespace std;

int main(){
     std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    //freopen("D:\\input.txt", "r", stdin);
    int i;
    double a, b;
	char c, s[250];
	while(gets(s)&&strcmp(s, "0")!=0){//注意这地方的0千万是双引号而不是单引号,毕竟人家是字符串 
		stack<char> s1;
		stack<double> s2;
		for(i = 0; s[i]; i++){
			if(s[i]>='0'&&s[i]<='9'){
				a = 0;
				while(s[i]>='0'&&s[i]<='9'){
					a = a * 10 + s[i] - '0';
					i++;
				}
				i--;
				s2.push(a);
			}
			else if(s[i]=='+'||s[i]=='-'){
				if(!s1.empty()){
					c = s1.top();
					s1.pop();
					a = s2.top();
					s2.pop();
					b = s2.top();
					s2.pop();
					if(c=='+')	a += b;
					else if(c=='-')	a = b - a;
					s2.push(a);
					s1.push(s[i]);
				}
				else
					s1.push(s[i]);
			}
			else if(s[i]=='*'||s[i]=='/'){
				char ch = s[i];
				b = 0;
				i += 2;
				while(s[i]>='0'&&s[i]<='9'){
					b = b * 10 + s[i] - '0';
					i++;
				}
				i--;
				a = s2.top();
				s2.pop();
				if(ch=='*')	a *= b;
				else if(ch=='/') a /= b;
				s2.push(a); 
			}
		}
		while(!s1.empty()){
			c = s1.top();
			s1.pop();
			a = s2.top();
			s2.pop();
			b = s2.top();
			s2.pop();
			if(c=='+')	a += b;
			else if(c=='-')	a = b - a;
			s2.push(a);
		}
		printf("%.2lf\n", s2.top());
	}

	return 0;
}

不过,很快发现,这种算法有一个缺陷,就是不能计算带括号的表达式。

其实,有一种很成熟的方法专门用来处理表达式,即中缀表达式转后缀表达式,然后进行与少运算。这种方法普适性很强,思路也很清晰,分为两部分,一部分是中缀转后缀,一部分是后缀求值。所以,我推荐这种方法,但是我在这里不再赘述了,因为我看到一个博客讲解的非常清晰,在这里贴出来:C/C++带括号四则运算 - CSDN博客 以及中缀转后缀利用栈将 (中缀表达式) 转换成 (后缀表达式)

关于这道求四则混合运算的原题,杭电OJ有:Problem - 1237 蓝桥杯也有:“蓝桥杯”练习系统

专栏的第一篇正式文章就选择了这样一道以前的题,而且还是道模板题,主要是因为这个问题对我启发很大。很多时候,我们在学习数据结构和算法的时候,都觉得像是在做数学题一样,除了做题,找不到实际应用价值,慢慢地也就感觉很枯燥了。但实际上,很多算法是可以实现难以预估的价值的,除了功能实现以外,对功能的优化也是非常值得研究的问题,在这里,算法就发挥了不可替代的作用。

第一次写这类文章,很多地方可能表述不太清晰,欢迎大家指正,也欢迎大家投稿,一起分享你的ACM历程。

发布于 2017-10-13

文章被以下专栏收录