制作计算器,包含后缀表达式的求法

/*STL 中栈的使用方法(stack)
基本操作:
#include <stack>
stack <char> s
s.push(x) 将x加入栈中,即入栈操作
s.pop() 出栈操作(删除栈顶),只是出栈,没有返回值
s.top() 返回第一个元素(栈顶元素)
s.size() 返回栈中的元素个数
s.empty() 当栈为空时,返回 true
*/

//下边为百度百科给的算法详细解释

/*将一个普通的中序表达式转换为逆波兰表达式的一般算法是:
首先需要分配2个栈,一个作为临时存储运算符的栈S1(含一个结束符号),一个作为输入逆波兰式的栈S2(空栈),
S1栈可先放入优先级最低的运算符#,注意,中缀式应以此最低优先级的运算符结束。
可指定其他字符,不一定非#不可。从中缀式的左端开始取字符,逐序进行如下步骤:
(1)若取出的字符是操作数,则分析出完整的运算数,该操作数直接送入S2栈
(2)若取出的字符是运算符,则将该运算符与S1栈栈顶元素比较,
如果该运算符优先级大于S1栈栈顶运算符优先级,则将该运算符进S1栈,
否则,将S1栈的栈顶运算符弹出,送入S2栈中,直至S1栈栈顶运算符低于(不包括等于)该运算符优先级,
最后将该运算符送入S1栈。
(3)若取出的字符是“(”,则直接送入S1栈顶。
(4)若取出的字符是“)”,则将距离S1栈栈顶最近的“(”之间的运算符,逐个出栈,依次送入S2栈,此时抛弃“(”。
(5)重复上面的1~4步,直至处理完所有的输入字符
(6)若取出的字符是“#”,则将S1栈内所有运算符(不包括“#”),逐个出栈,依次送入S2栈。
完成以上步骤,S2栈便为逆波兰式输出结果。不过S2应做一下逆序处理。便可以按照逆波兰式的计算方法计算了!*/

/*计算方法:
新建一个表达式, 如果当前字符为变量或者为数字,则压栈,如果是运算符,
则将栈顶两个元素弹出作相应运算,结果再入栈,最后当表达式扫描完后,栈里的就是结果。*/

/*我的算法理解:
						第一大步:把中缀表达式转化为后缀表达式。
扫中缀式:
1.数字:直接取出进数组ep
2.左括号:进栈op
3.右括号:从栈顶扫栈op,若不是左括号就出栈,加到数组ep中,直到出现左括号,把左括号出栈
4.等号:将栈op中除'#'的元素全部输出
5.运算符:比较栈顶与当前运算符优先级,在栈顶优先级高于当前运算符优先级时,栈顶不断出栈,
加到数组ep中,否则,跳出循环,把当前运算符入栈。
						第二大步:根据中缀表达式求值。
1.遇到数字:直接进栈num
2.遇到运算符:对栈顶的两个元素进行出栈并运算,结果入栈num
3.遇到空格:自加,continue
4.遇到回车:直接跳出结束。
*/


//输入要求:可以含有数字,小数点,运算符,以等号结束,最后加回车,不含有空格
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <stack>
#define N 1000

using namespace std;

stack <char> op;//存字符的栈op
stack <double> num;//存储double数据的栈
int i,len,j;
char ch, str[N],ep[N];//str数组存储输入的字符串,ep数组用于存储后缀表达式

double calc(double a, char o, double b)//具体两个元素的计算
{
	switch (o)
	{
	case '+': return a + b; 
	case '-': return a - b; 
	case '*': return a * b; 
	case '/': return a / b; 
	case '^': return pow(a,b); 
	}
}

int pri(char ch)//根据符号返回优先级
{
	switch (ch)
	{
	case '#': return -1;
	case '+':
	case '-': return 1;
	case '*':
	case '/': return 2;
	case '^': return 3;
	case '(': return 0;
	}
}

void getnum()//把str中的数字串存到ep中
{
	while (((str[i] >= '0' && str[i] <= '9') || str[i] == '.') && i < len)
		ep[j++] = str[i++];
	ep[j++] = ' ';
	return;
}

double getnum2()//把ep中的一个数字以double型返回出来
{
	j = 0;
	char s[N];
	while (((ep[i] >= '0' && ep[i] <= '9') || ep[i] == '.') && i < len)
		s[j++] = ep[i++];
	s[j] = '\0';
	return atof(s);
}

void back()//op栈顶元素出栈,并进入ep中
{
	char c = op.top(); op.pop();
	ep[j++] = c;
	ep[j++] = ' ';
	return;
}

void trans()//求后缀表达式
{
	while (i < len)
	{
		if ((str[i] >= '0' && str[i] <= '9'))//如果为数字
		{
			getnum();
			continue;
		}
		switch (str[i])
		{
		case '(':	op.push(str[i]); i++; break;//左括号的进栈
		case ')'://遇到右括号,把op栈中所有符号赋给数组ep,直到遇到左括号为止
			while (op.top() != '(')
				back();
			i++;
			op.pop();
			break;
		case '='://遇到等号将栈op中剩余符号放入ep中,除'#'外;
			while (op.top() != '#')
				back();
			i++;
			break;
		default ://遇到运算符,先把栈中比当前优先级高的出栈并放入ep中,
				 //直至遇到优先级较低的,把当前元素进栈
			while (pri(str[i]) <= pri(op.top()))
				back();
			op.push(str[i]);
			i++;
			break;
		}
	}
}

double cal()
{
	while (i < len)
	{
		if (ep[i] == ' ')//空格
		{
			i++;
			continue;
		}
		if (ep[i] == '\n')//换行
		{
			return num.top();
		}
		if (ep[i] >= '0' && ep[i] <= '9')//数字
		{
			num.push(getnum2());
			i++;
		}
		else//运算符
		{
			double b = num.top(); num.pop();
			double a = num.top(); num.pop();//把num栈顶连个元素出栈
			num.push(calc(a, ep[i], b));//把计算结果入栈num
			i++;
		}
	}
}

int main()
{
	i = 0;
	while(scanf("%c", &ch) && ch!='\n')//输入字符串存入str
		str[i++] = ch;
	len = i;//记录数组str长度
	printf("原表达式为:  ");
	for (int k = 0; k < len-1; k++)
		printf("%c", str[k]);
	printf("\n");
	i = 0;
	j = 0;
	while (!op.empty())	op.pop();
	while (!num.empty())	num.pop();
	op.push('#');//初始化i,j和栈op,num
	trans();
	j--;
	ep[j] = '\n';//把数组ep最后一位的空格换回回车
	printf("逆波兰式为:  ");
	for (int k = 0; k <= j; k++)//遍历输出ep,即为逆波兰式
		printf("%c", ep[k]);
	//求出后缀表达式之后开始对其计算求值,下边先初始化变量
	len = j+1;//数组ep的长度为len
	i = 0;
	j = 0;
	printf("最终结果为:  %.6lf\n", cal());
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值