栈的应用-四则运算算术表达式(C++版)

版权声明:本文为博主原创文章,未经博主允许不得转载。 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 

猜你喜欢

转载自blog.csdn.net/wenzhou1219/article/details/86099898