数据结构——02-算数表达式-栈-实验题目分享

一、实验题目

算数表达式计算:

设计算法根据用户输入的合法表达式计算结果并显示出来

表达式中的符号为+、-、*、/、(、)

表达式中数字为整数

二、实验环境

Windows 11

Visual Studio Code

(总体代码在最后)

三、实验过程

思路分析:

需要实现一个算法来解析和计算包含加法(+)、减法(-)、乘法(*)、除法(/)以及括号(( ))的合法整数表达式:

根据学过的树的知识,这个表达式可以有三种表达方式: ​ 前缀、中缀、后缀

前缀:将表达式变为前缀后,从右往左,每次从操作数栈取一个数,再取一个操作符,再取一个操作数,进行运算。

后缀:将表达式变为后缀后,从左往右,每次从操作数栈取一个数,再取一个操作符,再取一个操作数,进行运算。

中缀:虽然表达式写出来很适合人类观看,但是对计算机实现来说比较困难,每当遇见一个算术运算符就要看操作符栈是不是空的,如果是空的就直接放进去,如果不是空的就要比较优先级,如果优先级大就进去,如果小就要取出两个数字进行运算再放进去

举个例子:

img

img

img

img

1. 创建两个栈:一个用于存储操作数(数字),另一个用于存储操作符(+、-、*、/);

2. 从左到右扫描表达式:

如果遇到操作数,将其压入操作数栈。

如果遇到操作符:

        如果操作符栈为空,或者栈顶是左括号,直接将操作符压入操作符栈。

        如果当前操作符的优先级高于操作符栈栈顶的操作符,将当前操作符压入操作符栈。

        否则,从操作数栈中弹出一个操作数和操作符栈的栈顶操作符,执行相应的运算,并将结果压回操作数栈。然后将当前操作符压入操作符栈。

如果遇到左括号,将其压入操作符栈。

        如果遇到右括号,从操作数栈中弹出一个操作数和操作符栈中的操作符,执行运算,并将结果压回操作数栈。重复此过程直到遇到左括号,然后从操作符栈中弹出左括号。

3. 表达式扫描完毕后,如果操作符栈中仍有操作符,重复第二步的过程,直到操作符栈为空。

4. 最后,操作数栈中应该只剩下一个元素,即表达式的结果。

代码:

img

计算函数 (Calculate):这个函数负责执行基本的算术运算。它从数字栈中弹出两个数字,根据传入的运算符执行相应的运算,并将结果压入数字栈。

img

完整代码请见附件

测试结果:

img

四、结果分析

img

使用这两个结构体来实现数字栈和字符栈

img

压栈

img

主函数 (main):这是程序的入口点。它首先读取一个表达式,然后初始化数字栈和字符栈。接着,它遍历表达式中的每个字符,如果是数字,则将其转换为浮点数并入栈;如果是运算符,则根据运算符的优先级和栈内的运算符进行比较,然后决定是入栈还是执行计算。最后,当表达式中的所有字符都被处理完毕后,执行剩余的运算符,并将最终结果打印出来。

img

总体代码结构:

1. 输入处理:

  1. 从用户那里获取输入的表达式,并将其转换为字符数组或字符串。

  2. 编写一个函数来检查表达式的合法性,包括括号匹配和操作符的正确使用。

2. 表达式解析:

  1. 实现上述两个栈的逻辑。

  2. 编写一个循环来逐个字符处理表达式。

3. 计算结果:

  1. 根据两个栈的内容执行计算。

  2. 在遇到操作符时,从栈中弹出相应的操作数,执行运算,并压入结果。

4. 输出结果:

  1. 一旦表达式的所有部分都被处理完毕,操作数栈顶的元素就是最终结果。

  2. 显示结果给用户。

五、实验心得

在本次实验中,通过编写和测试一个简单的表达式求值器程序,加深对栈这一数据结构的理解,同时熟悉基本的算术运算和运算符优先级处理,我更加深入地理解了栈的工作原理以及如何利用栈来解决实际问题。

我将具体完成阶段分为三个:设计阶段:首先设计了两个栈结构,一个用于存储操作数(数字),另一个用于存储操作符(字符)。确定了每个栈的基本操作,包括初始化、入栈、出栈和获取栈顶元素。 编码阶段:接着,根据设计实现了主函数和栈操作函数。主函数负责读取表达式并逐个字符进行解析,根据运算符优先级和结合性规则,执行相应的入栈和计算操作。 测试阶段:在完成编码后,我进行了多轮测试,包括简单表达式的计算和错误输入的处理。

程序能够正确处理基本的算术表达式,并返回预期的结果。

学习总结:学习了栈的基本操作和在程序中的应用。 理解了运算符优先级和结合性的概念。 加强了对C语言中字符串处理和字符操作的理解。 意识到了代码测试的重要性,以及如何通过测试来发现并解决问题。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#include <math.h>
#define maximum 100000

typedef struct//数字栈
{	float data[maximum];
	int top;
} number;

typedef struct//字符栈
{	char data[maximum];
	int top;
} sign;

void InitNumber(number *stack);//初始化数字栈
void GetTopNumber(number stack, float *e);//获取栈顶元素
void PushNumber(number *stack, float e);//进栈
void PopNumber(number *stack, float *e);//出栈

void InitSign(sign *stack);
void GetTopSign(sign stack, char *e);
void PushSign(sign *stack, char e);
void PopSign(sign *stack, char *e);

void Calculate(number *stack, char e);

number Num;
sign sig;
char expression[maximum];

int main()
{	gets(expression);
	int length;
	length = strlen(expression);
	int i;
	float en, n;
	char es;
	InitNumber(&Num);
	InitSign(&sig);
	for (i = 0; i < length; i++)
	{	if (expression[i] >= '0' && expression[i] <= '9')
		{	n = expression[i] - '0'; //字符型转换为整型
			while (expression[i + 1] != '\0')
			{	if (expression[i + 1] >= '0' && expression[i + 1] <= '9')
				{	n = n * 10 + expression[i + 1] - '0';
					++i;
				}
				else
					break;
			}
			PushNumber(&Num, n);
		}
		else if (expression[i] == '+' || expression[i] == '-' || expression[i] == '*' || expression[i] == '/'
		         || expression[i] == '^' || expression[i] == '(' || expression[i] == ')')
		{	switch (expression[i])
			{	case '+':
					if (sig.data[sig.top - 1] != '+' && sig.data[sig.top - 1] != '-' && sig.data[sig.top - 1] != '*'
					        && sig.data[sig.top - 1] != '/' && sig.data[sig.top - 1] != '^')
						//与栈顶元素的优先级相比较, 高于时入栈,此处判断是否入栈。
						PushSign(&sig, '+');
					else
					{	while (sig.top > 0 && sig.data[sig.top - 1] != '(') //如果栈不为空切不为左括号,则出栈
						{	PopSign(&sig, &es);
							Calculate(&Num, es);
						}
						PushSign(&sig, '+');
					}
					break;
				case '-':
					if (sig.data[sig.top - 1] != '+' && sig.data[sig.top - 1] != '-' && sig.data[sig.top - 1] != '*'
					        && sig.data[sig.top - 1] != '/' && sig.data[sig.top - 1] != '^')
						PushSign(&sig, '-');
					else
					{	while (sig.top > 0 && sig.data[sig.top - 1] != '(')
						{	PopSign(&sig, &es);
							Calculate(&Num, es);
						}
						PushSign(&sig, '-');
					}
					break;
				case '*':
					if (sig.data[sig.top - 1] != '*' && sig.data[sig.top - 1] != '/' && sig.data[sig.top - 1] != '^')
						PushSign(&sig, '*');
					else
					{	while (sig.top > 0 && sig.data[sig.top - 1] != '(')
						{	PopSign(&sig, &es);
							Calculate(&Num, es);
						}
						PushSign(&sig, '*');
					}
					break;
				case '/':
					if (sig.data[sig.top - 1] != '*' && sig.data[sig.top - 1] != '/' && sig.data[sig.top - 1] != '^')
						PushSign(&sig, '/');
					else
					{	while (sig.top > 0 && sig.data[sig.top - 1] != '(')
						{	PopSign(&sig, &es);
							Calculate(&Num, es);
						}
						PushSign(&sig, '/');
					}
					break;
				case '^':
					if (sig.data[sig.top - 1] != '^')
						PushSign(&sig, '^');
					else
					{	while (sig.top > 0 && sig.data[sig.top - 1] != '(')
						{	PopSign(&sig, &es);
							Calculate(&Num, es);
						}
						PushSign(&sig, '^');
					}
				case '(':
					PushSign(&sig, '(');
					break;
				case ')':
					while (sig.data[sig.top - 1] != '(')
					{	PopSign(&sig, &es);
						Calculate(&Num, es);
					}
					PopSign(&sig, &es);
			}
		}
	}
	while (sig.top > 0)
	{	PopSign(&sig, &es);
		Calculate(&Num, es);
	}
	GetTopNumber(Num, &en);
	printf("%.0f\n", en);
	return 0;
}

void InitNumber(number *stack)
{	stack->top = 0;
}

void GetTopNumber(number stack, float *e)
{	if (stack.top == 0)
		return;
	else *e = stack.data[stack.top - 1];
}

void PushNumber(number *stack, float e)
{	if (stack->top >= maximum)
		return;
	else
		stack->data[stack->top++] = e;
}

void PopNumber(number *stack, float *e)
{	if (stack->top == 0)
		return;
	else *e = stack->data[--stack->top];
}

void InitSign(sign *stack)
{	stack->top = 0;
}

void GetTopSign(sign stack, char *e)
{	if (stack.top == 0)
		return;
	else *e = stack.data[stack.top - 1];
}

void PushSign(sign *stack, char e)
{	if (stack->top >= maximum)
		return;//栈满
	else
	{	stack->data[stack->top] = e;
		stack->top++;
	}
}

void PopSign(sign *stack, char *e)
{	if (stack->top == 0)
		return;
	else *e = stack->data[--stack->top];
}

void Calculate(number *stack, char e)// 计算结果
{	float num1, num2, result;
	PopNumber(stack, &num2);
	PopNumber(stack, &num1);
	switch (e)
	{	case '+':
			result = num1 + num2;
			PushNumber(stack, result);
			break;
		case '-':
			result = num1 - num2;
			PushNumber(stack, result);
			break;
		case '*':
			result = num1 * num2;
			PushNumber(stack, result);
			break;
		case '/':
			if (num2 == 0)
				printf("表达式错误!");
			else
			{	result = num1 / num2;
				PushNumber(stack, result);
				break;
			}
		case '^':
			result = pow(num1, num2);
			PushNumber(stack, result);
			break;
	}
}

  • 27
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
算数表达式求值是的一个重要应用之一。算数表达式通常由数字、运算符和括号组成,例如: (3 + 4) * 5 - 6 / 2 为了求出这个表达式的值,我们需要遵循一定的计算规则,通常是先计算括号内的表达式,再依次计算乘、除、加、减运算。使用可以较为方便地实现这个过程。 具体操作步骤如下: 1. 建立两个,一个用于存储数字,另一个用于存储运算符。 2. 从左到右遍历表达式的每一个字符,遇到数字则直接压入数字中,遇到运算符则判断其优先级,如果其优先级高于顶运算符,则直接将其压入运算符中;否则,将顶运算符弹出,与数字中的两个数字进行运算,将结果压回数字中,然后继续比较当前运算符与新的顶运算符的优先级。 3. 当表达式遍历完成后,依次弹出运算符中的运算符,并与数字中的两个数字进行运算,将结果压回数字中,直到运算符为空。 4. 最终,数字中仅剩一个数字,即为表达式的值。 以表达式 (3 + 4) * 5 - 6 / 2 为例,具体操作过程如下: 1. 遍历到字符 '3',将其压入数字中。 2. 遍历到字符 '+',由于运算符为空,直接将其压入运算符中。 3. 遍历到字符 '4',将其压入数字中。 4. 遍历到字符 ')',弹出运算符中的运算符 '+',并弹出数字中的两个数字 3 和 4,计算出结果 7,将其压入数字中。 5. 遍历到字符 '*',由于运算符中的运算符优先级高于当前运算符,直接将其压入运算符中。 6. 遍历到字符 '5',将其压入数字中。 7. 遍历到字符 '-',弹出运算符中的运算符 '*',并弹出数字中的两个数字 7 和 5,计算出结果 35,将其压入数字中。 8. 遍历到字符 '6',将其压入数字中。 9. 遍历到字符 '/',由于运算符中的运算符优先级低于当前运算符,弹出数字中的两个数字 35 和 6,计算出结果 5,将其压入数字中。 10. 遍历结束,弹出运算符中的运算符 '-',并弹出数字中的两个数字 35 和 5,计算出结果 30,即为表达式的值。 综上,使用可以较为方便地实现算数表达式的求值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

被瞧不起的神

谢谢啦,感谢支持|一起努力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值