算术表达式求值(栈结构及其应用) - C语言
表达式求值是实现程序设计语言的基本问题之一,也是栈的应用的一个典型例子。一个算术表达式是由操作数(operand)、运算符(operator)和界限符(delimiter)组成的。假设操作数是正整数,运算符只含加减乘除等四种二元运算符,界限符有左右括号和表达式起始、如:(7+15)*(23-28/4)。引入表达式起始、结束符是为了方便。设计一个程序,演示算术表达式求值的过程。
一、实验目的
表达式求值是实现程序设计语言的基本问题之一,也是栈的应用的一个典型例子。一个算术表达式是由操作数(operand)、运算符(operator)和界限符(delimiter)组成的。假设操作数是正整数,运算符只含加减乘除等四种二元运算符,界限符有左右括号和表达式起始、如:(7+15)*(23-28/4)。引入表达式起始、结束符是为了方便。设计一个程序,演示算术表达式求值的过程。
二、实验要求
实验要求:
- 从文本文件输入任意一个语法正确的(中缀)表达式,显示并保存该表达式。
- 利用栈结构,把(中缀)表达式转换成后缀表达式,并以适当的方式展示栈的状态变化过程和所得到的后缀表达式。
- 利用栈结构,对后缀表达式进行求值,并以适当的方式展示栈的状态变化过程和最终结果。
- 设计思想
程序大体思路十分清晰,分为读入中缀表达式、转换为后缀表达式、后缀表达式求值三个部分。
使用char类型栈的结构形式来进行表达式的存储,接下来分为三部分进行阐述。
表1 函数定义
函数定义 | 作用 |
typedef struct SqStack SqStack; | 定义栈的结构 |
SqStack *initSqStack(); | 顺序栈的初始化 |
void SqPush(SqStack *S, elemtype e); | 入栈 |
elemtype SqPop(SqStack *S); | 出栈 |
elemtype SqGetTop(SqStack *S); | 获取栈顶元素 |
int isEmpty(SqStack *S); | 判断栈非空 |
int is_num(char c); | 判断是数字 |
int priority(char c); | 计算操作符优先级 |
void judge_priority(char operator, SqStack * S); | 比较操作符优先级 |
float arr2num(char temp_num[], int num_of_int, int num_of_decimal); | 将字符数组转化为数字 |
1 读入中缀表达式
使用文件操作,将存储在“arithmetic_expression.txt”中的表达式写入字符数组expr中。其中,表达式有以下要求:
①仅包含加减乘除和次方操作,可以使用括号;
②表达式间没有空格。
示例:
2 转换后缀表达式
2.1 后缀表达式转换规则
使用char类型栈将中缀表达式转化为后缀表达式,具体规则如下:
从头到尾读取中缀表达式的每个对象,按不同情况处理。
- 运算数:直接输出;
- 左括号:压入栈内;
- 右括号:将栈顶的运算符弹出并输出,直至遇到左括号(出栈但不输出);
- 运算符:
- 若优先级大于栈顶运算符,则把它压栈;
- 若优先级小于等于栈顶运算符,将栈顶运算符弹出并输出;再与新的栈顶运算符进行比较,直至该运算符优先级大于栈顶运算符优先级为止,然后将该运算符压栈;
- 当所有对象处理完毕,则把栈内存留的运算符一并输出。
2.2 运算符优先级
入栈前的左括号 > 次方 > 乘 = 除 > 加 = 减 > 入栈后的左括号
具体实现:priority函数
2.3 运算数的判定
由于本程序将运算数扩展到了实数范围,所以不可避免会遇到小数以及多位数,因此,对于运算数的判定规则与处理方式如下:
- 认为小数点(.)也为运算数;
- 遍历中缀表达式,若当前对象判定为运算数且下一位也是运算数,正常写入数组;若当前对象判定为运算数且下一位是操作符,则写入字符’e’作为数字的结束。
按照上述规则,对前例的中缀表达式,在result数组中存储为下:
表2 后缀表达式存储示例
9 | e | 3 | 2 | . | 2 | e | 1 | e | - | 3 | 2 | e | * | + | 0 | e | 8 | 9 | e | - | 2 | e | / | + |
3 后缀表达式求值
3.1 预处理
由于数字是以char类型存放在result数组中,并不可以直接进行运算,所以首先通过arr2num函数将char类型数字转化为float类型数字,并给予其标号,将最终结果分别存储在nums(运算数)和processed_expression(运算符及数字标号)数组中。
处理结果如下所示:
表3 原数组result
9 | e | 3 | 2 | . | 2 | e | 1 | e | - | 3 | 2 | e | * | + | 0 | e | 8 | 9 | e | - | 2 | e | / | + |
表4 处理后数组processed_expression
0 | 1 | 2 | - | 3 | * | + | 4 | 5 | - | 6 | / | + |
其中,数字0为char类型,代表第1个数字,1代表第二个数字......与此同时,nums数组存储如下:
表5 nums数组
内容 | 9 | 32.2 | 1 | 32 | 0 | 89 | 2 |
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
可以看到,每一个运算数其数字标号与其在nums中的下标是一一对应的,便于索引。
(1)arr2num的实现
构造char类型的temp_num数组用来临时存放某一个数字,使用flag作为当前数字是否为小数位的标志,使用num_of_decimal作为小数位数的计数器、num_of_int作为整数位数的计数器。
遍历result字符串:
① 如果发现为数字,则将其存入temp_num数组;具体来说,若flag等于0,则说明其为整数位,num_of_int计数器加1,若flag等于1,则说明其为小数位,num_of_decimal计数器加1。
② 若发现为’.’,则flag置为1;若发现为’e’,则标志此数字已经写入完成,进行后续处理操作,且将计数器置0,flag置0;
③ 若发现为操作符,则忽略。
以数字32.2为例,其相应变量存储如下:
表6 相关变量
32.2 | temp_num | num_of_int | num_of_decimal | ||
| 2 | 1 |
由此可知每个数字对应的权重。
表7 对应权重
temp_num | 3 | 2 | 2 |
权重 | 10^1 | 10^0 | 10^(-1) |
故将其分别与权重为相乘再求和即可得到数字,将其存入nums数组中。
(2)标号的必要性
由于本程序采用的是char类型的栈,因此栈中只可以存放char类型数据。而运算数为float类型,显然不可以存储。因此提出了以标号对应下标的形式,后续操作中入栈出栈的为其标号,对其操作时使用nums[(int)(index-’0’)]即可访问原数据。
此外,还可以选择新建一个float类型的栈,便可以不额外增加标号对应下标这一步骤,但此操作需要重新编写float栈相关的定义以及一系列函数,会造成代码量的大幅增加和重复,因此不予考虑。
3.2 后缀表达式求值运算
目前我们得到以下数据:
表8 已有数据
数组 | 含义 | 内容 | |||||||||||||
processed_expression | 含数字标号 的后缀表达式 |
| |||||||||||||
nums | 标号所对应的数字 |
|
为了方便阐述,下将标号与标号所对应的数字视为同一件事。
按照如下规则使用栈进行运算:
- 若是数字,则将其压入栈中;
- 若是操作符(二目),则取出栈中最顶部的两个数字做对应运算,并将其运算结果压入栈中;
- 当后缀表达式遍历结束时,栈中元素即为最终结果,出栈。
注:②中将运算结果压入栈中的处理:将计算结果存入nums数组,并给其对应标号。此时可以发现,当运算过程过长时,char类型的下标0~9都被用完,因此接着使用后续字符作为下标,例如,:的ASCII码值为58,’:’-’0’ = 10,故作为下标10使用,以此类推。
由此我们便可以计算出后缀表达式的值。
三、源代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#define elemtype char
#define MAXSIZE 100 // 栈的最大容量
int count = 0; // 记录结果中有多少个数
char result[2 * MAXSIZE]; // 存放后缀表达式结果
int num_end_flag; // 标记上一个输出的为数字
typedef struct SqStack SqStack; // 定义栈的结构
SqStack *initSqStack(); // 顺序栈的初始化
void SqPush(SqStack *S, elemtype e); // 入栈
elemtype SqPop(SqStack *S); // 出栈
elemtype SqGetTop(SqStack *S); // 获取栈顶元素
int isEmpty(SqStack *S); // 判断非空
int is_num(char c); // 判读是数字
int priority(char c); // 计算操作符优先级
void judge_priority(char operator, SqStack * S); // 比较操作符优先级
float arr2num(char temp_num[], int num_of_int, int num_of_decimal); // 将数组转化为数字
int main()
{
int i = 0;
char expr[MAXSIZE];
SqStack *stack = initSqStack();
/*--- 将表达式读取到expr数组中 ---*/
FILE *expression = fopen("./arithmetic_expression.txt", "r");
if (!expression)
{
printf("fail to open!\n");
}
else
{
for (i = 0; !feof(expression); i++)
{
expr[i] = fgetc(expression); //将文件内容赋给expr数组
}
fclose(expression);
}
expr[i] = '\0';
int len = strlen(expr) - 1;
printf("The infix expression read is: ");
printf("%s\n", expr);
printf("----------------------------------------------------------------------------------\n");
printf("Infix to suffix process:\n");
/*--- 使用栈将中缀表达式转化为后缀表达式 ---*/
num_end_flag = 0;
for (i = 0; i < len; i++)
{
// 数字 直接输出
if (is_num(expr[i]))
{
printf("Step%d: '%c' is number, output\n\n", i + 1, expr[i]);
result[count] = expr[i];
count++;
if (!is_num(expr[i + 1]))
{
result[count] = 'e';
count++;
}
}
else
{
// 操作符 进行处理
printf("Step%d: '%c' is operator, process\n", i + 1, expr[i]);
// 左括号 压入栈中
if (expr[i] == '(')
{
printf("- '(' has the highest priority, push it into stack\n\n");
SqPush(stack, expr[i]);
continue;
}
// 右括号 出栈直至左括号
if (expr[i] == ')')
{
printf("- pop stack until meet '('\n");
char temp = SqGetTop(stack);
while (temp != '(')
{
result[count] = temp; // 将出栈结果存储
count++;
printf("- '%c' out of stack\n", temp);
SqPop(stack);
temp = SqGetTop(stack);
}
SqPop(stack); // 将'('出栈,不输出
printf("- '(' out of stack\n\n");
continue;
}
// 普通运算符 比较优先级
judge_priority(expr[i], stack);
printf("\n");
}
}
// 所有元素处理结束 逐个出栈
printf("All operator has processed, output the operators in the stack in turn\n");
while (!isEmpty(stack))
{
char temp = SqGetTop(stack);
result[count] = temp;
count++;
printf("- output '%c'\n", temp);
SqPop(stack);
}
// 输出后缀表达式结果
printf("\n----------------------------------------------------------------------------------\n");
printf("The postfix expression is: ");
for (i = 0; i < count; i++)
if (result[i] == 'e')
printf(" ");
else
{
if (is_num(result[i]))
printf("%c", result[i]);
else
printf("%c ", result[i]);
}
printf("\n");
/* 进行运算前的预处理,将字符转化为数字 */
// char result[MAXSIZE] = {"9e32.2e1e-3e2e^*+0e89e-2e/+"};
len = strlen(result);
char temp_num[MAXSIZE]; // 用于存放需处理的数字
int count_temp_num = 0;
float nums[MAXSIZE]; // 用于存放处理完的数字们
char processed_expression[MAXSIZE]; //处理后的表达式(数字使用下标代替)
int individuals = 0; // 处理后的表达式中元素的个数
int index = 0; // 数字的下标 num_i
int flag = 0; // 作为已经是小数的标志
int num_of_decimal = 0; // 小数的位数
int num_of_int = 0; // 整数的位数
for (i = 0; i < len; i++)
{
//如果是数字,则进行处理
if (is_num(result[i]))
{
if (result[i] == '.')
flag = 1;
else
{
if (flag)
num_of_decimal++;
else
num_of_int++;
temp_num[count_temp_num] = result[i];
count_temp_num++;
}
}
else
{
// 若不是数字,则将运算符存起来
if (result[i] != 'e')
{
processed_expression[individuals] = result[i];
individuals++;
}
// 如果是e,则说明之前为数字,处理之前的数组
else
{
float number = arr2num(temp_num, num_of_int, num_of_decimal);
nums[index] = number;
processed_expression[individuals] = index + '0';
individuals++;
index++;
num_of_decimal = 0;
num_of_int = 0;
flag = 0;
count_temp_num = 0;
}
}
}
/* 后缀表达式求值 */
printf("\n----------------------------------------------------------------------------------\n");
printf("Evaluation of suffix expression:\n\n");
len = strlen(processed_expression);
float temp_result[MAXSIZE];
int temp_index = 0;
for (i = 0; i < len; i++)
{
printf("Step%d ", i + 1);
if (is_num(processed_expression[i]))
{
printf("number:%f, push it into stack\n\n", nums[(int)(processed_expression[i] - '0')]);
SqPush(stack, processed_expression[i]);
}
else
{
printf("operator:%c, caculate\n", processed_expression[i]);
char num2 = SqPop(stack);
char num1 = SqPop(stack);
printf("- get the top element of the stack, num2: %f\n", nums[(int)(num2 - '0')]);
printf("- get the top element of the stack, num1: %f\n", nums[(int)(num1 - '0')]);
float res12;
switch (processed_expression[i])
{
case '-':
res12 = nums[(int)(num1 - '0')] - nums[(int)(num2 - '0')];
printf("- caculate %f - %f\n", nums[(int)(num1 - '0')], nums[(int)(num2 - '0')]);
break;
case '+':
res12 = nums[(int)(num1 - '0')] + nums[(int)(num2 - '0')];
printf("- caculate %f + %f\n", nums[(int)(num1 - '0')], nums[(int)(num2 - '0')]);
break;
case '*':
res12 = nums[(int)(num1 - '0')] * nums[(int)(num2 - '0')];
printf("- caculate %f * %f\n", nums[(int)(num1 - '0')], nums[(int)(num2 - '0')]);
break;
case '/':
res12 = nums[(int)(num1 - '0')] / nums[(int)(num2 - '0')];
printf("- caculate %f / %f\n", nums[(int)(num1 - '0')], nums[(int)(num2 - '0')]);
break;
case '^':
res12 = pow(nums[(int)(num1 - '0')], nums[(int)(num2 - '0')]);
printf("- caculate %f ^ %f\n", nums[(int)(num1 - '0')], nums[(int)(num2 - '0')]);
break;
default:
break;
}
char res_name = index + '0';
nums[index] = res12;
index++;
SqPush(stack, res_name);
printf("- push the caculate result %f into stack\n\n", res12);
}
}
int final_index = (int)(SqGetTop(stack) - '0');
float final_res = nums[final_index];
printf("Calculation completed\n");
printf("The calculation results result is %f\n", final_res);
printf("\n----------------------------------------------------------------------------------\n");
printf("%s = %f\n\n", expr, final_res);
system("pause");
return 0;
}
/* 将数组转化为数字 */
float arr2num(char temp_num[], int num_of_int, int num_of_decimal)
{
int p = 0;
float res = 0;
int temp;
for (p = 0; p < num_of_int; p++)
{
temp = temp_num[p] - '0';
res += (float)(temp * pow(10, num_of_int - p - 1));
}
if (num_of_decimal != 0)
{
for (p = num_of_int; p < num_of_int + num_of_decimal; p++)
{
temp = temp_num[p] - '0';
res += (float)(temp * pow(10, -p + num_of_int - 1));
}
}
return res;
}
/* 判断是否是数字 */
int is_num(char c)
{
if (c >= '0' && c <= '9' || c == '.' || (c >= ':' && c <= 'd' && c != '^'))
return 1;
else
return 0;
}
/* 比较运算符的优先级 */
void judge_priority(char operator, SqStack * S)
{
int p_oper = priority(operator); // 运算符的优先级
int p_stack; // 栈顶运算符的优先级
if (!isEmpty(S))
{
p_stack = priority(SqGetTop(S));
}
else
{
p_stack = 0; // 若栈顶为空,直接置为最低优先级0
}
// 优先级大于栈顶运算符,压栈
if (p_oper > p_stack)
{
printf("- the priority of '%c' is higher than the top('%c') of stack, push it into stack\n", operator, SqGetTop(S));
SqPush(S, operator);
return;
}
else
{
// 优先级小于等于栈顶运算符,输出栈顶,直到优先级大于栈顶
do
{
printf("- the priority of '%c' is less than or equal to the top('%c') of stack, pop the top out\n", operator, SqGetTop(S));
result[count] = SqGetTop(S);
count++;
SqPop(S);
// 重新计算栈顶优先级
if (!isEmpty(S))
p_stack = priority(SqGetTop(S));
else
p_stack = 0;
} while (p_oper <= p_stack);
printf("- the priority of '%c' is higher than the top('%c') of stack, push it into stack\n", operator, SqGetTop(S));
SqPush(S, operator);
}
}
/* 计算运算符的优先级 */
int priority(char c)
{
switch (c)
{
case '+':
return 1;
case '-':
return 1;
case '*':
return 2;
case '/':
return 2;
case '(':
return 0;
case '^':
return 3;
default:
return 0;
}
}
/*------------------- 栈的顺序存储 -------------------*/
typedef struct SqStack
{
elemtype *base; // 栈底指针
elemtype *top; // 栈顶指针 - 指向最高元素的上一个
int stacksize; // 栈可用的最大容量
} SqStack;
/* 顺序栈的初始化(创建) */
SqStack *initSqStack()
{
SqStack *S = (SqStack *)malloc(sizeof(SqStack));
S->base = (elemtype *)malloc(sizeof(elemtype) * MAXSIZE); // 栈底指向一个拥有MAXSIZE个元素大小的空间
S->top = S->base; // 初始化栈顶=栈底
S->stacksize = MAXSIZE;
return S;
}
/* 顺序栈的入栈 */
void SqPush(SqStack *S, elemtype e)
{
if (S->top - S->base == S->stacksize)
{
printf("stack overflow!");
return;
}
*(S->top) = e;
S->top += 1;
}
/* 出栈 */
elemtype SqPop(SqStack *S)
{
if (S->top == S->base)
{
printf("stack is empty!");
return -1;
}
S->top--;
return *(S->top);
}
/* 获取栈顶元素 */
elemtype SqGetTop(SqStack *S)
{
if (S->top == S->base)
{
printf("stack is empty! ");
return -1;
}
return *(S->top - 1);
}
/* 判断非空 */
int isEmpty(SqStack *S)
{
if (S->top == S->base)
return 1;
else
return 0;
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)