数据结构___栈___逆波兰表达式

版权声明:私藏源代码是违反人性的罪恶行为!博客转载无需告知,学无止境。 https://blog.csdn.net/qq_41822235/article/details/86502503

将算数表达式转换成后缀表达式并计算结果

一、背景

1.1 历史知识

20世纪50年代,波兰逻辑学家 J.Lukasiewicz困惑于如何处理四则运算。对于四则运算,先乘除后加减以及括号能强行改变优先级的特点使得算术问题交由计算机解决十分复杂。有一天,他灵感突现,想到了一种无关括号的后缀表达式法,称之为逆波兰表达式。非常巧妙地解决了程序实现四则运算的难题。

1.2 思路

分两步:首先将中缀表达式转成后缀表达式;其次根据后缀表达式进行求值。我们至少需实现两个函数:1求中缀转后缀;2根据后缀求值。

1.3 中缀转后缀表达式算法描述

从左到右遍历中缀表达式的每一个数字和符号,若是数字就输出即成为后缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级(没有括号,乘除优先于加减),是右括号或优先级低于栈顶符号,则栈顶元素依次出栈并输出,并将当前符号进栈,一直到最后输出后缀表达式为止。

单把优先级低于栈顶符号,则栈顶元素依次出栈并输出这句话截取出来如何思考?这样做的结果优先级高的将更靠近操作数,在后缀表达式进行求值的时候,就会被先计算;其实后缀表达式已经把优先级蕴含在顺序中了。

图1 中缀表达式转后缀表达式的例子

由图1 可以看到,乘号加号之前,就自然会被先计算,当然有括号加入就另说了

1.4 要点

  • 比较当前符号和栈顶符号的优先级的前提是——栈不空,如果栈空的话,应该直接将当前符号入栈。若不加以判断,对一个空栈取栈顶元素会导致不可预计的后果。
  • 如何用程序实现优先级的比较呢?在这里,采用了移位运算。
  • 可能会出现中缀表达式已经遍历完成但是栈不空的情况,在栈不空的时候,栈中所有元素都输出到后缀表达式中。
  • 当前元素进栈会有两个原因:1是不进行任何操作直接入栈;2是退出当前栈中全部元素后再入栈。
  • 退栈会有两个原因:1是当前元素是右括号,一直退栈直到栈顶元素是左括号同时要记得退出左括号,当前元素不进栈,2是当前元素优先级低于栈顶元素,将栈内元素全部输出到后缀表达式后当前元素进栈。本条应和上一条结合起来理解。
  • 如何区分不同操作数,正如图1 所示,如果在后缀表达式结果中,操作数与操作数之间不加间隔的话,就无法区分了。例如931,对照中缀表达式,我们知道是9和3和1,但是我们光拿一个后缀表达式931,就不明确是设么含义了。所以,操作数和操作数之间以空格隔开,这只是一种手段,也可以用#。

1.5 中缀转后缀编程实现

辅助函数:计算运算符优先级和判断运算符优先级。 

/*利用移位运算标记四则运算优先级 加 减 0; 乘 除 1; 括号 2 */
static int priority_call(char ch)
{
	int ret = -1;
	switch (ch)
	{
	case '+':
	case'-':
		ret = 0;
		break;
	case '*':
	case'/':
		ret = 1;
		break;
	case'(':
	case')':
		ret = 2;
		break;
	default:
		break;
	}
	return ret;
}

bool condition(char ch1, stack<char>& symbolSt)    //进行优先级的比较
{
	if (symbolSt.empty())	//栈空直接返回,对空栈去top会导致程序触发assert
		return false;
	int judge1 = priority_call(ch1);
	int judge2 = priority_call(symbolSt.top());
	if (judge1 < judge2)
		return true;
	else
		return false;
}

中缀表达式转后缀表达式函数 

void infixToPostfix(const char *&infix, char *&postfix)    //中缀表达式转后缀表达式
{
	int n = strlen(infix);	//计算中缀表达式长度
	int j = 0; //记录后缀表达式的下标
	stack<char>symbolSt;	//符号栈
	for (int i = 0; i < n; ++i)
	{
		if (isdigit(infix[i]))
		{
			while (isdigit(infix[i]))
			{
				postfix[j++] = infix[i++];
			}
			i--;
			postfix[j++] = ' ';	//添加空格区分不同操作数
		}
		else if (infix[i] == ')' || condition(infix[i], symbolSt))	//比较优先级的范围仅是四则运算
		{
			while (!symbolSt.empty() && symbolSt.top() != '(')
			{
				postfix[j++] = symbolSt.top();
				symbolSt.pop();
			}
			if (infix[i] == ')')
				symbolSt.pop();	//左括号退栈
			else
				symbolSt.push(infix[i]);
		}
		else
			symbolSt.push(infix[i]);
	}
	while (!symbolSt.empty())	//如果栈中还剩下符号的话,依次退栈即可。
	{
		postfix[j++] = symbolSt.top();
		symbolSt.pop();
	}
}

测试用例

int main()
{
	const char *infix = "9+(3-1)*3+10/2";
	int n = 2*(strlen(infix)+1);	//留下结束符
	char *postfix = new char[n];
	assert(postfix != NULL);
	memset(postfix, 0, n);
	infixToPostfix(infix, postfix);
	cout << postfix << endl;
	return 0;
}

1.7 根据后缀表达式求值的思路

正在写

1.8 根据后缀表达式求值编程实现

正在写

猜你喜欢

转载自blog.csdn.net/qq_41822235/article/details/86502503