中科因仑“3+1”工程特种兵精英论坛
标题:
表达式计算器(数据结构)
[打印本页]
作者:
伊海
时间:
2014-11-29 19:38
标题:
表达式计算器(数据结构)
本帖最后由 伊海 于 2014-11-29 19:43 编辑
要求:写一个控制台程序,该程序实现输入一个表达式能够根据表达式计算出相应的结果。
例如:例如:输入字符串"1+2*(3-4)/5=",输出结果:0.6
基本要求:能够对+ - * / 进行运算,能够对优先级进行运算和括号匹配运算。
扩展要求:
1.支持多数据类型输入,如: 0x12F(十六进制)、 12FH(十六进制)、 234O(八进制) 、‘F’(字符) 、0000 1111B(二进制)
2.支持未知数赋值运算 如: x=3 则 3X+5=14
3.支持常用函数运算 如: log、 ln、 sin、 sqrt等;
4.支持大数据计算;
5.支持自定义运算符。
这个题目涉及的东西很多很多,在往后待我慢慢给大家分析。
罗列下:
1.动态双向链表(这是我的算法,或许你可以试试数组,但是这两者的优劣在何处?我希望你能明白),表达式的校对
2.中缀表达式转化为后缀表达式
3.后缀表达式的计算
4.文件读写
5.不同进制的转换
6.大整数算法
(1)从大方向大致列举出这六点,现在我们就一项一项进行搞定。
//我们首先建立一个动态双向链表:
/* 建立一个头结点*/
Formula_head = new node;
init_node(Formula_head);
Formula_head->ch = getchar();
Formula_follow = Formula_head;
/* 从用户读取输入*/
for(;;){
newnode = new node; /* 新增加一个节点来存储输入*/
init_node(newnode); /* 初始化节点数据 */
while(1){ /* 获取输入数据,并且删除空格*/
newnode->ch = getchar();
//cin >> newnode->ch ;
if(' ' != newnode->ch ){
//print_link_data(Formula_follow);
break;
}
}
/* 结束输入*/
if('\n' == newnode->ch){
break;
}
/* 加入节点*/
Formula_follow = add_node(Formula_follow, newnode);
}
//读取表达式之后,需要对表达式进行校对,处理未知数和防止输入不规则表达式(错误表达式):
/* 检查是否是未知数算式*/
if( check_unknow(Formula_head) ){
x_head = Formula_head;
Formula_head = NULL;
X_flag = 1;
continue;
}
/*如果有未知数的处理*/
if(X_flag){
Formula_head = add_x_in_link(Formula_head,x_head);
}
/*检查错误*/
if(check(Formula_head)){
continue;
}
/***************************************************
** 函数名称:check
** 函数功能:扫描链表,检查表达式是否错误
** 入口参数:node *head
** 出口参数:
***************************************************/
int check(node *head)
{
int brackets = 0;
for(; head; head=head->next){
/*连续出现2个运算符,报错*/
if(('+'==head->ch) || ('-'==head->ch) ||
('*'==head->ch) || ('/'==head->ch)){
if(('+'==head->next->ch) || ('-'==head->next->ch) ||
('*'==head->next->ch) || ('/'==head->next->ch)){
cout<<"erro: Consecutive two operators!"<<endl;
return 1;
}
}
/* 括号不匹配,报错*/
if('(' == head->ch){
brackets++;
}
if(')' == head->ch){
brackets--;
if(brackets<0){
cout<<"erro: brackets is not right,please check it out!"<<endl;
return 1;
}
}
}
/* 括号不匹配*/
if(0 != brackets){
cout<<"erro: brackets is not right,please check it out!"<<endl;
return 1;
}
/*没错返回0*/
return 0 ;
}
/***************************************************
** 函数名称:check_unknow
** 函数功能:检查是否为未知数算式
** 入口参数:node *head
** 出口参数:
***************************************************/
int check_unknow(node *head)
{
if(('x'==head->ch) && ('='==head->next->ch) && (NULL!=head->next->next)){
return 1 ;
}
return 0;
}
复制代码
作者:
伊海
时间:
2014-11-29 19:39
本帖最后由 伊海 于 2014-11-29 19:41 编辑
这里将数据结构真正用于实际项目中,达到算法最优、代码最优的效果。
(2)我们应该到了中缀表达式转化为后缀表达式。
这个数据结构在计算器表达式的项目中是非常经典的。这里我就再啰嗦几句,中缀表达式也叫做中缀记法,
如1+2*3便是,因为操作符在中间嘛,所以就叫做中缀表达式。
那么什么叫做后缀表达式,举一反三,即为操作数在后面嘛,那么即是:123*+。
后缀表达式也叫做逆波兰表达式。
中缀表达式转化为后缀表达式,栈的用武之地来了,说到栈,第一个词语就是FILO,
先进后出。中缀表达式转化为后缀表达式也正是用了这一个特点。
/***************************************************
** 函数名称:*mid_to_bk
** 函数功能:中缀转换成后缀表达式
** 入口参数:node *head
** 出口参数:
***************************************************/
node *mid_to_bk(node *head)
{
node *mid = Separate_str(head); /* 处理后的中缀表达式链表*/
node *bk = NULL; /* 后缀表达式头指针*/
node *ptr = bk;
node *oper = NULL; /* 符号堆栈 */
node *newnode = NULL;
node *code_node = NULL; /* 用来记录,保存地址 */
/* 开始转换*/
for(; mid; mid=mid->next){
/* 将节点数据拷贝到新节点去*/
newnode = new node;
init_node(newnode);
copy_node_data(newnode,mid);
/* 如果遇到数字就直接入栈*/
if(NUM == newnode->type){
/*加入链表*/
if(NULL == bk){
ptr = bk = add_node(ptr,newnode);
} else{
ptr = add_node(ptr,newnode);
}
}
/* 如果遇到特殊表达式*/
else if(SP_OPERATOR == newnode->type){
/* 如果前一个运算符也是特殊运算符就要出栈 */
while(oper){
if(SP_OPERATOR == oper->type){
code_node = oper->befor; /* 将这个节点换到后缀表达式里面*/
ptr = add_node(ptr,oper);
oper = code_node;
} else break;
}
oper = add_node(oper,newnode);
}
/*如果遇到普通运算符*/
else if(OPERATOR == newnode->type){
/*'('直接进栈*/
if('(' == newnode->ch){
oper = add_node(oper,newnode);
}
/*')'直接退栈到')'*/
else if(')' == newnode->ch){
while(oper){
if('(' == oper->ch)break;/* 遇到'('退栈结束*/
code_node = oper->befor; /* 将这个节点换到后缀表达式里面*/
ptr = add_node(ptr,oper);
oper = code_node;
}
oper = del_node(oper); /* 删除掉'('符号*/
}
/* 遇到+-全部退栈,直到遇到'(',或者退完为止*/
else if(('+' == newnode->ch) || ('-' == newnode->ch)){
while(oper){
if('(' == oper->ch)break; /* 遇到'('退栈结束*/
code_node = oper->befor; /* 将这个节点换到后缀表达式里面*/
ptr = add_node(ptr,oper);
oper = code_node;
}
oper = add_node(oper,newnode);
}
/* 遇到/* 把特殊运算符全部退栈处理*/
else if(('*' == newnode->ch) || ('/' == newnode->ch)){
while(oper){
if('(' == oper->ch)break; /* 遇到'('和特殊运算符截止*/
if(('+' == oper->ch) || ('-' == oper->ch))break;
code_node = oper->befor; /* 将这个节点换到后缀表达式里面*/
ptr = add_node(ptr,oper);
oper = code_node;
}
oper = add_node(oper,newnode);
}
/*
* 下面这段程序是调试使用
*/
#if 0
cout<<"mid: ";
print_link_data(test);
getchar();
#endif
}
}
/* 把最后留在堆栈里面的东西退出来*/
while(oper){
code_node = oper->befor; /* 将这个节点换到后缀表达式里面*/
ptr = add_node(ptr,oper);
oper = code_node;
}
ptr->next = NULL;
/*
* 下面这段程序是调试使用
*/
#if 0
cout<<"mid: ";
print_link_data(bk);
#endif
return bk;
}
复制代码
作者:
伊海
时间:
2014-11-29 19:40
(3)承接上,我们实现了中缀表达式转化为逆波兰表达式,这节,我们要做的肯定就是怎么样将逆波兰表达式计算出来。
其实这个计算过程是很简单了,如下:
/***************************************************
** 函数名称:calculate
** 函数功能:对后缀表达式进行计算处理
** 入口参数:node *formula
** 出口参数:result[0]
*******************************************
********/
double calculate(node *formula)
{
double result[200];
int i=0;
for(; formula; formula = formula->next){
/*数据再次进堆栈*/
if(NUM == formula->type){
result = formula->num;
i++;
}
//由于前面进行了中缀表达式转化为逆波兰表达式,因此为这里的计算奠定了十足的运算基础。
/*遇到普通符号就直接进行判断并且计算*/
else if(OPERATOR == formula->type){
/*加法计算*/
if('+' == formula->ch){
i--;
result[i-1] = result[i-1]+result;
}
/*减法计算*/
if('-' == formula->ch){
i--;
result[i-1] = result[i-1]-result;
}
/*乘法计算*/
if('*' == formula->ch){
i--;
result[i-1] = result[i-1]*result;
}
/*除法计算*/
if('/' == formula->ch){
i--;
/* 如果除数是0的话提示错误*/
if(0 == result){
cout<<"erro: the divvisor equal zero!Please check is out!"<<endl;
exit(1);
} else{
result[i-1] = result[i-1]/result;
}
}
}
/*遇到特殊符号*/
else{
/*sin*/
if(!strcmp(formula->str,"sin")){ //进行符号匹配,如果是sin运算符,进行项目的运算。这里读者其实可以进行改进
result[i-1] = sin(result[i-1]);
}
/* cos*/
else if(!strcmp(formula->str,"cos")){
result[i-1] = cos(result[i-1]);
}
/* tan*/
else if(!strcmp(formula->str,"tan")){
result[i-1] = tan(result[i-1]);
}
/* log*/
else if(!strcmp(formula->str,"log")){
result[i-1] = log(result[i-1]);
}
/*sqrt*/
else if(!strcmp(formula->str,"sqrt")){
result[i-1] = sqrt(result[i-1]);
}
/*help*/
else if(!strcmp(formula->str,"help")){
r_file() ;
} else{
cout<<"have no this OPERATOR,if you want add it,please add it upon, thank you!\n"<<endl;
exit(1);
}
}
}
return result[0];
}
复制代码
//有些东西,看似很难,那是因为你没有突破心理的障碍,
//当你全身心投入去实践的时候,你会发现,一切都是那么简单并且有趣
欢迎光临 中科因仑“3+1”工程特种兵精英论坛 (http://bbs.enlern.com/)
Powered by Discuz! X3.4