本题为将一个中缀表达式转化为逆波兰式,同样为栈的应用,以下为原题(づ ̄3 ̄)づ╭❤~
有必要解释以下逆波兰式,其实就是后缀表达式,也就是计算机计算时识别的语言,比如上方的ab+c*,那么计算机就依次读取,a、b然后+,之后就可以计算a+b,然后c、*,也就是把a+b的和与c相乘,稍微详细一点的逆波兰式推荐知乎搜索:逆波兰式到底是什么鬼(虽然提问者有点睿智,但是几个回答者都是dalao),那么下面为题目分析
为了解决这个问题我创建了三个栈S1,S2,S3,其实从中缀到后缀的转化只需要一个栈(而从中缀到前缀则需要两栈),因为只需要将运算符放到栈S1中来回倒就ok(滑稽),额外创建的两个栈.......没什么用,储存表达式罢了,完全可以直接用字符串解决.......睿智了我......
思路分析,依次读取字符串的字符,先将一个“#”压进S1,作为一个比较的基础(\(^o^)/)
1.如果是字符,则直接压倒栈S2中,(知道为啥有S3了吗,因为压到S2中后输出是反的!反的!迫不得已最后再倒进S3在输出,哭唧唧)
2.如果是运算符,则就需要比较这个运算符和栈顶元素的优先级别了,假设之前栈顶的运算符为c1,后来读取的运算符为c2。
则有:如果c2优先级高于c1,那么c2压进S1栈储存
如果c2优先级低于c1,那么S1将c1弹出并压进S2,然后比较新的S1的栈顶元素和c2,如果还是c2优先级别低,那就再弹一个c1,最后直到c1的优先级别低于c2,此时将c2压进栈S1。(大概就这样....其实意思就是要是c2想进栈,栈顶元素的优先级必须比他小,别忘了最下边还压着一个“#”,这玩意优先级别最低)
那么他们的优先级顺序如下图所示,(很重要!!但是不需要背啊啥的,看看就懂了,乘除优先于加减,括号优先,就酱=_=)
需要特别注意为‘(’,因为左括号的优先级其实是有改变的,如果c2是左括号,则优先级别最高,如果c1是左括号,则优先级别最低,稍微理解一下,因为如果下一个来的是左括号的话,那么你必须先运算括号里边的,所以优先级别超级高,但是如果左括号已经压进栈内了,那么你必须等到有一个右括号来跟他(交配),才能完成括号内的计算。
好了思路就这样,上代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct Node{//定义带有节点的链表
char info;
struct Node *next;
}Node;
typedef struct linkstack{//定义栈的栈顶指针
struct Node *top;
}stack;
stack *creat(void)
{
stack *plstack;//创建一个空栈并返回指针
plstack=(struct linkstack*)malloc(sizeof(struct linkstack));
if(plstack!=NULL)
plstack->top=NULL;
else
printf("out!\n");
return plstack;
}
int is_empty(stack *plstack)//检查栈是否为空
{
return(plstack->top==NULL);
}
void push(stack *plstack,char x)//动态申请并将一元素入栈
{
struct Node *p;
p=(struct Node*)malloc(sizeof(struct Node));
if(p==NULL)
printf("out!\n");
else
{
p->info=x;
p->next=plstack->top;
plstack->top=p;
}
}
char pop(stack *plstack)
{//如果栈不为空则将栈顶元素出栈并返回该元素
struct Node *p;
char elem;
if(is_empty(plstack))
printf("out!5");
else
{
p=plstack->top;
elem=p->info;
plstack->top=plstack->top->next;
free(p);
}
return elem;
}
char toplink(stack *plstack)//如果栈非空则返回栈顶元素
{
if(is_empty(plstack))
printf("out!7");
return plstack->top->info;
}
stack *nibolan(char *e)
{
stack *s1,*s2,*s3;
s1=creat();
s2=creat();
s3=creat();//创建三个栈
push(s1,'#');//在一个中压进#
char *p=e,ch,cp;
int length=0;
for(;*p!='\0';p++)
{
switch(*p)
{
case'(':{
push(s1,*p);
break; //左括号直接入栈
}
case')':{ //如果是右括号则将左括号之前的全部出栈并依此压进2栈
while(toplink(s1)!='(')
{
ch=pop(s1);
push(s2,ch);
}
cp=pop(s1); //将左括号出栈,不要了,因为后缀表达式不需要括号
break;
}
case'+':
case'-':{
for(ch=toplink(s1);ch!='#';ch=toplink(s1))//一出现加减号那么可以说一下就可以运算之前所有的
{ //如果是加号减号则检查栈顶元素
if(ch=='(')
{
break; //如果是左括号那显然直接压进去就ok
}
else{ //但是如果是加减乘除号,就要考虑往外弹了,以为之前压进去的加减号的优先级也是大于后来的加减号
ch=pop(s1); //优先级低依此出栈并压进2栈
push(s2,ch);
}
}
push(s1,*p); //到最后再吧这个加减号压进S1
length++;
break;
}
case'*':
case'/':{
for(ch=toplink(s1);ch!='#'&&ch!='+'&&ch!='-';ch=toplink(s1))
{ //如果是乘号和除号则判定是否优先 比他低的优先级就是加减号
if(ch=='(')
{
break;
}
else{//同样 之前的若是乘除号,也是先进的优先级高于后来的
ch=pop(s1);
push(s2,ch);
}
}
push(s1,*p); //直到碰到优先级高直接压进
length++;
break;
}
default:
push(s2,*p); //其他数字直接压进2栈
length++;
}
}
while(!is_empty(s1)&&toplink(s1)!='#')
{ //结束后1栈不空则依此从出栈并压进2栈
ch=pop(s1);
push(s2,ch);
}
while(!is_empty(s2))
{ //依此将2栈出栈压进三战逆置
ch=pop(s2);
push(s3,ch);
}
return (s3);
}
void show(stack *p)
{ //从栈顶元素开始出栈,并输出出栈元素
stack *a=p;
while(!is_empty(a))
{
printf("%c",a->top->info);
pop(a);
}
}
int main()
{
char a[100],*e;
stack *c;
e=&a[0];
scanf("%s",a);
c=nibolan(e); //调用逆波兰函数得到栈3
show(c); //输出逆波兰式
return 0;
}