无语死了,好在终于弄出来了。
题目:利用栈编写表达式求值程序:输入含有“+”、“-”、“*”、“/”四则运算的表达式,其中负数要用(0-正数)表示,并以=结束。要求输出表达式的值(两运算符号的有限关系见教材《数据结构》表3.1)。
测试样式:
【第一组自测数据】
【键盘输入】
3*(9-7)#↙
【正确输出】
6
【第二组自测数据】
【键盘输入】
(0-12)*((5-3)*3)/(2+2)=
【正确输出】
-18
完整代码
typedef int Status;
typedef char SElemType;
typedef double SElemType2;
#include"malloc.h"
#include"stdio.h"
#include"math.h"
#include"stdlib.h"
#include"string.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define STACK_INIT_SIZE 10 //存储空间初始分配量
#define STACKINCREMENT 2 //存储空间分配增量
///////////////////////////////////////////////////////////////
//gets了字符串后,要将字符串中的【运算符】和【运算数】分开。
//故而要建立两个栈,一个存储【运算符】,另一个存储【运算数】。
//【运算符栈】为char类型,【运算数栈】为double类型。
//因此,相应的,对于两个种同类型的栈,就要有两种不同类型的基本操作。
///////////////////////////////////////////////////////////////
struct SqStack //运算符型的
{
SElemType * base;
SElemType * top;
int stacksize;
};
struct SqStack2 //运算数型的
{
SElemType2 * base;
SElemType2 * top;
int stacksize;
};
Status InitStack(SqStack &S) //运算符栈的初始化操作
{
S.base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType));
if(!S.base) exit(-2);
S.top = S.base;
S.stacksize = STACK_INIT_SIZE;
return OK;
}
Status InitStack2(SqStack2 &S) //运算数栈的初始化操作
{
S.base = (SElemType2 *)malloc(STACK_INIT_SIZE * sizeof(SElemType2));
if(!S.base) exit(-2);
S.top = S.base;
S.stacksize = STACK_INIT_SIZE;
return OK;
}
Status Push(SqStack &S, SElemType e) //运算符栈的进栈操作
{
if(S.top - S.base >= S.stacksize)
{
S.base = (SElemType *)realloc(S.base,(S.stacksize + STACKINCREMENT) * sizeof (SElemType));
if(!S.base) exit(-2);
S.top = S.base + S.stacksize;
S.stacksize += STACKINCREMENT;
}
* S.top = e;
S.top ++;
return OK;
}
Status Push2(SqStack2 &S, SElemType2 e) //运算数栈的进栈操作
{
if(S.top - S.base >= S.stacksize)
{
S.base = (SElemType2 *)realloc(S.base,(S.stacksize + STACKINCREMENT) * sizeof (SElemType2));
if(!S.base) exit(-2);
S.top = S.base + S.stacksize;
S.stacksize += STACKINCREMENT;
}
* S.top = e;
S.top ++;
return OK;
}
Status In(char c) //判断是否为运算符,是运算符则返回 TRUE, 否则返回 FALSE
{
if(c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')' || c == '=') return TRUE;
else return FALSE;
}
SElemType GetTop(SqStack S) //返回运算符栈的栈顶元素
{
SElemType *p;
p = S.top;
p--;
if(S.base == S.top) return ERROR;
return(*p);
}
SElemType2 GetTop2(SqStack2 S) //返回运算数栈的栈顶元素
{
SElemType2 *p;
if(S.base == S.top) return ERROR;
p = S.top;
p--;
return(*p);
}
Status Pop(SqStack &S, SElemType &e) //运算符栈的退栈操作
{
if(S.top == S.base) return ERROR;
S.top --;
e = * S.top;
return OK;
}
Status Pop2(SqStack2 &S, SElemType2 &e) //运算数栈的退栈操作
{
if(S.top == S.base) return ERROR;
S.top --;
e = * S.top;
return OK;
}
//判定运算符栈的栈顶运算符 op1 与读入的运算符 op2 之间有限关系的函数
//这个函数颇蠢,看了别人写的感觉简单明了,不过懒得改了
SElemType Precede(SElemType op1, SElemType op2)
{
switch(op1)
{
case '+':if(op2 == '+' || op2 == '-' || op2 == ')' || op2 == '=') return ('>');
else return ('<'); break;
case '-':if(op2 == '+' || op2 == '-' || op2 == ')' || op2 == '=') return ('>');
else return ('<'); break;
case '*':if(op2 == '(') return ('<');
else return ('>'); break;
case '/':if(op2 == '(') return ('<');
else return ('>'); break;
case '(':if(op2 == ')') return ('=');
else return ('<'); break;
case ')':return ('>'); break;
case '=':if(op2 == '=') return ('=');
else return ('<'); break;
}
}
double Operate(SElemType2 a, SElemType theta, SElemType2 b) //四则运算 a,b为运算数theta 为运算符。
{ //形式为 a theta b
double an;
switch(theta)
{
case'+':an = a + b; break;
case'-':an = a - b; break;
case'*':an = a * b; break;
case'/':an = a / b; break;
}
return(an); //注意返回值为double型
}
/////////////////////////////////////////////////////////////
//这里要考虑到输入的表达式中有两位数及以上的情况
//如该表达式 12*123=
//但表达式又是以字符的形式一个一个字符的接收
//即'1','2'是分开的,不是一下子接收'12'
//故而要加一段代码,主要思想为:
//在接收到一个数字的时候
//先暂存起来
//接收下一个看看是不是还是数字
//是的话就要和前面这个组合起来
//一旦下一个不是,就用atof 将TempData中的字符转为double型数据
/////////////////////////////////////////////////////////////
SElemType2 EvaluateExpression()
{
SqStack OPTR;
SqStack OPND;
InitStack(OPTR);Push(OPTR, '='); //其实'='就是书中的'#'。
InitStack2(OPND);
SElemType MyExpression[80];
SElemType TempData[10];//TempData用来暂存还是字符型时期的“数字”
//如3*(9-7)= 中的3,9,7 只是这时还是字符型
char * p;
char theta, x;
double Data, a, b;
int i = 0; //数组TempData的下标
gets(MyExpression); //输入表达式
p = MyExpression; //令p指向字符型数组MyExpression
//当读入的字符为'=' 且运算符栈OPTR的栈顶元素为'='是,结束该循环.
//书中以'#'='#'作为结束的标志,这里以'='='='作为结束的标志
while(*p != '=' || GetTop(OPTR) != '=')
{
if(!In(*p)) //判断p指向的字符是否为运算符
{
TempData[i] = *p; //接受到一个数字的时候,暂存入TempData
p++; //接收下一个
++i;
if(In(*p)) //一旦下一个不是
{
TempData[i] = '\0'; //就立即给TempData中暂存的数字最后加上结束符
Data=atof(TempData); //atof 功能为:把字符串转换成浮点数 暂存入Data中
Push2(OPND, Data); //将Data入栈
i = 0; //归零
}
}
else
switch (Precede(GetTop(OPTR), *p))
{
case '<':Push(OPTR,*p); //栈顶元素优先权低
p++; break;
case '=':Pop(OPTR,x); //脱括号
p++; break;
case '>':Pop(OPTR, theta);//运算符栈的栈顶元素退栈,进行Operate运算,并将运算结果入栈
Pop2(OPND, b); Pop2(OPND, a);
Push2(OPND, Operate(a, theta, b));
break;
}
}
return GetTop2(OPND);
}
int main(void)
{
printf("%g\n",EvaluateExpression());
return 0;
}
运行结果截图:
【小笔记】
if(!In(*p)) //判断p指向的字符是否为运算符
{
TempData[i] = *p; //接受到一个数字的时候,暂存入TempData
p++; //接收下一个
++i;
if(In(*p)) //一旦下一个不是
{
TempData[i] = '\0'; //就立即给TempData中暂存的数字最后加上结束符
Data=atof(TempData); //atof 功能为:把字符串转换成浮点数 暂存入Data中
Push2(OPND, Data); //将Data入栈
i = 0; //归零
}
}
痛苦死了,起初没写这行。发现过时输入格式为 个位数(运算符)两位数= 时都没错,可一旦反过来 两位数(运算符)个位数 = 就会错。
比如12-3= 结果就等于-20;393-10= 结果就为29。
仔细研究一番,原因实在是太坑爹了。
用393-1= 举例。
gets(MyExpression);
MyExpression =
3 |
9 |
3 |
- |
1 |
= |
\0 |
\0 |
\0 |
… |
指针p先指向第一个,即‘3’,然后将其存入TempD中,直到p指向 '-' ,
则TempData =
3 |
9 |
3 |
\0 |
\0 |
\0 |
\0 |
\0 |
\0 |
\0 |
然后data = atof(TempData) 即data = 393;
到此时第一个数字393已经转换并且存储结束了。
接下来开始转换字符‘1’了。
但这里就出错了!
p指向1,TempData =
1 |
9 |
3 |
\0 |
\0 |
\0 |
\0 |
\0 |
\0 |
\0 |
于是此时data = 193
再接着运算,本来应该393-1的就变成393-193了!
拯救的办法很简单!每当p指完了数字下一个指向运算符时,就紧接着在TempData 已存入的数字后面立即存上一个结束符‘\0’;
此时p应该指向‘=’了,同时TempData =
1 |
\0 |
3 |
\0 |
\0 |
\0 |
\0 |
\0 |
\0 |
\0 |
于是atof在这种情况下当然只管第一个‘\0’之前的数字了!
自然,Data = 1;
做完这个感觉不错。有时候一些错误真是出人意料啊!把自己的脑子锻炼成编译器那样,大概才能成神吧~!