最近在学习c++的数据结构时,学到对表达式的求值,正好又看到一个比较基础的c++程序设计题目,即设计一个简易的计算器
首先我们求一个表达式的值时用的是中缀表达式,例如求3 + 6 / 2的值时,我们会先计算6/2的值然后在计算加法,但是在计算机中并不会这么聪明的知道计算的优先级,因此我们需要将表达式转换称为计算机能理解的表达式,这里就涉及到数据结构中的中缀表达式转后缀表达式。
中缀表达式a + b*c + (d * e + f) * g,其转换成后缀表达式则为a b c * + d e * f + g * +。
转换过程需要用到栈,具体过程如下:
1)如果遇到操作数,我们就将操作数作为数字字符保存。
2)如果遇到操作符,则我们将其放入到栈中,遇到左括号时我们也将其放入栈中。
3)如果遇到一个右括号,则将栈元素弹出,将弹出的操作符输出直到遇到左括号为止。注意,左括号只弹出并不输出。
4)如果遇到任何其他的操作符,如(“+”, “*”,“(”)等,从栈中弹出元素直到遇到发现更低优先级的元素(或者栈为空)为止。弹出完这些元素后,才将遇到的操作符压入到栈中。有一点需要注意,只有在遇到" ) “的情况下我们才弹出” ( “,其他情况我们都不会弹出” ( "。
5)如果我们读到了输入的末尾,则将栈中所有元素依次弹出。
具体看看代码的实现。
我们首先定义运算符的优先级
int priority(char a)
{
int temp;
if (a=='*' || a=='/')
{
temp = 2;
}
if (a=='+' || a=='-')
{
temp = 1;
}
return temp;
}
定义完优先级之后就确定输出的字符是数字还是操作符。
bool isOperator(char ch)
{
switch (ch)
{
case '+' : case '-' : case '*' : case '/' :
return true;
default:
return false;
}
}
有了上面两个函数之后就可以开始对中缀表达式进行转换了:
string getPostfixExp(string& infix)
{
stack<char> operator_stack;
string postfix;
for (auto p : infix)
{
if (isOperator(p))
{
while (!operator_stack.empty() && isOperator(operator_stack.top()) && priority(operator_stack.top()) >= priority(p))//当栈里面有操作符之后进行一些列的判断
{
postfix.push_back(operator_stack.top());
postfix.push_back(' ');
operator_stack.pop();
}
operator_stack.push(p);//当首先栈里面没有操作符时将字符串里面的操作符添加进去
}
else if (p == '(')
{
operator_sack.push(p);
}
else if (p == ')')
{
while (operator_stack.top() != '(')
{
postfix.push_back(operator_stack.top());
postfix.push_back(' ');
operator_stack.pop();
}
}
else //代表字符为数字字符
{
postfix.push_back(p);
postfix.push_back(' ');
}
//当遍历到中缀表达式中的最后一个数字字符时,操作符栈里面还剩一个操作符未能进行添加,因此
while (!operator_stack.empty())
{
postfix.push_back(operator_stack.top());
postfix.push_back(' ');
operator_stack.pop();
}
postfix.pop_back();//删除后缀表达式中的最后一个空字符
return postfix;
}
}
经过上面的操作之后就可以利用后缀表达式进行计算啦!
int postfixCalculate(string& postfix)
{
int first, second;
stack<int> num_stack;
for (auto p : postfix)
{
switch (p)
{
case '*':
first = num_stack.top();
num_stack.pop();
second = num_stack.top();
num_stack.pop();
num_stack.push(first * second);
break;
case '/':
first = num_stack.top();
num_stack.pop();
second = num_stack.top();
if (first == 0)
{
cout<<"divide by zero";
break;
}
else
{
num_stack.pop();
num_stack.push(first / second);
break;
}
case '+':
first = num_stack.top();
num_stack.pop();
second = num_stack.top();
num_stack.pop();
num_stack.push(first + second);
break;
case '-':
first = num_stack.top();
num_stack.pop();
second = num_stack.top();
num_stack.pop();
num_stack.push(first - second);
break;
case ' ':
break;
//if the item is a number then push it in the stack
default:
num_stack.push(p - '0'); //数字字符减去'0'就得到了该数字
break;
}
}
int result = num_stack.top();
num_stack.pop();
return result;
}
下面贴出整个完整的代码:
#include
#include
#include
#include
using namespace std;
bool isOperator(char ch);
int priority(char a);
string getPostfixExp(string& infix)
{
stack<char> operator_stack;
string postfix;
for (auto p : infix)
{
if (isOperator(p))
{
while (!operator_stack.empty() && isOperator(operator_stack.top()) && priority(operator_stack.top()) >= priority(p))
{
postfix.push_back(operator_stack.top());
postfix.push_back(' ');
operator_stack.pop();
}
operator_stack.push(p);
}
else if (p == '(')
{
operator_stack.push(p);
}
else if (p == ')')
{
while (operator_stack.top() != '(')
{
postfix.push_back(operator_stack.top());
postfix.push_back(' ');
operator_stack.pop();
}
operator_stack.pop();
}
else
{
postfix.push_back(p);
postfix.push_back(' ');
}
}
while (!operator_stack.empty())
{
postfix.push_back(operator_stack.top());
postfix.push_back(' ');
operator_stack.pop();
}
postfix.pop_back();
return postfix;
}
bool isOperator(char ch)
{
switch(ch)
{
case '+': case '-': case '*': case '/':
return true;
default:
return false;
}
}
int priority(char a)
{
int temp;
if (a == '*' || a == '/')
temp = 2;
else if (a == '+' || a == '-')
temp = 1;
return temp;
}
//得到了后缀表达式,可以开始求值
int postfixCalculate(string& postfix)
{
int first, second;
stack<int> num_stack;
for (auto p : postfix)
{
switch (p)
{
case '*':
first = num_stack.top();
num_stack.pop();
second = num_stack.top();
num_stack.pop();
num_stack.push(first * second);
break;
case '/':
first = num_stack.top();
num_stack.pop();
second = num_stack.top();
if (first == 0)
{
cout<<"divide by zero";
break;
}
else
{
num_stack.pop();
num_stack.push(first / second);
break;
}
case '+':
first = num_stack.top();
num_stack.pop();
second = num_stack.top();
num_stack.pop();
num_stack.push(first + second);
break;
case '-':
first = num_stack.top();
num_stack.pop();
second = num_stack.top();
num_stack.pop();
num_stack.push(first - second);
break;
case ' ':
break;
//if the item is a number then push it in the stack
default:
num_stack.push(p - '0'); //数字字符减去'0'就得到了该数字
break;
}
}
int result = num_stack.top();
num_stack.pop();
return result;
}
int main()
{
string infix;
cin>>infix;
string postfix = getPostfixExp(infix);
cout<<postfix<<endl;
cout<<postfixCalculate(postfix)<<endl;
return 0;
}
上面代码的逻辑很清晰,耐心看完应该不难。
理论知识参考b栈里面的一个视频:中缀表达式转后缀表达式