看书学了一晚上这个内容,终于实现了
分为三个步骤:
0. 检查输入是否有误(因为输入其他的非预期字符,程序就会崩溃,我就试着加了一个检查输入的函数)
1. 先将正常的中缀表达式转换为后缀表达式
2. 再进行求值
根据后缀表达式求值比较简单,因为后缀表达式已经有了优先级。
比较难懂的是将中缀表达式转换为后缀表达式,需要考虑很多情况:
1. 如果字符是 '(' ,就直接入操作符栈,因为越内层的括号优先级越高,所以不用考虑什么
2. 如果字符是 ')' ,就要结束一个左括号了,将前面的操作符出栈送入postexp,直到遇到 '('。
3. 如果字符是 '-' 或 '+' ,就要将遇到的第一个 '(' 之前的所有操作符出栈送入postexp。因为相对来说,后面的 '+' 和 '-' 优先级是最低的,低于前面的所有操作符。最后出栈完,再将它压入栈。
4. 如果字符是 '*' 或 '/' , 先判断前面操作符栈底是否为 '*' 或 '/' ,是的话就要将栈里的符号出栈送入postexp。因为 '*' 或 '/' 的优先级低于前面的 '*' 或 '/',是高于前面的 '+' 和 '-' 的。
#include <iostream>
using namespace std;
const int MAXSIZE = 50;
// 字符栈
typedef struct
{
char data[MAXSIZE];
int top;
}Stack;
void InitStack(Stack *&s)
{
s = (Stack*)malloc(sizeof(Stack));
s->top = -1;
}
bool Push(Stack *&s, char e)
{
if (s->top == MAXSIZE - 1)
return false;
s->top++;
s->data[s->top] = e;
return true;
}
bool Pop(Stack *&s, char &e)
{
if (s->top == -1)
return false;
e = s->data[s->top];
s->top--;
return true;
}
bool GetTop(Stack *&s, char &e)
{
if (s->top == -1)
return false;
e = s->data[s->top];
return true;
}
bool StackEmpty(Stack *&s)
{
if (s->top == -1)
return true;
return false;
}
// 将中缀表达式转换成后缀表达式
void trans(char *exp, char postexp[])
{
int i = 0; // postexp 下标
char e; // 给 pop gettop用
Stack *Optr;
InitStack(Optr); //初始化操作符栈
while (*exp != '\0') // 对每个字符进行判断处理
{
switch (*exp)
{
case '(':
Push(Optr, '(');
exp++; //exp指针前移,继续处理下一个字符
break;
case ')': //右括号时,一直出栈,直到遇到 (
Pop(Optr, e);
while (e != '(' ) // 当 e='(' 时,正好把它丢弃了
{
postexp[i++] = e;
Pop(Optr, e);
}
exp++;
break;
case '+':
case '-': // + - 优先级相同,当做同一种情况处理
while (!StackEmpty(Optr)) //操作符栈只要不为空,则一直扫描出栈,直到遇到 ) 。
{
GetTop(Optr, e);
if (e == '(') //后面的 + - ,优先级最低,最后考虑;如果e是 (,则直接入栈即可,所以break
break;
else
{
postexp[i++] = e;
Pop(Optr, e);
}
}
Push(Optr, *exp); //最后将 + - 入栈
exp++;
break;
case '*':
case '/':
while (!StackEmpty(Optr))
{
GetTop(Optr, e);
if (e == '/' || e == '*') // * / 的优先级仅仅低于它前面的 * /,高于前面的 + -,所以要将前面的 * / 弹出栈;+ - 保留,因为新的 * / 会放在栈低,优先级高。
{
postexp[i++] = e;
Pop(Optr, e);
}
else
break; // 其他情况( + - 左括号 )退出,
}
Push(Optr, *exp); //最后将 / * 入栈
exp++;
break;
default:
while (*exp > '0' && *exp < '9') //循环判断是否为数字字符,如果是则保存到postexp,循环判断是因为可能是多位数字
{
postexp[i++] = *exp;
exp++;
}
postexp[i++] = '#'; //以#标志一个数字字串结束
}
}
while (!StackEmpty(Optr)) //扫描完exp后,操作符栈可能还有操作符,将其存到postexp
{
Pop(Optr, e);
postexp[i++] = e;
}
postexp[i] = '\0'; //结束字符串
free(Optr); //销毁栈
}
//--------- 下面是针对数字型栈的
typedef struct
{
double data[MAXSIZE];
int top;
}Stack_num;
void InitStack_num(Stack_num *&s)
{
s = (Stack_num *)malloc(sizeof(Stack_num));
s->top = -1;
}
bool Push_num(Stack_num *&s, double e)
{
if (s->top == MAXSIZE - 1)
return false;
s->top++;
s->data[s->top] = e;
return true;
}
bool Pop_num(Stack_num *&s, double &e)
{
if (s->top == -1)
return false;
e = s->data[s->top];
s->top--;
return true;
}
//---------
double compvalue(char *postexp)
{
Stack_num *num; //操作数栈
InitStack_num(num);
double result; //结果
double a, b; //弹出栈的两个数
double c; //计算弹出栈的两个数
double d; //将连续的数字字符转换成整数保存在d里
while (*postexp != '\0')
{
switch (*postexp)
{
case '+':
Pop_num(num, a); //因为后缀表达式已经有了优先级了,所以可以直接弹出两个数进行计算
Pop_num(num, b);
c = a + b;
Push_num(num, c);
break;
case '-':
Pop_num(num, a);
Pop_num(num, b);
c = b - a; //注意是b-a,因为a先出来,是后面的数字
Push_num(num, c);
break;
case '*':
Pop_num(num, a);
Pop_num(num, b);
c = a * b;
Push_num(num, c);
break;
case '/':
Pop_num(num, a); // a是除数
Pop_num(num, b);
if (a != 0)
{
c = b / a;
Push_num(num, c);
}
else
{
cout << "除0错误!" << endl;
exit(0);
}
break;
default:
d = 0;
while (*postexp >= '0' && *postexp <= '9') //当 *postexp = # 时,就忽略了。
{
d = 10 * d + (*postexp - '0');
postexp++;
}
Push_num(num, d);
}
postexp++; //继续下一个字符
}
Pop_num(num, result);
return result;
}
bool test(char *exp)
{
// start - 是否非法字符
for (int i = 0; exp[i] != '\0'; i++)
{
if ( !((exp[i] >= '0' && exp[i] <= '9') || exp[i] == '+' || exp[i] == '-' || \
exp[i] == '*' || exp[i] == '/' || exp[i] == '(' || exp[i] == ')') )
return false;
}
// end - 是否非法字符
// start - 括号是否匹配
Stack *s;
char e;
InitStack(s);
for (int i = 0; exp[i] != '\0'; i++)
{
switch (exp[i])
{
case '(':
Push(s, exp[i]);
break;
case ')':
if (Pop(s, e))
{
if (exp[i] == ')' && e == '(')
return true;
else
return false;
}
else
return false;
break;
}
}
if (s->top != -1) //栈为空才认为成功
return false;
else
return true;
// end - 括号匹配
return true;
}
int main()
{
char exp[MAXSIZE];
char postexp[MAXSIZE];
while (true)
{
cout << "输入表达式:";
cin >> exp;
if (!test(exp))
{
cout << "非法字符 或 括号不匹配!" << endl;
continue;
}
trans(exp, postexp);
cout << "后缀表达式:" << postexp << endl;
cout << "结果:" << compvalue(postexp) << endl;
}
system("pause");
return 0;
}