这篇的目的一样是为了复习,有错误的地方还请大家指正(这里的代码部分也比较混乱,主要是记录思路了)。
---------------------------------------------------------------------------------------------------------------------------------
在上学期的时候,我曾遇到过这样一道题:对给定的表达式(仅存在加减法)求值。当时的代码是这样的:
#include<stdio.h>
int main()
{
float a,b;
char x;
scanf("%f",&a);
while((x=getchar())!='\n')
{
scanf("%f",&b);
switch(x){
case'+':a=a+b;break;
case'-':a-=b;break;
case'*':a*=b;break;
case'/':a/=b;break;
};
}
printf("%f",a);
return 0;
}
这里仅仅需要使用一个switch和一个循环就能完成计算。但是这里便存在了一个问题:如果参与运算的符号不仅仅是加减法,而是优先级更高的乘除甚至次方,那么我们又该如何计算呢?由于计算的优先级问题,我们肯定不能只是进行简单的从左到右计算,我们需要判断出计算的先后。
栈
为了解决这个问题,我们将使用栈这项数据结构。栈存储数据的特点为先进后出(First In Last Out),其定义大致如下:
typedef struct Stack
{
int data[Max_Size];
int top;//top是表示栈顶元素的下标
}stack;
仔细研究计算表达式我们会发现,如果一个运算符优先级大于等于左右两边运算符的优先级,那么该运算符则会优先计算。(如下表)
1+2*3+4 2*3优先计算
1+2*3*4 2*3优先计算
1+2*3^4 2*3不能优先计算
那么我们建立两个栈,一个栈来收集数据(即输入的数字),一个栈用来收集运算符。我们将表达式中的数字/运算符号逐个放入栈中,然后依次计算(即边将数字放入栈中边进行计算)。具体如下图所示。
这里对栈的操作涉及初始化,进栈,出栈的操作,具体操作方式如下(由于运算符与数字的类型不一样,所以同一个操作我们需要写两个函数):
void InitStack(stackint *s)
{
s->top= -1;
};//初始化的过程中,直接设置空栈
int push(stackint *s,int x)
{
s->top = s->top+1;
s->data[s->top]=x;
return True;
};
int pop(stackint *s,int *x)
{
*x=s->data[s->top];
s->top--;
return False;
};
void InitStack_char(stackchar *s)
{
s->top= -1;
};
int push_char(stackchar *s,char x)
{
s->top = s->top+1;
s->data[s->top]=x;
return True;
};
int pop_char(stackchar *s,char *x)
{
*x=s->data[s->top];
s->top--;
return True;
};
那么最终我们写出的代码为 :
/*无括号运算表达式的值*/
/*表达式的优先级:1.^ 2. * / 3.+ - */
#include<stdio.h>
#include<stdlib.h>
#define True 1
#define False 0
#define Max_Size 50
typedef struct Stack_Int
{
int data[Max_Size];
int top;//top是表示栈顶元素的下标
}stackint;
typedef struct Stack_Char
{
char data[Max_Size];
int top;//top是表示栈顶元素的下标
}stackchar;
void InitStack(stackint *s);
int push(stackint *s,int x);
int pop(stackint *s,int *x);//这里pop函数的返回值的目的是判断函数是否成功
void InitStack_char(stackchar *s);
int push_char(stackchar *s,char x);
int pop_char(stackchar *s,char *x);
int compare(stackchar s);
int caculate(int x,int y);//x^y
int compute(int x,int y,char z);
int main()
{
stackint si;
stackchar sc;//当想要使用指针的时候一定要注意是否开辟了空间
int x,z,i;//x为输入的数据,该程序里所有输入的数据都由栈来保存,故仅使用一个变量x//z接收吐出来的变量,类似垃圾桶//i为循环变量
char y,q;//y的解释如上
InitStack(&si);
InitStack_char(&sc);
push(&si,0);
push_char(&sc,'+');//是栈中最底层的符号为优先级最低的'+'
scanf("%d",&x);
push(&si,x);
while((y=getchar())!='\n')
{
push_char(&sc,y);
scanf("%d",&x);
push(&si,x);
if(sc.data[sc.top]>=3)//当储存符号的栈内的元素超过三个时就开始判定
{
while(compare(sc) == 1)
{
x=compute(si.data[si.top-2],si.data[si.top-1],sc.data[sc.top-1]);
sc.data[sc.top-1]=sc.data[sc.top];
pop_char(&sc,&q);
si.data[si.top-2]=x;
si.data[si.top-1]=si.data[si.top];
pop(&si,&z);
}
};
};
/*完成最后内部的加减就可以了,可知最后的数字将比运算符号多一个*/
for(i=sc.top;i>=0;i--)//注意判断条件不要搞错了
{
if(sc.data[i] == '*')
{
x=si.data[si.top]*si.data[si.top-1];
pop(&si,&z);
pop(&si,&z);
push(&si,x);
}else if(sc.data[i] =='^')
{
x=caculate(si.data[si.top-1],si.data[si.top]);
pop(&si,&z);
pop(&si,&z);
push(&si,x);
}else if(sc.data[i] == '/')
{
x=si.data[si.top-1]/si.data[si.top];
pop(&si,&z);
pop(&si,&z);
push(&si,x);
}else if(sc.data[i] == '+')
{
x = si.data[si.top]+si.data[si.top-1];
pop(&si,&z);
pop(&si,&z);
push(&si,x);
}else{
x=si.data[si.top-1]-si.data[si.top];
pop(&si,&z);
pop(&si,&z);
push(&si,x);
};
};
printf("%d",si.data[0]);
return 0;
}
void InitStack(stackint *s)
{
s->top= -1;
};//初始化的过程中,直接设置空栈
int push(stackint *s,int x)
{
s->top = s->top+1;
s->data[s->top]=x;
return True;
};
int pop(stackint *s,int *x)
{
*x=s->data[s->top];
s->top--;
return False;
};
void InitStack_char(stackchar *s)
{
s->top= -1;
};
int push_char(stackchar *s,char x)
{
s->top = s->top+1;
s->data[s->top]=x;
return True;
};
int pop_char(stackchar *s,char *x)
{
*x=s->data[s->top];
s->top--;
return True;
};
int compare(stackchar s)//这个函数的作用是用来比较运算符的优先级,并且默认了栈中至少含有两个元素
{
if((s.data[s.top-1]=='*' || s.data[s.top-1]== '/')&&(s.data[s.top]!='^'))
{
return True;
}else if(s.data[s.top-1]=='^')
{
return True;
}else
{
return False;
}
};
int caculate(int x,int y)//x=0,y=5
{
int n,i;
for(n=1,i=0;i<y;i++)
{
n*=x;
};
return n;
};
int compute(int x,int y,char z)
{
int result;
if(z=='*')
{
result = x*y;
}else if(z=='/')
{
result = x/y;
}else if(z=='^')
{
result = caculate(x,y);
};
return result;
};
这里面有几个比较特殊的地方:
1.每次进行运算的都是比+优先级更高的符号,等到最后我们只需要计算加法和减法即可。(即直接顺序进行即可)。
2.在边界的位置(栈底与栈顶)附近只有一个符号参与比较,由于栈顶难以处理(随时都在发生改变),故只能在循环结束后对其进行判断。但是对于栈底,我们加入一个“0+”,这样就能消去栈底的特殊性了。