1.中缀计算器

要求:编写一个程序,由于求四则运算中缀表达式的值。

 

所谓“四则运算中缀表达式”,就是四则混合运算算式,比如9+7*(3-2),3*6-(4+8),

由于运算符号放在运算数值中间,所以被称为中缀表达式。

 

一、思路:

输入的字符串先用字符数组equ[1000]存放起来。扫描下去直到equ[n]=NULL为止

很自然,我们要解决如下两个大问题:

 

1.正确读取字符串里面的数字和运算符

比如13+95*2,你要能成功识别数字吧,能成功分理出数字13,95,2和运算符+和*

这个问题比较简单,编写函数digits来实现。细节不讲了

 

2.按照优先级进行运算

首先分成两小类:括号优先级,运算符优先级。其中括号优先级十分简单,说这个

(1)括号优先级

假设我们已经编写了一个函数operation,能正确计算算式,下面有算式8-(7+6*2)+7*5

算式 8-(7+6*2)+7*5

operation(0):8-(7+6*2)+7*5
               ↑     
           
①扫描到'('处,operation递归,调用一新的operation(1)函数;继续扫描字符串并计算
operation(1):7+6*2)+7*5

②扫描到')'处:此时operation(1)计算完7+6*2的内容,计算结果=7+6*2=19
检测到')',operation(1)执行完成,返回运算结果给operation(0),得到:

operation(0):8-19+7*5
                 ↑

(2)运算符号优先级

这是这个计算器的核心算法。

想像一下,如果算式全都是相同优先级的运算符号,比如加和减,完全可以从左一路运算到右

算式7+8-9+4,边扫描边根据符号加减,是没有问题的,7+8=15,15-9=6,6+4=10

但是优先级不同了,多了个乘或除,再这样运算就会出错了

把算式改成7+8*9+4,再根据符号从左到右运算,就会变成7+8=15,15*9=145,145+4=149

 

既然如此,我们干脆先把数值和符号都先分别放到链栈p和q存起来

为了方便出栈,链栈p和q要为          栈顶->栈底

压栈运算符优先级=in_push,q栈顶运算符优先级=top

①in_push<top,说明前一步运算的优先级大;所以q栈的元素要出栈,同时弹出两个数字进行运算,运算后再压回去。通过这步运算,7+9*6-4可以化成7+54-4

in_push=top,我们也不能直接压栈,而是先把同级运算符先弹出运算,直到满足③或q栈空才能再压入。

为什么?因为算式是从左往右运算的,如果同级运算符直接压栈,到时候扫描完成,弹栈运算的时候,相当于从右往左计算,如下图:

8-7+6

先压再运算(错误):
由于同级,扫描完字符串后可以得到
p:6→7→8     q:+→-
先弹出6和7及运算符+,计算6+7=15,压栈得:
p:15→8    q:-
再弹出15和8,及符号-,计算得8-15=-7

先运算再压栈(正确):
检测到‘+’时,先计算8-7=1,然后1,+分别压入p和q栈
字符串扫描完成,有:p:6→1, q:+
弹出并运算得6+1=7

③in_push>top,直接压栈,这个不必多说了

 

具体思路就讲完了,当然,除了基本的四则运算,我们也可以添加运算,如取余,幂次,开方,对数,三角函数等等。

只要设置好相应的运算优先级即可

 

二、示例演示

以算式9+7*8-3为例,演示一遍计算器具体操作:

算式:9+7*8-13    p栈:数值    q栈:操作符
 
①9+7*8-13    9入栈    
 ↑           p:9        q:null

②9+7*8-13    ‘+’入栈
  ↑          p:9        q:+

③9+7*8-13    7入栈
   ↑         p:7→9      q:+

④9+7*8-13    ‘*’优先级大于q栈顶元素‘+’,‘+’入栈
    ↑        p:7→9      q:*→+

⑤9+7*8-13    8入栈
     ↑       p:8→7→9    q:*→+

⑥9+7*8-13    ‘-’优先级小于‘*’,弹出‘*’,及p栈顶两数8,7计算。得8*7=56,将56压栈
      ↑      p:56→9     q:+

⑦9+7*8-13    ‘-’优先级等于‘+’,弹出‘+’,及p栈顶两数56,9计算。得56+9=65,将65压栈
      ↑      p:65       q:

⑧9+7*8-13    q栈空,‘-’压栈
      ↑      p:65       q:-

⑨9+7*8-13    13入栈
       ↑     p:13→65    q:-

⑩9+7*8-13    字符串扫描完成,将q里面的运算符一次弹出运算(每次q弹1个p弹2个)
             p弹出13,65 q弹出‘-’号,计算65-13=52
             压栈:p:52

结果:p栈的值52即为最终答案  

 

 

三、代码

#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<windows.h>

#define newp (node*)malloc(sizeof(node))

typedef struct stack{
    double val;//存放运算数值
	char ope;//存放运算符 
	int pri;//运算符优先级 
    struct stack *next;
}node;  //建立栈类型 

void gotoxy(int x, int y){
    COORD pos;//表示一个字符在控制台屏幕上的坐标
    pos.X = x;
    pos.Y = y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);//是API中定位光标位置的函数。
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
	CONSOLE_CURSOR_INFO cci;
	GetConsoleCursorInfo(hOut,&cci);//获取光标信息
	cci.bVisible = FALSE;//隐藏光标
	SetConsoleCursorInfo(hOut,&cci);//设置光标信息
}//光标控制模块

int digits(char equ[1000],int scan[1]){
	int i=0,n,num=0; 
	while(equ[scan[0]+i]>='0' && equ[scan[0]+i]<='9'){
		++i;
	}
	n=i;
	while(i>0){
		num+=(equ[scan[0]+(n-i)]-48)*pow(10,i-1);
		--i;
	}
	scan[0]+=n-1;
	return num;
}//数值提取器 

bool priority(int push,int top){//压栈操作符和s_ope栈顶操作符的优先级 
	if(push>top){
		return 1;} 
	return 0;
}//优先级判断器 

void cal(node *s,char ope){
	gotoxy(0,22);
	if(ope=='+') s->next->val+=s->val;
	if(ope=='-') s->next->val-=s->val;
	if(ope=='*') s->next->val*=s->val;
	if(ope=='/'){if(s->val!=0)s->next->val=s->next->val/s->val;
		else printf("[运算错误:不可以除0!]");}
	if(ope=='%'){if(s->val==0) printf("[运算错误:不可以对0取模!]");
		if((int)s->val+(int)s->next->val!=s->val+s->next->val) printf("[运算错误:小数间不可取余!]");
		else s->next->val=(int)s->next->val%(int)s->val;
	}
	if(ope=='^'){s->next->val=pow(s->next->val,s->val);}
	if(ope=='@'){if(s->val<0)printf("[运算错误:负数无法开根!]");
		else s->val=sqrt(s->val);printf("(%d %d)",s->val,sqrt(s->val));
	}
} //运算器 

int leve(char t){
	if(t=='+' || t=='-') return 1;
	if(t=='*' || t=='/' || t=='%') return 2;
	if(t=='^' || t=='@') return 3; 
} 

double operation(char equ[1000],int scan[1]){
	char oper[]={'+','-','*','/','^','@','%'};
	++scan[0]; 
	node *s_val=newp,*s_ope=newp;//数值链栈,操作符链栈 
	s_val->next=NULL,s_ope->next=NULL;//下一个节点设为空 
	s_val->val=0;s_ope->ope='#';s_ope->pri=-1;//设置栈底元素 
	while(equ[scan[0]]!=NULL && scan[0]<1000 && equ[scan[0]]!=')'){
		char e=equ[scan[0]];
		if(e=='('){
			node *s_n=newp;s_n->next=s_val;s_n->val=operation(equ,scan);s_val=s_n;
		} 
		if(e>='0' && e<='9'){
			node *s_n=newp;s_n->next=s_val;s_n->val=digits(equ,scan);s_val=s_n;
		}
		int key=0;
		for(int i=0;i<7;++i){
			if(e==oper[i]){key=1;}
		}
		if(key==1){
			int Leve=leve(equ[scan[0]]);
			while(priority(Leve,s_ope->pri)==0){
				cal(s_val,s_ope->ope); s_val=s_val->next;s_ope=s_ope->next;
			}//若压栈运算符优先级不大于栈顶元素,一直出栈,并运算,直到能压入栈为止 
			node *s_o=newp;s_o->next=s_ope;s_o->ope=equ[scan[0]];s_o->pri=Leve;s_ope=s_o;	
		} 
		++scan[0];
	}
	while(s_ope->ope!='#'){
		cal(s_val,s_ope->ope);s_val=s_val->next;s_ope=s_ope->next;
	}
	return s_val->val;
}//字符翻译兼运行中心 

int main(){
	int move=0,scan[1];
	printf("\n*****************************************************************\n");
	printf("中缀计算器 \n");
	printf("1.运算符中间必须有运算数值或括号\n");
	printf("2.注意算式输入的正确性\n");
	printf("3.运算范围:+-2^31\n");
	printf("4.输入字符串的字符数量要小于1000\n"); 
	printf("5.仅支持非负整数输入!小数用整数相除的方式输入,负数用0-n的方式输入\n");
	printf("小数1.72表示为172/100,负数-2.7表示为0-27/10\n");
	printf("--------------------------------------------------------------------\n");
	printf("运算符优先级\n");
	printf("1级:+(加) -(减)\n2级:*(乘) /(除) %%(取余)\n3级:^(幂) @(开方)\n最高级:左括号‘(’+ 右括号‘)’\n"); 
	printf("*******************************************************************\n\n");
	while(move==0){
		gotoxy(0,20); 
		printf("输入算式:");
		char equ[1000]={0};
		scan[0]=-1;
		scanf("%s",&equ);
		double total=operation(equ,scan);
		gotoxy(0,21);printf("\n结果:%lf 输入'0'继续",total);scanf("%d",&move);
		gotoxy(0,20); printf("输入算式:                                                                        ");
		gotoxy(0,22);printf("                                                                                 ");
	} 
	return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值