题前需要了解的:中缀、后缀表达式是什么?(不知道你们知不知道,反正我当时不知道,搜的百度)
基本思路:先把输入的中缀表达式→后缀表达式→进行计算得出结果
栈:”先进先出,先进后出“!
中缀转后缀(先把转换后的后缀表达式存入字符数组):从左至右依次读取,遇到运算数存入字符数组,遇到运算符压入栈,继续读取–如果遇到的运算符优先级比栈顶的运算符优先级低或者相等(比如“+与+或-” ----- “* 与 或/”------“/与/或”),则先将栈中的运算符输送至字符数组(如果栈中有“(”,则只输出到左括号就停止输出,不输出左括号),继续读取–如果遇到运算符优先级比栈顶运算符高的则入栈成为新的栈顶运算符,继续读取----如果遇到“)”,则将栈元素输出至字符数组,直至输出至”(“停止(在后缀表达式中没有括号,所以括号不输出入字符数组),直至读取完毕,然后将栈中剩余的运算符输出至字符数组。完毕!(注意:在遇到右括号”)“后就要将”(“左括号从栈中删除了,因为为防止将括号输出至字符数组)。
后缀表达式求值:从左至右读取,遇到运算数则将其存入栈中,遇到运算符(比如”/“)则将栈顶元素的前一个运算数(比如temp1)与栈顶元素(比如temp2)出栈(–注意–)并进行运算,temp1/temp2,并将其最终结果重新压入栈中成为新的栈顶元素,直至得出最终结果。
上代码:
#include
#include
#include
#define MAX 100
typedef float Num;//为防止以后变换操作数类型需要
typedef struct
{
Num data[MAX];
int top;
}StackNum;//运算数栈
typedef struct
{
char data[MAX];
int top;
}StackChar;//运算符栈
//------函数声明---------
void InitNum(StackNum *p);//运算数栈初始化
void PushNum(StackNum *p, Num e);//运算数压栈
void PopNum(StackNum *p, Num *e);//运算数出栈
Num GetNum(StackNum p);//取栈顶元素
//-----------------------
void InitChar(StackChar *p);//运算符栈初始化
void PushChar(StackChar *p, char e);//运算符压栈
void PopChar(StackChar *p, char *e);//运算符出栈
//-----------------------
void Fun(StackNum *p, char e);//计算并压入运算数栈
//-----------------------
void main()
{
int i;//循环变量
Num temp;//存放一个临时转换数
char str[MAX], ch;//存放中缀表达式原式,临时运算符
//-----------
StackNum n1;
StackChar c1;
InitNum(&n1);
InitChar(&c1);
//------------
for (;;)
{
printf("请输入中缀表达式:");
gets(str);
/*
注意字符串输入函数与scanf("%s",str) 的区别,scanf遇到空白字符,
包括空格,制表符,换行符时均会停止输入,所以不可取,而gets功能为读入一行,
并将换行符转换为字符串结束符。
*/
for (i = 0; str[i] != '\0'; i++)//读完整字符串-----字符串结束标志'\0'
{
if (str[i] >= '0'&&str[i] <= '9')//分岔点一:----------------------------------------------------------------如果为数字
{
temp = str[i] - '0';//-----将字符转换为数值
while (str[i + 1] != '\0')//多位数值获取
{
if (str[i + 1] >= '0'&&str[i + 1] <= '9')
{
temp = temp * 10 + str[i + 1] - '0';//------注意!
i++;
}
else
break;//如果不是多位数字,则跳出多位获取循环
}
PushNum(&n1, temp);//将获取来的数值入栈
}
else if (str[i] == '+' || str[i] == '-' || str[i] == '*' || str[i] == '/' || str[i] == '(' || str[i] == ')')//分岔点二:-------如果为运算符
{
switch (str[i])//表达式可为:整型/字符型/枚举型-----C语言中
{
//case 后可为 整型,字符型----C语言中
case '+':
if (c1.data[c1.top - 1] != '+'&&c1.data[c1.top - 1] != '-'&&c1.data[c1.top - 1] != '*'&&c1.data[c1.top - 1] != '/')
{
PushChar(&c1, '+');
}
else//如果不然,则将之前的先都出栈并计算,然后再入栈
{
while (c1.top > 0 && c1.data[c1.top - 1] != '(')//将优先级高的运算符先输出计算,其中括号内的优先级最高
{
PopChar(&c1, &ch);
Fun(&n1, ch);//计算,并压运算数栈
}
PushChar(&c1,'+');
}
; break;
case '-':
if (c1.data[c1.top - 1] != '+'&&c1.data[c1.top - 1] != '-'&&c1.data[c1.top - 1] != '*'&&c1.data[c1.top - 1] != '/')
{
PushChar(&c1, '-');
}
else//如果不然,则将之前的先都出栈并计算,然后再入栈
{
while (c1.top > 0 && c1.data[c1.top - 1] != '(')//将优先级高的运算符先输出计算,其中括号内的优先级最高
{
PopChar(&c1, &ch);
Fun(&n1, ch);//计算,并压运算数栈
}
PushChar(&c1, '-');
}
; break;
case '*':
if (c1.data[c1.top - 1] != '*'&&c1.data[c1.top - 1] != '/')
{
PushChar(&c1, '*');
}
else//如果不然,则将之前的先都出栈并计算,然后再入栈
{
while (c1.top > 0 && c1.data[c1.top - 1] != '(')//将优先级高的运算符先输出计算,其中括号内的优先级最高
{
PopChar(&c1, &ch);
Fun(&n1, ch);//计算,并压运算数栈
}
PushChar(&c1, '*');
}
; break;
case '/':
if (c1.data[c1.top - 1] != '*'&&c1.data[c1.top - 1] != '/')
{
PushChar(&c1, '/');
}
else//如果不然,则将之前的先都出栈并计算,然后再入栈
{
while (c1.top > 0 && c1.data[c1.top - 1] != '(')//将优先级高的运算符先输出计算,其中括号内的优先级最高
{
PopChar(&c1, &ch);
Fun(&n1, ch);//计算,并压运算数栈
}
PushChar(&c1, '/');
}
; break;
case '(':
PushChar(&c1, '(');
; break;
case ')'://并没有将'('压入栈中,只是当作一种出栈信号
while (c1.data[c1.top - 1] != '(')
{
PopChar(&c1, &ch);
Fun(&n1, ch);//计算,并压运算数栈
}
PopChar(&c1, &ch);//将'('也出栈,但并不计算
; break;
}
}
}
while (c1.top > 0)//将剩余的运算符出栈并计算
{
PopChar(&c1, &ch);
Fun(&n1, ch);
}
printf("\t\t%s=%.2f", str, GetNum(n1));
printf("\n");
system("pause");
}
}
void InitNum(StackNum *p)
{
p->top = 0;
}
void InitChar(StackChar *p)
{
p->top = 0;
}
void PushNum(StackNum *p, Num e)
{
if (p->top == MAX)
printf("运算数栈满!\n");
else
{
p->data[p->top] = e;
p->top++;
}
}
void PushChar(StackChar *p, char e)
{
if (p->top == MAX)
printf("运算符栈满!\n");
else
{
p->data[p->top] = e;
p->top++;
}
}
void PopNum(StackNum *p, Num *e)
{
if (p->top == 0)
printf("运算数栈空!\n");
else
{
p->top--;
*e = p->data[p->top];
}
}
void PopChar(StackChar *p, char *e)
{
if (p->top == 0)
printf("运算符栈空!\n");
else
{
p->top--;
*e = p->data[p->top];
}
}
void Fun(StackNum *p, char e)
{
Num temp1, temp2;//存放两个临时操作数
PopNum(p, &temp2);
PopNum(p, &temp1);
switch (e)
{
case '+':PushNum(p, temp1 + temp2); break;
case '-':PushNum(p, temp1 - temp2); break;
case '*':PushNum(p, temp1*temp2); break;
case '/':PushNum(p, temp1 / temp2); break;
}
}
Num GetNum(StackNum p)
{
return p.data[p.top - 1];
}
因为我也是个小菜鸟,所以本次也全当作笔记总结的文章,我又找了两篇我参考的大佬的文章,如下:
原文:https://blog.csdn.net/myCsdn_Xm/article/details/80861183
题目描述
为了便于处理表达式,常常将普通表达式(称为中缀表示)转换为后缀{运算符在后,如X/Y写为XY/表达式。在这样的表示中可以不用括号即可确定求值的顺序,如:(P+Q)(R-S) → PQ+RS-。后缀表达式的处理过程如下:扫描后缀表达式,凡遇操作数则将之压进堆栈,遇运算符则从堆栈中弹出两个操作数进行该运算,将运算结果压栈,然后继续扫描,直到后缀表达式被扫描完毕为止,此时栈底元素即为该后缀表达式的值。
输入
输入一行表示后缀表达式,数与数之间一定有空格隔开(可能不只一个空格),最后输入@表示输入结束。
数据保证每一步的计算结果均为不超过100000的整数。
输出
输出一个整数,表示该表达式的值.
样例输入
14 3 20 5 / *8 - + @
样例输出
18
#include
typedef struct STRACK //定义结构体
{
double a[100];
int top;
} STRACK;
int main()
{
double totle=0,e=0;
char s[100];
int i;
STRACK L;
L.top=-1;
gets(s);
for(i=0; s[i]!='@'; i++)
{
if(s[i]<='9'&&s[i]>='0')
{
L.top++;
int temp=s[i]-'0';
int k=i+1;
while(s[k]!='@') //利用while循环得到由多位由字符组成的数值
{
if(s[k]<='9'&&s[k]>='0')
{
temp=10*temp+(s[k]-'0');
i++;
k++;
}
else break;
}
L.a[L.top]=temp;
}
else if(s[i]=='+'||s[i]=='-'||s[i]=='*'||s[i]=='/') //遇到运算符进行计算
{
switch(s[i])
{
case '+':
e=L.a[L.top-1]+L.a[L.top];
break;
case '-':
e=L.a[L.top-1]-L.a[L.top];
break;
case '*':
e=L.a[L.top-1]*L.a[L.top];
break;
case '/':
e=L.a[L.top-1]/L.a[L.top];
break;
}
L.a[L.top-1]=e; //往前一位存储
L.a[L.top]=0;
L.top--;
}
}
printf("%.0lf",L.a[L.top]); //输出最后结果
return 0;
}
原文:https://blog.csdn.net/hanmiaobei7428/article/details/82049881
表达式的求值问题(堆栈)
0. 解决目标
将形如2*(9+6/3-5)+4表达式求值的思想
(翻译成中缀表达式为:6/2-3+4*2,我们不进行中缀表达式的翻译操作,只是为了方便理解中间的过程)
从左向右“扫描”,逐个处理运算数和运算符号
遇到运算数怎么办?如何“记住”目前还不未参与运算的数?
遇到运算符号怎么办?对应的运算数是什么?
下图是一种解决办法。
这里使用一种结构,由于长得像先称之为“槽”,这种槽有什么特点?
1.只能存放数字
2.存放的数字只能后面进来的先出
这里有个问题,如果是符号怎么办呢?
我们提供一种解决办法,如果遇到符号,则将槽最顶部的数字与前一个数字从槽中拿出,进行操作,操作为:
1.前一个数字 运算符 槽最顶部的数字
2.并讲运算结果 再放入槽中
3.直至所有东西都按从左到右的顺序 尝试进入槽中,便得到结果。
这种能够解决后缀表达式的求值问题的结构——“槽”,就是堆栈。它是一种线性存储结构,后入先出。
我们用堆栈解决了后缀表达式的求值问题,那么问题来了,如何将中缀表达式转换成后缀表达式呢?
##2. 中缀化后缀
目标:将形如2*(6/3+4)-5的中缀表达式化成 2 6 3 / 4 + * 5 -的后缀表达式
带括号的表达式看起来比较复杂,我们先看没有括号的转换。
小目标:将形如2+9/3-5的中缀表达式化成2 9 3 / + 5 - 的后缀表达式
构造一种堆栈,只能存放符号,同样遵循后入先出的原则。
有两个问题
1.遇到数字怎么办?
2.堆栈中的符号怎么处理?
第一个问题很简单,输出即可,因为我们只需要求表达式,并不需要同时计算。
第二个问题,因为符号有优先级,当将符号放入堆栈时,比较其与前一个符号的优先级,若低于,则先输出前一个运算符。这个也很好理解,高优先级的运算先进行。
解决过程如下图所示。
那如果带括号要怎么解决呢?问题有:
1.括号也算一种符号,但括号不参与运算,
2.括号提供一种优先级,括号里面的运算优先级最高
第一个问题,我们在后缀表达式转换成值的时候是直接进行操作的,利用顺序已经将括号的功能包括进去,但只是不显示括号而已。具体解决是在一对括号齐全时,将其中的运算符输出。
第二个问题,我么将括号放入堆栈之前认为其优先级最高,在放入堆栈之后,将其认为优先级最低,即只进行括号里面的优先级比较(忽略括号)。
解决过程如下图。
总结中缀表达式转化成后缀表达式的方法如下:
学习自《数据结构:陈越》之线性结构
呼,终于把最近看的,学的,总结起来了。。。
我如果有什么地方弄得不对的,看到的道友可以在下方评论说出来,或者私信我。
学习使我们快乐~