版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wenzhou1219/article/details/86099898
很久之前写过,用栈实现四则运算,现在会看当时的实现有两个问题:
1.使用自己实现的容器,不够通用
2.代码实现的封装性不够
此次在原先的基础上,使用C++重构,直接使用STL容器类来实现四则运算。
原理
这里不再赘述,方法和注意事项参考之前文章理论部分
核心算法1
最通用的方法就是使用两个栈,分别对运算数和运算符压栈出栈实现运算符优先级计算,核心函数为算符比较函数。如下,注意这里的优先级构造技巧,和老文章的方法不太一样,更直观简单。
//核心函数,确定两个运算符的优先级,以此决定入栈出栈还是计算
E_LEVEL_COMPARE CompareOperator(const char c1, const char c2) const
{
//非法的匹配
if ((c1 == ')' && c2 == '(') ||
(c1 == '#' && c2 == ')') ||
(c1 == '(' && c2 == '#'))
{
return E_LEVEL_INVAILD;
}
//括号和#匹配匹配,表示需要将两个符号范围内运算完成
if ((c1 == '(' && c2 == ')') ||
(c1 == '#' && c2 == '#'))
{
return E_LEVEL_SAME;
}
//(和)优先计算
if (c1=='(' || c2=='(')
{
return E_LEVEL_SMALLER;
}
if (c1 == ')' || c2 == ')')
{
return E_LEVEL_LARGER;
}
//剩下的按照预先设定的运算符表映射来查找和比较优先级,注意这里相同的优先级,前者大于后者
return m_op_level.at(c1) < m_op_level.at(c2) ? E_LEVEL_SMALLER : E_LEVEL_LARGER;
}
CStackCalc()
{
// 初始运算符
m_op_level = { { '#', -999 },{ '+', 0 },{ '-', 0 },{ '*', 1 },{ '/', 1 },{ '%', 1 },{ '(', 999 },{ ')', 999 } };
}
不断读入和判断优先级即可,遇见高优先级的算符压栈,遇见低优先级的算符出栈计算,如下:
bool CalcExpression(const string str)
{
//清空堆栈
m_op.swap(stack<char>());
m_num.swap(stack<double>());
//首尾加上#标记运算的开始和结束
string rstr = str + '#';
m_op.push('#');
//遍历字符串,判断运算符或运算数字,完成计算
const char* p = rstr.c_str();
while (*p!='\0')
{
if (!IsOperator(*p))
{
m_num.push(ReadNum(p));
MoveToNext(&p);
}
else
{
char topOp = m_op.top();
switch (CompareOperator(topOp, *p))
{
case E_LEVEL_INVAILD:
cout << "表达式无效" << endl;
return false;
case E_LEVEL_SAME:
m_op.pop();
MoveToNext(&p);
break;
case E_LEVEL_SMALLER:
m_op.push(*p);
MoveToNext(&p);
break;
case E_LEVEL_LARGER:
m_op.pop();
auto num2 = m_num.top();
m_num.pop();
auto num1 = m_num.top();
m_num.pop();
double num = 0;
if (!Calc(num1, topOp, num2, &num))
{
cout << "除数不能为0" << endl;
}
m_num.push(num);
break;
}
}
}
if (m_op.size()!=0 || m_num.size()!=1)
{
cout << "表达式不合法" << endl;
return false;
}
cout << "计算结果为:" << m_num.top() << endl;
return true;
}
核心算法2
算法2就是之前说的后序表达式法(逆波兰式)。
其实现在再看,后序表达式法其实和算法1很像,只不过是把算法1拆分成了生成后序表达式和计算后序表达式两步。
先看生成后序表达式,其实和算法1基本一样,无非是把出栈计算换成了生成后序表达式队列。
bool ConvertToPostExp(const string str)
{
//首尾加上#标记运算的开始和结束
string rstr = str + '#';
m_op.push('#');
//遍历字符串,判断运算符或运算数字,完成计算
const char* p = rstr.c_str();
while (*p!='\0')
{
if (!IsOperator(*p))
{
m_exp.push_back({ E_NUM, {'_', ReadNum(p)} });
MoveToNext(&p);
}
else
{
char topOp = m_op.top();
switch (CompareOperator(topOp, *p))
{
case E_LEVEL_INVAILD:
cout << "表达式无效" << endl;
return false;
case E_LEVEL_SAME:
m_op.pop();
MoveToNext(&p);
break;
case E_LEVEL_SMALLER:
m_op.push(*p);
MoveToNext(&p);
break;
case E_LEVEL_LARGER:
m_exp.push_back({ E_OP, { m_op.top(), 0} });
m_op.pop();
break;
}
}
}
if (m_op.size()!=0)
{
cout << "表达式不合法" << endl;
return false;
}
cout << "后序表达式为:" << endl;
for (auto iter = m_exp.begin(); iter!=m_exp.end(); iter++)
{
if (iter->eType == E_NUM)
{
cout << (iter->eData).num << " ";
}
else
{
cout << (iter->eData).op << " ";
}
}
return true;
}
再看计算后序表示式,其实就是顺序出队列计算
bool CalcExpression(const string str)
{
//清空堆栈
m_exp.clear();
m_op.swap(stack<char>());
m_num.swap(stack<double>());
//转成后序表达式(逆波兰式)
if (!ConvertToPostExp(str))
{
return false;
}
//依次弹出数字和运算符完成计算
for (auto iter=m_exp.begin(); iter!=m_exp.end(); iter++)
{
if (iter->eType == E_NUM)
{
m_num.push(iter->eData.num);
}
else
{
double num2 = m_num.top();
m_num.pop();
double num1 = m_num.top();
m_num.pop();
double num = 0;
if (!Calc(num1, iter->eData.op, num2, &num))
{
cout << "除数不能为0" << endl;
return false;
}
m_num.push(num);
}
}
if (m_num.size() != 1)
{
cout << "表达式不合法" << endl;
return false;
}
cout << "计算结果为:" << m_num.top() << endl;
return true;
}
下载
演示代码下载链接
原创,转载请注明来自http://blog.csdn.net/wenzhou1219