一、需求分析
基于堆栈的中缀算数表达式的求值问题,输入一个中缀算术表达式,将其转换为后缀表达式,然后对后缀表达式进行求值。参与运算的为小于10的自然数。
功能及要求:
(1)先设计一个函数,把中缀算术表达式转换成后缀算术表达式。
(2)再设计一个函数,完成后缀算术表达式的求值计算。
(3)设计一个主函数,进行测试。
(4)输入要求:测试时中缀算数表达式的要以符号“#”结尾。只允许操作数的取值范围为一位正整数
输出要求:对每组数据输出2行,第1行为中缀表达式对应的后缀式,第2行为后缀式求 值的结果。
输入样例:3+(6-4/2)x5#
输出样例:3642/-5*+#
后缀算数表达式计算结果为23
算法提示:首先借助一个运算符栈将中缀表达式转换为后缀表达式,然后再借助一个运算符栈对后缀表达式进行求值。
附:中缀表达式变成等价的后缀表达式的算法:设立一个栈,存放运算符,从左到右扫描中缀表达式,若遇到操作数直接输出,若遇到运算符,则必须与栈顶算符比较,若当前读到的运算符级别高于栈顶运算符则进栈,否则退出栈顶运算符并输出;若遇到左括号,则进栈,继续读下一个字符;若遇到右括号,则一直做出栈并输出出栈运算符的操作,直到退到左括号为止。当表达式读完,栈变成空时,输出的结果即为后缀表达式。
二、概要设计
⒈ 设计链式堆栈的抽象数据类型定义:
ADT LinStack {
数据类型:
snode,SLNode:结点结构体名
data:存放数据元素
next:指针域存放指向下一个结点的指针
DataType:数组元素(即数据元素)的数据类型
基本操作: 操作结果
StackInitiate( **head) 初始化堆栈
StackNotEmpty( *head) 非空否
StackPush( *head,x) 入栈
StackPop( *head,&x) 出栈
StackTop(*head,i,&x) 取栈顶数据元素
Destroy(**head) 撤销动态申请空间
} ADT LinStack
2. 本程序模块结构
(1) 主函数main.c模块
int Priority(char oper) {
switch (oper) {
定义运算符优先级
}
}
void ConvertPostExp(char str[], char str1[])
把中缀表达式转换成后缀表达式,转换过程需要用到栈,具体过程如下:
1)设置一个堆栈,初始时将栈顶元素置为#。
2)顺序读入中缀算数表达式,当读到的单词为操作数时就将其输出,并接着读下一个单词
3)当读到的单词为运算符时,令x1为当前栈顶运算符的变量,x2为当前扫描读到运算符的变量,把当前读入的单词赋予变量x2,如果遇到左括号时将其直接入栈中。
3)如果遇到一个右括号,则将栈元素出栈,将弹出的操作符输出直到遇到左括号为止。注意,左括号只出栈并不输出。
4)如果遇到任何其他的操作符,如(“+”,“-”,“*”,“/”“#”)等,比较变量x1的优先级与变量x2的优先级、若x1的优先级高于x2的优先级,则将x1退栈并作为后缀算术表达式的一个单词输出。然后接着比较新的栈顶运算符x1的优先级写x2的优先级:若x的优先级低于x的优先级,则将x2的值进栈,
然后接着读下一个单词:若x的优先级等于x的优先级为“#”且为“#,则算法结束。#退栈输出
int PostExp(char str[]) // 后缀表达式求值
把中缀算术表达式变换成相应的后缀算术表达式后,计算后缀算术表达式的值的过程仍是一个堆栈应用问题。其算法思想是:设置一个堆栈存放操作数,从左到右依次扫描后缀算术表达式,每读到一个操作数就将其进栈,每读到一个运算符就从栈顶取出两个操作数施以该运算符所代表的运算操作,并把该运算结果作为一个新的操作数入栈,此过程直进行到后缀算术表达式读完,最后栈顶的操作数就是该后缀算术表达式的运算结果。
main(){
中缀转后缀;
输出后缀算术式;
后缀算术式求值;
输出计算结果
}
(2) 头文件LinStack.h模块。头文件LinStack.h中包括链式堆栈结构体定义、初始化操作、判断堆栈是否非空、入栈、出栈、取栈顶元素和撤销动态申请空间等函数声明。
(3) 头文件DataType.h模块。头文件DataType.h中包括数据类型的定义
(5) 源文件LinStack.c模块。源文件LinStack.c中包括初始化操作、判断堆栈是否非空、入栈、出栈、取栈顶元素和撤销动态申请空间操作的实现。
三、详细设计
⒈ 基本数据类型操作
(1) DataType.h模块
typedef char DataType; //定义DataType
(2)LinStack.h模块
#include"DataType.h"
typedef struct snode
{
DataType data;
struct snode* next;
}LSNode;
void StackInitiate(LSNode** head);//初始化带头结点链式堆栈
int StackNotEmpty(LSNode* head); //判断堆栈是否非空
void StackPush(LSNode* head, DataType x);//把数据元素x插入链式堆栈head的栈顶作为新的栈顶
int StackPop(LSNode* head, DataType* d); //出栈并把栈顶元素由参数d带回
int StackTop(LSNode* head, DataType* d); //取栈顶元素
void Destroy(LSNode* head); //撤销动态申请空间
(3)LinStack.c模块
#include<stdio.h>
#include"LinStack.h"
#include <malloc.h>
void StackInitiate(LSNode** head) //初始化带头结点链式堆栈
{
*head = (LSNode*)malloc(sizeof(LSNode));
(*head)->next = NULL;
}
int StackNotEmpty(LSNode* head)
//判断堆栈是否非空,非空,返回1,空,返回0
{
if (head->next == NULL)
return 0;
else
return 1;
}
void StackPush(LSNode* head, DataType x)
//把数据元素x插入链式堆栈head的栈顶作为新的栈顶
{
LSNode* p;
p = (LSNode*)malloc(sizeof(LSNode));
p->data = x;
p->next = head->next; //新结点链入栈顶
head->next = p; //新结点成为新的栈顶
}
int StackPop(LSNode* head, DataType* d)
//出栈并把栈顶元素由参数d带回,出栈成功则返回1,否则返回0
{
LSNode* p = head->next;
if (p == NULL)
{
printf("堆栈已空出错!");
return 0;
}
head->next = p->next; //删除原栈顶结点
*d = p->data; //原栈顶结点元素赋予d
free(p); //释放原栈顶结点内存空间
return 1;
}
int StackTop(LSNode* head, DataType* d)
//取栈顶元素并把栈顶元素由参数d带回
{
LSNode* p = head->next;
if (p == NULL)
{
printf("堆栈已空出错!");
return 0;
}
*d = p->data;
return 1;
}
void Destroy(LSNode* head) //撤销动态申请空间
{
LSNode* p, * p1;
p = head;
while (p != NULL)
{
p1 = p;
p = p->next;
free(p1);
}
}
(4)主函数main.c模块
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include<ctype.h>
#include<string.h>
#include"LinStack.h"
int Priority(char oper) //定义运算符优先级
{
switch (oper)
{
case '#':
return 0;
break;
case '(':
return 1;
break;
case '+':
case '-':
return 2;
break;
case '*':
case '/':
return 3;
break;
}
}
void ConvertPostExp(char str[], char str1[]) //中缀表达式转换成后缀表达式
{
DataType x, x1, x2;
int i = 0, j = 0;
LSNode* head; //定义头指针变量head
StackInitiate(&head); //初始化链式堆栈head
StackPush(head, '#'); //将栈顶元素设为#
for (i = 0; str[i] != '\0'; i++) //扫描中缀表达式,直到结束
{
if (isdigit(str[i])) //当str[i]为操作数时
{
str1[j] = str[i]; //将操作数输出
j++;
}
else //当str[i]为运算符时
{
x2 = str[i]; //将扫描元素放入到x2中
if (x2 == '(') //当运算符为“(”时
{
StackPush(head, x2); //运算符(进栈
}
else if (x2 == ')') //当运算符为“)”时
{
StackTop(head, &x1); //取栈顶元素
while (x1 != '(')
{
StackPop(head, &x); //运算符退栈
str1[j] = x; //输出出栈运算符
j++;
StackTop(head, &x1); //取栈顶元素
}
StackPop(head, &x); //将运算符“(”退栈
}
else
{
while (StackNotEmpty(head))
{
StackTop(head, &x1);
if (Priority(x1) > Priority(x2))
//当栈顶运算符优先级大于扫描到的运算符时,栈顶运算符退栈输出
{
StackPop(head, &x);
str1[j] = x;
j++;
}
else if (Priority(x1) < Priority(x2))
//当栈顶运算符优先级小于扫描到的运算符时,栈顶运算符退栈输出
{
StackPush(head, x2);
break; //结束本次循环,继续读下一个单词
}
else if (Priority(x1) == Priority(x2) && x1 == '#' && x2 == '#')
//当栈顶运算符优先级等于扫描到的运算符且为#时,栈顶运算符退栈输出
{
StackPop(head, &x);
str1[j] = x;
break;
}
else //当栈顶运算符优先级等于扫描到的运算符且不为#时,运算符进栈
{
StackPush(head, x2);
break;
}
}
}
}
}
str1[j + 1] = '\0';
Destroy(head);
}
int PostExp(char str[]) // 后缀表达式求值
{
DataType x, x1, x2;
int i;
LSNode* head; //定义头指针变量head
StackInitiate(&head); //初始化链式堆栈head
for (i = 0; str[i] != '#'; i++) //循环直到输出为#
{
if (isdigit(str[i])) //当str[i]为操作数时
{
x = (int)(str[i] - 48); //转换成int类型数据并存入变量x中
StackPush(head, x); //x入栈
}
else //当str[i]为运算符时
{
StackPop(head, &x2); //退栈得到操作数,存入变量x2中
StackPop(head, &x1); //退栈得到操作数,存入变量x2中
switch (str[i]) //执行str[i]所表示的运算
{
case '+': {x1 += x2; break; }
case '-': {x1 -= x2; break; }
case '*': {x1 *= x2; break; }
case '/':
if (x2 == 0.0)
{
printf("除数为0错!\n");
exit(0);
}
else
{
x1 /= x2;
break;
}
}
StackPush(head, x1); //运算结果入栈
}
}
StackPop(head, &x); //得到计算结果,存入x中
Destroy(head);
return x; //返回计算结果
}
int main()
{
char str[] = "3+(6-4/2)*5#"; //测试数据,注意测试数据末尾要加上#
char str1[100];
int result;
ConvertPostExp(str, str1); //中缀表达式转换成后缀表达式
for (int i = 0; str1[i]!='\0'; i++)
{
printf("%c ", str1[i]);
}
printf("\n");
result = PostExp(str1); // 后缀表达式求值
printf("后缀算数表达式计算结果为: %d\n", result);
system("pause");
return 0;
}
⒉ 函数的调用关系图
函数的调用关系图反映了本演示程序的层次结构,如图3.1所示。
图3.1 函数调用关系图