用栈计算中缀表达式(具体详细,还包含getchar的用法)

标题## 用栈计算中缀表达式

中缀表达式也就是我们常说的普通数学表达式,那么怎么用栈来计算呢?请听我一一分解。
我们基本的思路是用两个栈来分别存储表达式中的数字和运算符,在通过有序的出栈来进行计算。(我用OPTR代表运算符栈,OPND代表运算数字栈)
方便判断表达式的结束,我会在运算符栈第一个元素和表达式后面加一个#
在此之前,先要把基础的功能函数写出来
1.判断是数字还是运算符

//判断计算表达式中下一个字符是数字还是运算符 ,返回0是代表运算数字,返回1代表运算符号 
int in(char ch)
{
	switch(ch)
	{
		case '0': case '1': case '2': case '3': case '4':
	  	case '5': case '6': case '7': case '8': case '9':
	  		return 1;
	  		break;
	  	default: 
	  		return 0;
	}
}

2.比较运算符优先级的函数(这个是最关键的)
在给出代码之前,我先给一张对比表给大家看看
在这里插入图片描述
图片中的θ2表示运算符栈的栈顶元素,θ1表示读取到的运算符
大于号表示θ2的优先级高于θ1,小于就相反,等于就优先级相等
(下面代码我就用1,0,-1依次代表大于,等于,小于)

int precede(char topch,char ch)
{
	if(topch=='+'||topch=='-')
	{
		if(ch=='+'||ch=='-'||ch=='#'||ch==')')
		{
			return 1; 
		}
		else
		{
			return -1;
		} 
	}
	else if(topch=='*'||topch=='/')
	{
		if(ch=='(')
		{
			return -1;
		}
		else
		{
			return 1;
		} 
	}
	else if(topch=='(')
	{
		if(ch==')')
		{
			return 0;
		}
		else
		{
			return -1;
		}
	}
	else if(topch=='#')
	{
		if(ch=='#')
		{
			return 0;
		}
		else
		{
			return -1;
		}
	}
	else
	{
		return 2;
	}
	
} 

3.计算的函数

int operate(int num1,int num2,char operators)
{
	int result;
	switch(operators)
	{
		case '+': result=num1+num2; break;
		case '-': result=num1-num2; break;
		case '*': result=num1*num2; break;
		case '/': result=num1/num2; break;
	}
	return result;
} 

上面的基本函数给出来了,我们就来分析里面的具体逻辑吧

因为对数字的操作较简单,我先解决它:
(因为得到的都是字符型,所以需要减去一个’\0’来变成整形)
如果数字仅仅是一位数,那么就直接入栈就行了。
如果是两位以及更高位那么就需要把他拼接起来,之后就直接入栈,所对应的代码:

		if(in(ch))
		{
			sum=ch-'0';
			printf("******%d******\n",sum);
			ch=getchar();
			while(in(ch))
			{
				sum=sum*10+(ch-'0');
				printf("******%d******\n",sum);
				ch=getchar();
			}
			OPND=stack1push(OPND,sum);
		}

数字解决完了,下面我们来解决运算符:
当读到的时字符时,我们先取运算符栈里的元素用来和读到的字符作比较
(1)如果返回的是-1(就代表栈顶元素优先级低于读取到的字符),我们就把读取到的字符入栈并且再执行一次 ch=getchar()

case -1:
	OPTR=stackpush(OPTR,ch);
	ch=getchar();
	break;

(2)如果返回的是0(就代表栈顶元素优先级等于读取到的字符),我们就把运算符栈的栈顶元素出栈并且再执行一次 ch=getchar()

case 0:
	x=stackpop(OPTR);
	ch=getchar();
	break;

(3)如果返回的是1(就代表栈顶元素优先级高于读取到的字符),我们就做运算:
运算符栈出一次栈,运算数字就出两次栈(两次出栈顺序要相反,因为出栈顺序是和入栈顺序相反的),把得到的结果压入运算数字栈内
并且不需要做ch=getchar()(下面我会解释)

case 1:
	theta=stackpop(OPTR);
	b=stack1pop(OPND);
	a=stack1pop(OPND);
	OPND=stack1push(OPND,operate(a,b,theta));
	printfstack1(OPND);
	break;

为什么返回1是要做ch=getchar(),而返回-1和0时不需要?
答:当返回1时有一种情况时读取到的是 ‘)’ ,此时如果还读取一次,运算符栈内的 ‘(’ 就不会出栈,后面的一系列运算就会出问题。当不是这种情况时,不读取也能够正常运算
所以不需要读取!!!

到此运算数字和运算符都搞定了,下面就是一些小问题(虽说是小问题,但一不小心就会很麻烦,本人亲测有效。。。)
(1)关于getchar的问题,先看一张图片
在这里插入图片描述
getchar 是从键盘上来读取我们的数据,但它们不是直接从键盘上来读取我们的数据。它们和键盘之间有一个区域叫缓冲区。
输入函数先来看缓冲区中是否有数据,如果有,它直接就拿走了,不需要从键盘输入,如果缓冲区什么都没有,则需要从键盘输入,再拿走。
比如说我们前面输入了一些需要的数字再来输入我们的表达式,在缓冲区就会有回车字符,当遇到getchar时,就会被读取,就导致读不到我们需要它读的字符。所以我们在此之前要先把缓冲区里清空在来进行我们的表达式输入

//把缓冲区中的内容全读走
	while ( getchar() != '\n')
	{
		;
	}

写上以上一段代码,妈妈再也不用担心getchar的问题了

(2)关于循环该采用怎样的型式以及循环里面的条件的问题
(这个问题一不小心就会导致全盘皆崩,请千万要小心处理)

do
{

}while(ch!='#'||getpop(OPTR)!='#');

这里我采用的时do–while循环,为什么呢?我来解释一下:
不论是 ch!=‘#’ 还是 getpop(OPTR)!=‘#’ ,都有另一个还没有进行完的情况就是:
(1)当 ch!=‘#’ 时,就代表已经读完了表达式,而此时运算符栈里面可能还有运算符没有做完,就还需要做进行一次运算
(2)当getpop(OPTR)!=‘#’ ,可能表达式还没读完,这种情况就比较简单了,就不解释了

到此,小毛病都解决的差不多了。下面我把完整的代码给出来

#include<string.h>
#include"function.cpp"
#include"charStack.cpp" 
#include"intStack.cpp"
int main()
{
	//printf("输入一个表达式(以#结尾):");
	//定义一个操作符栈 
	charstack *OPTR;
	OPTR=stackinit(OPTR);
	OPTR=stackpush(OPTR,'#');
	
	//定义一个操作数栈 
	intstack *OPND; 
	OPND=stack1init(OPND);
	
	//定义一些相关的变量
	char topch,ch,x,theta;
	int sum,a,b,result;
	while(ch=getchar()!='\n')
	{
		;
	} 
	printf("输入一个表达式(以#结尾):");
	ch=getchar();
	//开始循环
	do
	{
		if(in(ch))
		{
			sum=ch-'0';
			printf("******%d******\n",sum);
			ch=getchar();
			while(in(ch))
			{
				sum=sum*10+(ch-'0');
				printf("******%d******\n",sum);
				ch=getchar();
			}
			OPND=stack1push(OPND,sum);
			printfstack1(OPND);
		}
		else
		{
			topch=getpop(OPTR);
			switch(precede(topch,ch))
			{
				case -1:
					OPTR=stackpush(OPTR,ch);
					printfstack(OPTR);
					ch=getchar();
				break;
				case 0:
					x=stackpop(OPTR);
					printfstack(OPTR);
					ch=getchar();
				break;
				case 1:
					theta=stackpop(OPTR);
					b=stack1pop(OPND);
					a=stack1pop(OPND);
					OPND=stack1push(OPND,operate(a,b,theta));
					printfstack1(OPND);
				break;
			}
		}
	}while(ch!='#'||getpop(OPTR)!='#');
	result=stack1pop(OPND);
	printf("表达式的结果为:%d",result);
	return 0;
}

其中function.cpp包含的是上面说过的基本函数
charStack.cpp和intStack.cpp 包含的是栈了一些基本运算,这里我就不过多介绍了,因为每个人都有自己的独特定义方式
最后,在此声明,本人写这个是把自己在编码过程中遇到的问题做一个总结,希望也能帮助到遇到困难的你。
如果此文章有什么不恰当的地方,希望大家在评论区留言,让我们一起来讨论一下。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值