目录
1.需求分析
2.主要难点——逆波兰算法
2.1 中缀表达式转换为后缀表达式
2.2 后缀表达式的计算
3.编程实现
3.1 C/C++版本
3.2 Qt版本
4.参考资料
编程实现一个科学计算器(类似于Windows自带的计算器),要求能够实现加减乘除混合运算,并且能够识别括号,优先级正确。
下面是本博客的Qt版本的计算器效果图
我们日常所用的数学表达式(如5+3)都是中缀表达式,中缀表达式是人容易理解的表达式。后缀表达式又叫做逆波兰表达式,对计算机来说,计算中缀表达式是很困难的,但是计算后缀表达式却非常容易,所以我们先把中缀表达式转化成后缀表达式来计算。下面的动态图和算法流程图可以很好的演示整个转换的过程:
图1 中缀表达式转后缀表达式的动态图
图2 中缀表达式转后缀表达式的流程图
后缀表达式的计算是比较简单的,基本思路就是遇到操作符就将操作数出栈并根据操作符进行计算,并将结果进栈,如果没有遇到操作符,就直接将操作数进栈。下图是具体的流程图,注意下面的代码使用'\0'作为表达式的终止符号(本人偷懒使用了别人的流程图)
图3 后缀表达式的计算---动态图
图4 后缀表达式的计算----流程图
在编程的时候,刚开始准备使用C语言,编程的过程中发现,在中缀转后缀表达式的时候需要一个存放字符型元素的栈,而后缀表达式的计算中又需要一个存放double型元素的栈,这样一来,就需要分别编写两个栈,很麻烦。下面的代码是直接用C++的类模板来实现的,程序比C语言简单很多,当然纯粹用C语言肯定也是没有任何问题的。
实际编程中还需要考虑正负号,因为‘+’和‘-’有时候不表示加减,为了解决这个问题,可以将负数-a看成是0-a,把正数+a看成0+a,这样一来,正负号的问题也解决了。
(1)类的声明 calculator.h
#ifndef CALCULATOR_H
#define CALCULATOR_H
enum MAXSIZE
{
STACK_INIT_SIZE=20,//定义初始最大容量
STACKINCREMENT=10,//栈满的时候,动态增加容量,每次增加10个元素空间
MAXBUFFER=10,//最大缓冲区
MAX_EXP_LEN=100//表达式最长为100
};
template
class Calculator
{
public:
struct sqStack
{
ElemType *base;//指向栈顶
ElemType *top;
int stackSize;//当前栈的最大容量
};
Calculator();
~Calculator();
void Push(ElemType e);
bool Pop(ElemType &e);
void clearStack();
int StackLen();
int Calculation(char Postfix[]);//后缀表达式的计算
bool Infix2Postfix(char Infix[],char Postfix[]);//中缀表达式变为后缀表达式
private:
sqStack s;
};
#endif // CALCULATOR_H
(2)类的实现 calculator.cpp
#include "calculator.h"
#include
template
Calculator::Calculator()
{
s.base=new ElemType[STACK_INIT_SIZE];//栈底指向申请空间的首地址
if(s.base==NULL)//申请失败
exit(0);
s.top=s.base;//top总是指向有效元素的下一个空间(栈顶),top中没有数据
s.stackSize=STACK_INIT_SIZE;
}
//销毁栈,将内存空间释放
template
Calculator::~Calculator()
{
delete []s.base;
}
template
void Calculator::Push(ElemType e)
{
if(s.top-s.base>=s.stackSize)
{
s.base=(ElemType *)realloc(s.base,(s.stackSize+STACKINCREMENT)*sizeof(ElemType));
// realloc是申请一个新的空间,并将旧的内容拷贝到新的空间,还会释放以前的空间
if(s.base==NULL)
exit(0);
s.top=s.base+s.stackSize;//因为重新分配了空间,所以重新设置栈顶
s.stackSize=s.stackSize+STACKINCREMENT;//当前栈的最大容量变大了
}
*(s.top)=e;
s.top++;
}
template
bool Calculator::Pop(ElemType &e)
{
if(s.top==s.base)
return false;//空栈
e=*(--(s.top));
return true;
}
//清空栈,不改变物理空间
template
void Calculator::clearStack()
{
s.top=s.base;
}
//计算栈的当前容量(存储的数据量或者元素个数)
template
int Calculator::StackLen()
{
return s.top-s.base;
}
template
int Calculator::Calculation(char Postfix[])
{
int i=0,j;
char c;
char str[MAXBUFFER];
double a=0,b=0;
for(j=0;Postfix[j]!='\0';j++)
{
// c=Postfix[j];
while ((Postfix[j]>=48)&&(Postfix[j]<=57)||Postfix[j]=='.') //输入的是数字
{
str[i]=Postfix[j];
// printf("str[%d]=%c\n",i,c);
i++;
str[i]='\0';
if(i>=10)
{
printf("出错,输入的数据长度过大!\n");
return -1;
}
// scanf("%c",&c);
j++;
if((Postfix[j]==' '))
{
// str[i]='\0';
// printf("str[%d]=%c\n",i,Postfix[j]);
a=atof(str);
// printf("%f \n",a);
Push(a);
i=0;
}
}
switch (Postfix[j])
{
case '+':
Pop(a);
if(!Pop(b))//防止这是符号位(单目运算符)
{
Push(a);
break;
}
Pop(b);
// printf("%f+%f=%f\n",b,a,b+a);
Push(b+a);
break;
case '-':
Pop(a);
if(!Pop(b))//
{
Push(-a);
break;
}
// printf("%f-%f=%f\n",b,a,b-a);
Push(b-a);
break;
case '*':
Pop(a);
Pop(b);
// printf("%f*%f=%f\n",b,a,b*a);
Push(b*a);
break;
case '/':
Pop(a);
if(a==0)
{
printf("除数不能为零 !\n");
return -1;
}
Pop(b);
Push(b/a);
break;
default:
break;
}
}
Pop(a);
return a;
}
template
bool Calculator::Infix2Postfix(char Infix[],char Postfix[])
{
Calculator s;
int i=0,j=0;
char e;
printf("中缀表达式为:");
while (Infix[j]!='\0')
{
while(Infix[j]>='0' && Infix[j]<='9')
{
printf("%c",Infix[j]);
Postfix[i++]=Infix[j];
j++;
if(Infix[j]<'0' || Infix[j]>'9')
{
Postfix[i++]=' ';
printf(" ");
}
}
switch (Infix[j])
{
case ')':
s.Pop(e);
while ('('!=e)
{
printf("%c ",e);
Postfix[i++]=e;
Postfix[i++]=' ';
s.Pop(e);
}
break;
case '+':
case '-':
if(0==s.StackLen())
s.Push(Infix[j]);
else
{
do
{
s.Pop(e);
if('('==e)
{
s.Push(e);
}
else
{
printf("%c ",e);
Postfix[i++]=e;
Postfix[i++]=' ';
}
}while (s.StackLen() && '('!=e);
s.Push(Infix[j]);
}
break;
case '*':
case '/':
case '(':
s.Push(Infix[j]);
break;
case '\0':
break;
default:
printf("\n输入格式错误!\n");
return -1;
}
if('\0'==Infix[j])
break;
j++;
}
while (s.StackLen())
{
s.Pop(e);
printf("%c ",e);
Postfix[i++]=e;
Postfix[i++]=' ';
}
Postfix[i]='\0';
printf("\n");
return true;
}
(3)测试程序 main.cpp
#include
#include "calculator.cpp"
#include
using namespace std;
int main()
{
Calculator cal;
char Infix[MAX_EXP_LEN],Postfix[MAX_EXP_LEN];
gets(Infix);
double sum;
cal.Infix2Postfix(Infix,Postfix);
sum=cal.Calculation(Postfix);
printf("最终计算结果为:%f\n\n",sum);
return 0;
}
(4)测试结果
下面计算8+(6-3)*(-5)+10/2:
Qt实现的科学计算器有较友好的界面,效果图在本文的开头。
Qt的编程比较复杂一些,在这个程序里面不仅添加了界面,而且还增加了清除数据,后退一个数字等功能,用了四个文件来编写程序,基本思路跟上面的C++类似,由于程序太长,这里就不放代码了。我已经将完整的源代码上传,有需要的小伙伴可以前去下载(点击进入下载界面),没有积分的小伙伴可以私聊我。
[1] 流程图来自https://blog.csdn.net/hackerain/article/details/7682891?locationNum=15
[2]中缀表达式转后缀表达式的动态图来自https://www.cnblogs.com/lulipro/p/7450886.html