文末有完整代码,有疑问的读者可以在评论区一起讨论,也可以私信一起交流学习
一:题目要求
<1>案例分析
任何一个表达式都是由操作数(operand)、运算符(operator)和界限符(delimiter)组成的,统称他们为单词。一般地,操作数即可以是常数也可以是被定义为变量或常亮的标识符;运算符可以分为算数运算符、关系运算符和逻辑运算符3类;基本界限符有左右括号和表达式结束符等。为了叙述的简洁,在此仅讨论简单算术表达式的求值问题,这种表达式只含加、减、乘、除4种运算符。读者不难将它推广到更一般的表达式上。
下面把运算符和界限符统称为算符;我们知道算术四则运算遵循以下三条规则:
1>先乘除,后加减
2>从左算到右
3>先括号内后括号外
根据上述3条运算规则,在运算的每一步中,任意两个相继出现的算符theta1和theta2之间的优先关系,至多是下面3种关系之一:
theta1<theta2,即theta1的优先权低于theta2
theta1=theta2,即theta1的优先权等于theta2
theta1>theta2,即theta1的优先权高于theta2
1:算符间的优先关系表:
由规则(1),先进行乘除运算,后进行加减运算,所以有"+" < "*" 、"+" <" /" 、 "*" > "+" 、"/" > "+"等。
由规则(2),运算遵循左结合性,当两个运算符相同时,先出现的运算符优先级高,所以有"+" > "+"、"-" > "-" 、"*" > "*"、"/" > " I "。
由规则(3),括号内的优先级高,tahta1为"+" "_" "*" 和 "/" 时的优先级均低于tahta2为"("时的优先级,但高于tahta2为")"时的优先级。
表中的"("=")"表示当左右括号相遇时,括号内的运算已经完成。为了便于实现,假设每个表达式均以"#"开始,以 "#" 结束。所以 "#" = "#" 表示整个表达式求值完毕。")" 与 "(" 、"#" 与 ")" 以及"(" 与 "#" 之间无优先关系,这是因为表达式中不允许它们相继出现,一旦遇到这种情况,则可以认为出现了语法错误。在下面的讨论中,我们暂假定所输入的表达式不会出现语法错误。
【案例实现】
可以使用两个工作栈实现表达式求值算法,一个称作 OPTR ,用以寄存运算符;另一个称作 OPND ,用以寄存操作数或运算结果。
<2>【算法步骤】
①初始化 OPTR 栈和 OPND 栈,将表达式起始符"#"压入 OPTR 栈。
②读取表达式,读入第一个字符 ch ,如果表达式没有读取完毕至"#"或 OPTR 的栈顶元素不为"#"时,则循环执行以下操作。
●若 ch 不是运算符,则压入 OPND 栈,读入下一字符 ch 。
●若 ch 是运算符,则根据 OPTR 的栈顶元素和 ch 的优先级比较结果,做不同的处理:
>若小于,则将 ch 压入 OPTR 栈,读入下一字符 ch ;
>若大于,则弹出 OPTR 栈顶的运算符,从 OPND 栈弹出两个数,进行相应运算,将结果压人 OPND 栈;
>若等于,则 OPTR 的栈顶元素是"("且 ch 是")",这时弹出 OPTR 栈顶的"(",相当于括号匹配成功,然后读入下一字符 ch 。
OPND 栈顶元素即表达式求值结果,返回此元素。
理论指导《数据结构 C语言第二版》 严蔚敏....编著
2:ASCII码参考表:
3:输入历程思路表:
二、代码实现
<1>.cpp文件
#include"Module.h"
int main()
{
char ch,x;//用以存储压入栈的运算符
char theta;//用以存储弹出栈的运算符
char a,b;//用以存储运算数数值
LinkStack OPTR ;//运算符栈
LinkStack OPND;//操作数栈
InitStack(OPTR);//初始化栈
InitStack(OPND);
Push(OPTR, '#');//将#压入运算符栈OPTR中
scanf_s("%c", &ch);//不管是数字还是字符在本例中都以字符char的形式输入以及存储
getchar();
while (ch != '#' || GetTop(OPTR) != '#')
{
if (!In(ch))//如果不是算符则:
{
Push(OPND, ch);//把数字字符压入OPND栈中
scanf_s("%c", &ch);//继续读入下一个元素
getchar();//解决输入函数,输入字符时因为缓冲区而不能正常输入的问题
}
else
switch (Precede(GetTop(OPTR), ch))//比较OPTR的栈顶元素和ch的优先级
{
case '<':
Push(OPTR, ch);//优先权大于栈顶算符则压入OPTR栈
scanf_s("%c", &ch);//继续读取下一字符
getchar();
break;
case '>':
Pop(OPTR, theta);//如果小于栈顶算符则弹出栈顶算符
Pop(OPND, b);//同时弹出OPND数栈中两个元素进行运算
Pop(OPND, a);//例:7-2,7先输入2后输入,先弹出b2再弹出a7
Push(OPND, Operate(a, theta, b));//做运算7-2,再把运算结果压入数栈OPND中
break;
case '=':
Pop(OPTR, x);
scanf_s("%c", &ch);
getchar();
break;
}
}
printf("表达式计算结果为:%d\n",GetTop(OPND) - 48);//GetTop返回的数据类型为char型,减48得到我们需要的int类型
return true;
}
<2>.h文件
#pragma once
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#define ElemType int
//链栈的存储结构
typedef struct StackNode//使用typedef给struct StackNode起别名SN和*LinkStack
{
ElemType data;//数据域
StackNode* next;//指针域
}StackNode, * LinkStack;//定义两个结构体类型其中LinkStack为指向结构体的指针
//初始化函数
//链栈的初始化操作就是构造一个空栈,因为没必要设头结点,所以直接将栈顶指针置空即可
int InitStack(LinkStack& S)
{
S = NULL;
return true;
}
//入栈
int Push(LinkStack& S,char Ele)
{
LinkStack p;//定义p的类型为指向结构体SN的指针
p = new StackNode;//生成新节点并为p申请空间
p->data = Ele;//将新结点的数据域赋值为data
p->next = S;//将新结点插入栈顶
S = p;//修改栈顶指针为p
return true;
}
//出栈
int Pop(LinkStack& S,char& Ele)//弹出栈顶元素给变量Ele
{
LinkStack p;
if (S == NULL)
{
return false;
}
else
{
Ele = S->data;//用将栈顶元素的值赋给variable
p = S;//用p临时保存栈顶元素空间,以备释放
S = S->next;//修改栈顶指针
delete p;//释放栈顶元素的空间
return true;
}
}
//取栈顶元素
int GetTop(LinkStack& S)
{
if (S == NULL)
{
return false;
}
else
{
return S->data;//返回栈顶元素
}
}
//遍历链栈元素
int Traversal(LinkStack& S)
{
LinkStack p;
p = S;
if (p == NULL)
{
printf("链栈为空!!\n");
return false;
}
else
{
printf("链栈存储数据为:");
for (p = S; p != NULL; p = p->next)//链表循环
{
printf("%d\t", p->data);
}
printf("\n");
return true;
}
}
//In判断ch是否为运算符
int In(char ch)
{
char arr[7] = { '+','-','*','/','(',')','#' };
for (int i = 0; i < 7; i++)
{
if(ch==arr[i])//遍历char数组,如果在数组中则判定为算符返回true否则返回false
{
return true;
}
}
return false;
}
//判断运算符优先级
char Precede(char theta1, char theta2)//根据算符间的优先关系表得到相应的输出
{
if ((theta1 == '(' && theta2 == ')') || (theta1 == '#' && theta2 == '#'))
{
return '=';
}
else if (theta1 == '(' || theta1 == '#' || theta2 == '(' || (theta1 == '+' && (theta2 == '*' || theta2 == '/')) || (theta1 == '-' && (theta2 == '*' || theta2 == '/')))
{
return '<';
}
else
return '>';
}
//ASCII码中‘#’35‘*’42 ‘+’43
char Operate(char a, char theta, char b)
{
switch (theta)//函数返回类型为char
{
case '+'://对于数字来说char类型与int类型对应的十进制数相差48 ‘0’的ASCII码对应的十进制数为48
return (a - '0') + (b - '0') + 48;//将char类型的字符转化为int类型做运算后,加上48转化为字符型的数字
case '-': //例34-32=2 2+48=50 50为2对应的ASCII值
return (a - '0') - (b - '0') + 48;
case '*':
return (a - '0') * (b - '0') + 48;
case '/':
return (a - '0') / (b - '0') + 48;
}
return 0;//前面的语句没有成功返回时函数返回0
}
<3>运行结果
注意:输入时中文“()”和 英文 '()' 得到的结果会不一样,原因是二者在ASCII码中对应的十进制数不同,正确输入应使用英文输入法的'()'