计算器我们都实现了很多了,但是,我们写的计算器都是求解两个操作数的,并不能求解一个表达式,如今栈已经学完了,那么我们可不可以使用栈来实现表达式的求解呢?
我们数学上求解一个表达式是如何进行的,有括号的先求括号内部的,之后先算乘除,再算加减。现在如果来实现一个这样可以求解表达式的计算器,怎样进行呢?
首先是将中缀表达式转化为后缀表达式,具体的规则如下:
1、遇到操作数,直接输出;
2、栈为空时,遇到运算符,入栈;
3、遇到左括号,将其入栈;
4、遇到右括号,执行出栈操作,并将出栈的元素输出,直到弹出栈的是左括号,左括号不输出;
5、遇到其他运算符’+”-”*”/’时,弹出所有优先级大于或等于该运算符的栈顶元素,然后将该运算符入栈;
6、最终将栈中的元素依次出栈,输出。
之后就是后缀表达式的求解了,大致是这样的:
扫描后缀表达式, 如果是数字,则让其进栈; 若为操作符,则从栈中取出两个操作数,先取出的作为右操作数,后取出的作为左操作数,然后进行该操作符的运算,并使其结果入栈。 重复上述过程,直至表达式扫描完成。 最终栈中只留一个元素,就是表达式结果。
下面是具体的实现代码:
#include<iostream>
using namespace std;
#include<assert.h>
#include<stack>
//获取操作符的优先级
int GetPriority(char ch, int flag)//flag为1时表示栈内优先级 flag为0表示栈外优先级
{
if (ch == '+' || ch == '-')
{
if (flag)
{
return 3;
}
else
{
return 2;
}
}
else if (ch == '*' || ch == '/' || ch == '%')
{
if (flag)
{
return 5;
}
else
{
return 4;
}
}
else if (ch == '(')
{
if (flag)
{
return 1;
}
else
{
return 6;
}
}
else if (ch == ')')
{
if (flag)
{
return 6;
}
else
{
return 1;
}
}
}
//中缀表达式转化后缀表达式
void InfixToPostfix(char *dest, char*src)
{
assert(dest);
assert(src);
stack<char> s;//保存操作符,使其按照优先级从大到小输出
char* _cur = src;//用一个指针指向中缀表达式
char* _temp = dest;//用指针指向存储后缀表达式的空间
while (*_cur != '\0')
{
if (*_cur >= '0' && *_cur <= '9')//如果是数字字符,那么保存
{
*_temp++ = *_cur;
_cur++;
continue;
}
//如果是操作符,那么分情况讨论
else if (*_cur == '+' ||* _cur == '-' || *_cur == '*' || *_cur == '/' || *_cur == '%' || *_cur == '(' || *_cur == ')')
{
if (s.empty())//
{
s.push(*_cur);
_cur++;
}
else
{
if (*_cur == ')')
{
while (s.top() != '(')
{
*_temp++ = s.top();
s.pop();
}
s.pop();//删除栈顶的‘(’
*_cur++;
}
//如果当前操作符的优先级大于栈顶元素的优先级,将当前操作符入栈
if (GetPriority(*_cur, 0) > GetPriority(s.top(), 1))
{
s.push(*_cur);
_cur++;
}
else
{
while (!s.empty() && GetPriority(*_cur, 0) < GetPriority(s.top(), 1))
{
*_temp++ = s.top();
s.pop();
}
s.push(*_cur);
_cur++;
}
}
}
else//跳过空格
{
*_temp++ = *_cur++;
}
}
//将栈内剩余元素放入表达式
while (!s.empty())
{
*_temp++ = s.top();
s.pop();
}
}
//后缀表达式求值(计算器)
int Calculate(char *_postfix,char*_infix)
{
InfixToPostfix(_postfix, _infix);
stack<int> s;//注意前后两个栈的使用用途不同,实例化也不同
char*_cur = _postfix;
int temp = 0;//保存数字字符转化后的数字
int res = 0;//保存运算结果
while (*_cur != '\0')
{
if (*_cur >= '0'&&*_cur <= '9')
{
res = 0;
while (!isspace(*_cur) && *_cur >= '0'&&*_cur <= '9')
{
temp = *_cur - '0';//将数字字符转化为数字
res = res * 10 + temp;// 例如:12
_cur++;
}
s.push(res);
}
else if (*_cur == '+' || *_cur == '-' || *_cur == '*' || *_cur == '/' || *_cur == '%')
{
int right = s.top();//先取出来的是右操作数
s.pop();
int left = s.top();
s.pop();
switch (*_cur)
{
case '+':
s.push(left + right);
break;
case '-':
s.push(left - right);
break;
case '*':
s.push(left * right);
break;
case '/':
if (right)
{
s.push(left / right);
break;
}
case '%':
s.push(left % right);
break;
}
_cur++;
}
else//跳过空格
{
_cur++;
}
}
if (!s.empty())
{
res = s.top();
s.pop();
return res;
}
}
void FunTest()
{
char* _infix = "12 * (3 + 4) - 6 + 8 / 2 ";
char _postfix[20] = {};
int result = Calculate(_postfix, _infix);
cout <<result << endl;
}
int main()
{
FunTest();
return 0;
}