栈的应用之算术表达式的求值:
思想: 建立两个栈,一个操作数栈,一个运算符栈,遍历算术表达式字符串,读取的是数,将值压入操作数栈中,读取下一个字符;读取的是运算符,将读取运算符与栈顶运算符优先级进行比较:如果栈顶运算符优先级低,则将读取运算符直接压入运算符栈中,然后读取下一个字符。如果栈顶运算符优先级高,则出进行“计算操作”,操作数栈中出栈两个数,假如先出的为a,后出的为b,运算符栈中出一个运算符,假设为op,进行b op a计算,然后将计算结果再压入操作数栈中,一直进行“计算”操作,直到运算符栈顶运算符的优先级比所读取的运算符优先级低,然后将读取运算符压入运算符栈中,读取下一个字符。读取完最后一个字符后,如果运算符栈不为空,则进行“计算”操作,一直到运算符栈为空,最后操作数栈的栈顶数值,即为最终所求的结果。
具体算法思路:用两个栈Oper和Optr来存放读取的操作数和运算符,Optr先压入一个#符号,遍历输入的算术表达式字符串(以#符号结尾)
1.读取的是操作数字符,因为每次只能读取一个字符,所以需要计算连续的操作数字符的值,压入操作数栈Oper中
2.读取的是运算符(只有七种:+、-、*、/、(、)、#)
(1)如果读取的是(、*、/三者之一,直接压入Optr栈中,因为“(”是一个子表达式的开始,而“*”和“/”的优先级是最高的
(2)如果读取的是+,-之一,如果Optr栈顶运算符为*或者\,则进行“计算”操作,直到Optr栈顶运算符不为*或者\为止,将+或-压入Optr栈中
(3)如果读取的是),则进行“计算”操作,直到Optr栈顶元素为(为止,然后将(出栈
(4)如果读取的是#,则进行“计算”操作,直到Optr栈顶元素为#为止,此时即运算完毕
计算操作:Oper栈先后出两个数a,b,Optr栈中出一个运算符 op,进行b op a的计算,将计算结果压入Oper中即可
Oper栈顶数值即最终的运算结果。
代码如下:按照自己拆开的思路自己写的,是用顺序栈实现的,有问题还请各位指出哈,新手一枚,第一篇博客。
//算术表达式求值
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define Maxsize 128
#define N sizeof(SqStack)
typedef double ElemType; //int型的只能求出整数,改成double可求出小数位!!!
typedef struct
{
ElemType data[Maxsize]; //数据域
int top; //栈顶"指针"
}SqStack;
int main(void)
{
bool push(SqStack * p, ElemType n);
ElemType pop(SqStack * p); //出栈,函数类型是栈元素数据类型
ElemType getTop(SqStack * p); //获取栈顶元素,注意函数返回类型也是栈元素数据类型
ElemType evaluate_a_expression(char* s); //求s指向字符串算术表达式的值
char string[Maxsize];
ElemType result;
printf("Please input the arithmetic operation expression(don't have Spaces and ends with a # sign):\n");
scanf("%s", string);
result = evaluate_a_expression(string);
printf("the result of arithmetic operation expression: %f", result);
return(0);
}
SqStack* creat(void) //创建栈,将top初始化为-1
{
SqStack* k = (SqStack*)malloc(N);
k->top = -1;
return(k);
}
bool push(SqStack* p, ElemType n) //进栈
{
//进栈先判断栈满!!!
if (p->top == Maxsize - 1)
return(false);
p->data[++p->top] = n;
return(true);
}
ElemType pop(SqStack* p) //出栈
{
//出栈先判断栈空!!!
if (p->top == -1)
return(-1); //返回-1表示出栈失败
return(p->data[p->top--]);
}
ElemType getTop(SqStack* p) //获取栈顶元素
{
//获取栈顶元素也要判断栈空!!!
if (p->top == -1)
return(-1); //返回-1表示取值失败
return(p->data[p->top]); //直接返回栈顶元素
}
ElemType evaluate_a_expression(char* s)
{
SqStack* Optr, * Oper;
ElemType d, result;
bool calculate_press(SqStack * Oper, SqStack * Optr);
Optr = creat(); //运算符栈
push(Optr, '#'); //字符#先压入栈中
Oper = creat(); //操作数栈
for (; *s != '\0'; s++)
{
if (*s >= '0' && *s <= '9')
{
d = 0;
while (*s >= '0' && *s <= '9') //用一个循环来计算连续的数字字符组成的值
{
d = 10 * d + *s - '0';
s++;
}
s--; //跳出循环,s指向数字字符的后面字符,为了同步,再--,否则将会跳过读取一个字符
//将求的值压入Oper栈中
push(Oper, d);
}
else
{
if (*s == '(' || *s == '*' || *s == '/')
push(Optr, *s); //乘和除优先级最高,(左括号,都直接进操作符栈
else if (*s == ')')
{
while (getTop(Optr) != '(') //没到左括号,一直计算
calculate_press(Oper, Optr);
pop(Optr); //左括号出栈
}
else if (*s == '#')
{
while (getTop(Optr) != '#') //还剩有计算没做,继续运算
calculate_press(Oper, Optr);
break;
}
else if (*s == '+' || *s == '-')
{
char top_element = getTop(Optr); //取运算符栈顶操作符,与读取的操作符比较优先级
switch (top_element)
{
case '+':
case '-':
case '(':
case '#':
push(Optr, *s);
break; //栈顶元素优先级低,直接将所读操作符入栈,读取下一个
case '*':
case '/': //栈顶元素优先级高,则进行“计算并且压入”操作
do{
calculate_press(Oper, Optr); //先做一次计算
top_element = getTop(Optr); //取新的栈顶运算符
}while (top_element == '*' || top_element == '/'); //栈顶元素优先级高则继续计算,直到栈顶元素优先级低,
//到栈顶元素优先级低,压入读取元素
push(Optr, *s);
break;
}
}
else
{
printf("error!");
exit(0);
}
}
}
result = getTop(Oper); //操作数栈顶元素即所求结果
free(Oper);
free(Optr); //操作数栈和运算符栈释放掉
return(result);
}
//计算函数: 操作数栈中出2个数和运算符栈中出1个操作符做计算,结果压入操作数栈中
bool calculate_press(SqStack* Oper, SqStack* Optr)
{
ElemType a, b, result;
char k = pop(Optr); //运算符栈中出一个运算符
a = pop(Oper);
b = pop(Oper); //操作数栈中出两个数
switch (k)
{
case '+':
result = b + a; break;
case '-':
result = b - a; break;
case '*':
result = b * a; break;
case '/':
if (a == 0)
{
printf("The denominator is 0 error!"); //输出错误信息后终止程序
exit(0); //该函数在 stdlib.h 的头文件中,与malloc等函数一样
}
else
result = b / a;
break;
}
push(Oper, result); //结果压入Oper操作数栈中
return(true);
}