表达式求值(c语言实现)

引入

在这里插入图片描述
在程序运行的时候我们输入上述的一串的表达式,最终可以通过计算机输出这个表达式的一个结果。

而在相关的表达式中的运算符只包括 + - × ÷,当然还有 ( ),通过口算想必一眼就可以得出结果,但如何借助代码实现,这是一个难题。因为我们除了考虑数字与数字之间的运算,还涉及优先级,以及有括号的情况,当然还有一个问题,如何将输入字符串中的数字进行提炼,并进行存储也是一个问题。

我们有幸站在巨人的肩膀上,针对这种问题已经有了很好的提供解决办法。我们常见的表达式就是我上面代码的第一行展示的,我们把这种表达式称之为中缀表达式,即每一个数字间都有一个运算符。除了这种表达式还有一种我没显示的叫做后缀表达式,即将运算符放到数字后面的一种表示方法,如:
在这里插入图片描述
如果说中缀表达式是我们人容易理解的,那么后缀表达式就是计算机所能理解的。

(在后缀表达式中的#,是将相连的数字符号进行隔开,避免在字符转换成整型出现错误,比如10和2,如果不用#隔开,那在转换过程中就会变成102,而不是10和2)

通过中缀表达式计算出结果

我先来讲述后缀表达式如何在计算机中进行计算的,在讲述中缀表达式是如何转换成后缀表达式。

首先我们在一个字符数组变量str2,并且已经存放了 9#3#1-3*+10#2/+。
创建一个整型数组a,这里是会运用到了栈的知识,我们只需简单的模拟栈的相关功能即可。

int i=0;//存放字符串下标
int j=0;//存放数组的下标
char str2[50]="9#3#1-3*+10#2/+"
int a[20];

现在就要去遍历整个str2了,对于数字和运算符以及#,有着不同的处理办法。
首先我们考虑第一种情况:

  • 如果是数字,就需要转换成整型,存入到数组a中。
int num=0;//存放准换的整型
while(str2[i]!='\0')
{
	if(isdigit(str2[i]))//找到数字,匹配成功
	{
	//多个数字连在一起,如123,就需要循环,转换成整型一百二十三,存入num中
		while(str2[i] != '#'&&
		str2[i]!='-'&&
		str2[i]!='+'&&
		str2[i]!='*'&&
		str2[i]!='/')//循环结束的条件
		{
			num=num*10+(str2[i]-'0');
			i++;
		}
		a[j]=num;
		j++;
		num=0;//num需要重新赋值上0,否则会影响下一个数字的转换
	}
	else if()
	{
	}
	else
	{
	}
	
}
//isdigit()是c语言中判断是否为数字的一个库函数,是数字返回非0,不是数字返回0
//头文件是#include<ctype.h>

当遇到的不是数字将有以下两种考虑:
一种是运算符,一种是#。

  • 如果是运算符就需要在a数组中出栈两个整型,然后根据运算符计算结果,并重新入栈,循环往复,当遍历完整个字符串以后,最终a数组会只剩下一个元素,也就是最终的结果。
  • 如果是#,就可以直接跳过了。
	else if(str2[i]!='#'&&!isdigit(str2[i]))
	{
		a[j-2]=Cal(str2[i],a,j);
		j--;
		i++;
	}
	else//如果是str2[i]='#'的情况,会跳过,i++
	{
		i++;
	}

通过下图,可以理解上述代码。
在遇到减号前,a数组的存储情况,以及计算以后的情况。

在这里插入图片描述
此时的str2[i]指向的是一个减号,将1出栈,放到减号右边,3放到减号左边,实现计算,计算的结果放到下标为1的地方,因为此时的j指向下标为3,所以a[j-2]存放这个值,j–回到下标为2的地方。我们采用的是j表示元素个数。
同理其他运算符也是如此。

Cal的实现:

int Cal(char k, int* a,int j)
{
	if (k == '-')
	{
		return a[j - 2] - a[j - 1];
	}
	else if (k == '+')
		return a[j - 2] + a[j - 1];
	else if (k == '*')
		return a[j - 2] * a[j - 1];
	else if (k == '/')
		return a[j - 2] / a[j - 1];
}

char k存放当前str2[i]中的运算符。
int *a,就是这个数组。
int j:表示该数组下标。

将上面代码结合最终呈现:

int Cal(char k, int* a,int j)
{
	if (k == '-')
	{
		return a[j - 2] - a[j - 1];
	}
	else if (k == '+')
		return a[j - 2] + a[j - 1];
	else if (k == '*')
		return a[j - 2] * a[j - 1];
	else if (k == '/')
		return a[j - 2] / a[j - 1];
}

//9+(3-1)*3+10/2
//9#3#1-3*10#2/+
//计算结果
int Calculate(char* str2)
{
	int a[50];
	int i = 0;
	int num = 0;
	int j = 0;
	while (str2[i] != '\0')
	{
		if(isdigit(str2[i]))
		{
			while (str2[i] != '#'&&str2[i]!='-'&&str2[i]!='+'&&str2[i]!='*'&&str2[i]!='/')
			{
				num = num * 10 + (str2[i] - '0');
				i++;
			}
			a[j] = num;
			j++;
		}
		else if(str2[i]!='#'&&!isdigit(str2[i]))
		{
			//从a中拿两个数据计算
			a[j-2]=Cal(str2[i], a,j);
			j--;
			i++;
		}
		else
		{
			i++;
		}
		num = 0;
	}
	return a[0];
}

后缀表达式是便于在计算机中用代码实现表达式求值的一种表达方式,当我们通过中缀表达式计算出表达式的结果,那么就来到代码的核心部分,如何将中缀表达式转换成后缀表达式呢?以及如何将相邻的数字在转换存储到str2[]中用#隔开?

中缀表达式转换成后缀表达式

这里先展示需要的相关变量:

int i=0;//str1下标
int j=0;//str2下标
int a=0;//用于实现#的穿插
char str1[MAX]='\0';//存放中缀表达式
char str2[MAX]='\0';//存放后缀表达式
S stack;//存放表达式中的运算符(栈)

头文件展示:

#include <stdio.h>
#include <ctype.h>
#define MAX 100
int n;
typedef struct Stack
{
	char data[MAX];
	int top;
}S;

//中缀转换为后缀
void RPN(char* str1, char* str2, S* stack);

函数实现:

//中缀转换为后缀
void RPN(char* str1, char* str2, S* stack)
{
	int i = 0;//表示str1字符个数
	int j = 0;//表示str2字符个数
	int a = 1;//
	while (str1[i] != '\0')
	{
		if (isdigit(str1[i]))
		{
			if (a == 0&&isdigit(str2[j-1]))
			{
				str2[j] = '#';
				j++;
			}
			str2[j] = str1[i];
-			i++;
			j++;
			a = 1;
		}
		else
		{
			a = 0;
			if (stack->top == 0)
			{
				stack->data[stack->top] = str1[i];
				i++;
				stack->top++;
			}
			else
			{
				if (str1[i] == '(')
				{
					stack->data[stack->top] = str1[i];
					stack->top++;
					i++;
				}
				else
				{
					if (str1[i]==')')
					{
						while (stack->data[stack->top-1] != '(')
						{
							str2[j] = stack->data[stack->top-1];
							stack->top--;
							j++;
						}
						stack->top--;
						i++;
					}
					else
					{
						if (stack->data[stack->top] == '(')
						{
							stack->data[stack->top] = str1[i];
							stack->top++;
							i++;
						}
						else
						{
							if (str1[i] == '/' || str1[i] == '*')
							{
								stack->data[stack->top] = str1[i];
								stack->top++;
								i++;
							}
							else
							{
								if (stack->data[stack->top-1] == '*' || stack->data[stack->top-1] == '/')
								{
								while (stack->data[stack->top-1] != '(')
									{
										str2[j] = stack->data[stack->top - 1];
										j++;
										stack->top--;
									}
									stack->data[stack->top] = str1[i];
									i++;
									stack->top++;
								}
								else
								{
									stack->data[stack->top] = str1[i];
									stack->top++;
									i++;
								}
							}
						}
					}
				}
			}
		}
	}
	while (stack->top != 0)
	{
		str2[j] = stack->data[stack->top-1];
		stack->top--;
		j++;
	}
}

看了这一串代码是不是非常头疼,因为会考虑很多情况,所以涉及了许多if和else的语句,但通过一个思维导图就一目了然。

本思维导图的代码实现还未介入穿插#的操作

在这里插入图片描述
因为存在优先级,所以将±这类运算符入栈时,如果stack->top==0,或者是(都可以直接入栈。

()包含的算式优先级最高,其次是* /,最后是±。

后缀表达式穿插#的实现

其实用其他符号都可以。
在这里插入图片描述
字符的数字只有1~9。
在遍历中缀表达式的过程中类型变化一定是:
数字–符号—数字—数字—符号—数字—符号—数字–数字—数字—符号

对于连续三个的数字,转换为整型就是三位数,连续两个的数字,就是两位数,单个的就是个位数。

我们定义int a=1,当str1中一个数字或者多个连续的数字相继存放到str2中以后,再往下遍历的时候一定会遍历到运算符,此时就可以将a赋值为0,
这样,当重新遇到数字的时候,就可以加上一个条件语句,即a满足a=0,就可以打印#,记得j++哦。

但上述还存在一个细节问题,
在后缀表达式中,会涉及到连续的数字相连,需要考虑用#隔开,但有时候str2中已经有了运算符将相隔,就不用#隔开了,因此代码实现。
在这里插入图片描述
这里的if语句除了判断a是否为0,还要满足str2[j-1]是否为数字,是数字就需要#隔开,不是就不用隔开了。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值