数据结构:栈的应用—中缀表达式转后缀表达式、后缀表达式的运算

后缀(逆波兰)表达式

中缀表达式:就是我们平时用的标准四则运算表达式,运算符在操作数中间,例如:9+(3-1)*3+10/2

后缀表达式:也称为逆波兰表达式,是将运算符写在操作数之后的表达式,例如上式的后缀表达式为:9 3 1 - 3 * + 10 2 / +

作用:对计算机而言,中缀表达式是比较复杂的结构,而逆波兰式在计算机看来却是比较简单易懂的结构。因为计算机普遍采用的内存结构是栈式结构,它执行先进后出的顺序


中缀表达式转后缀表达式

:中缀表达式 9+(3-1)*3+10/2 转换为后缀表达式 9 3 1 - 3 * + 10 2 / +

人眼观察方法:因为a + b 转为后缀表达式是 a b +,先在表达式中找最先运算的子式转成后缀表达式,如下图要先算3-1,转后缀为31-,再把31-看为一个整体A,则接下来运算A * 3,转为后缀即为A 3 *,即3 1 - 3 *,依次类推就可推出最终转成的后缀表达式为9 3 1 - 3 * + 10 2 / +
在这里插入图片描述

程序算法思路:从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级,是右括号或优先级低于栈顶符号(乘除优先加减)则栈顶元素依次出栈并输出,并将当前符号进栈,一直到最终输出后缀表达式为止,总结如下:
在这里插入图片描述

详细过程如下9+(3-1)*3+10/2 转换为后缀表达式 9 3 1 - 3 * + 10 2 / +

  1. 初始化一空栈,用来对符号进出栈使用,如下图左侧

  2. 第一个是数字9,直接输出,后面是符号+,进栈,如下图右侧在这里插入图片描述

  3. 第三个字符是(,左括号还未配对,入栈,如下图左侧

  4. 第四个字符是数字3,直接输出,总表达式为9 3,接着是-,进栈,如下图右侧在这里插入图片描述

    扫描二维码关注公众号,回复: 11993960 查看本文章
  5. 接下来是数字1,输出,总表达式为9 3 1,后面是),此时,要去匹配之前的左括号,所以栈顶依次输出,直到(出栈。总的表达式现在为9 3 1 -,栈中剩下+,如下图左侧

  6. 紧接着是*,因为此时栈顶为+号,优先级低于乘号,因此不输出,乘号进栈,接着是数字3,输出,总表达式为9 3 1 - 3,如下图右侧在这里插入图片描述

  7. 之后是+,此时栈顶为乘号,栈顶优先级高,因此栈中元素出栈并输出(没有比+更低的优先级,所以全部出栈),总输出表达式9 3 1 - 3 * +。然后将当前这个符号+进栈,如下图左侧

  8. 紧接着是数字10,直接输出,总表达式变为9 3 1 - 3 * + 10。然后是/ ,栈顶+优先级低于除号,/进栈,如下图右侧在这里插入图片描述

  9. 接下来是最后一个数字2,输出,表达式为9 3 1 - 3 * +10 2,如下图左侧

  10. 中缀表达式遍历结束,将栈中符号依次全部出栈并输出,最后输出的后缀表达式为:9 3 1 - 3 * +10 2 / + 在这里插入图片描述

C代码如下

//中缀表达式转后缀表达式
//中缀表达式存储在字符串中,为便于表示,代码使用9+(3-1)*3+8/2为例子

#include "stack.h"
#include <stdio.h>

int main()
{
    
    
	Stack s;
	InitStack(&s);

	const char mid[] = "9+(3-1)*3+8/2";  //中缀表达式
	char back[14];  //后缀表达式
	int i = 0;
	int j = 0;
	char useless;

	for (i = 0; i < 13; i++)
	{
    
    
		if (mid[i] <= '9'&&mid[i] >= '0')  //是数字,直接输出
		{
    
    
			back[j] = mid[i];
			j++;
		}
		else if (mid[i] == '(')  //是左括号,直接入栈
		{
    
    
			Push(&s, mid[i]);
		}
		else if (mid[i] == ')')  //是右括号,栈中出栈直到第一个左括号出栈
		{
    
    
			while (GetTop(&s) != '(')
			{
    
    
				Pop(&s, &back[j]);
				j++;
			}
			Pop(&s, &useless);			
		}
		else if (mid[i] == '*' || mid[i] == '/')  //是*/,出栈,直到栈空或者栈中遇到左括号或+-,当前符号入栈
		{
    
    
			while (!IsEmpty(&s) && (GetTop(&s) != '+' && GetTop(&s) != '-'))
			{
    
    
				Pop(&s, &back[j]);
				j++;
			}
			Push(&s, mid[i]);
		}
		else if (mid[i] == '+' || mid[i] == '-')  //是+-,出栈,直到栈空或者左括号当前符号入栈
		{
    
    
			while (!IsEmpty(&s)&&GetTop(&s) != '(')
			{
    
    
				Pop(&s, &back[j]);
				j++;
			}
			Push(&s, mid[i]);
		}
	}
	//中缀表达式遍历结束,要将栈中剩余符号依次弹出
	while (!IsEmpty(&s))
	{
    
    
		Pop(&s, &back[j]);
		j++;
	}
	back[j] = '\0';

	printf("中缀表达式: %s\n\n", mid);
	printf("后缀表达式: %s\n\n", back);

	return 0;
}

代码运行结果:
在这里插入图片描述


计算机如何用后缀表达式求值

例如:中缀表达式 9+(3-1)*3+10/2 的后缀表达式 9 3 1 - 3 * + 10 2 / +,计算机如何用该后缀表达式进行运算呢

规则:从左到右遍历后缀表达式的每个数字和符号,遇到数字就进栈,遇到是符号,就将处于栈顶两个数字出栈,进行运算,运算结果进栈,一直到最终获得结果

详细过程如下9 3 1 - 3 * + 10 2 / +

  1. 初始化一个空栈,该栈用来对要运算的数字进出使用
  2. 后缀表达式中前三个都是数字,所以9 3 1依次进栈,如下图
    在这里插入图片描述
  3. 接下来是 - 运算符,所以将栈顶的1出栈作为减数,3出栈作为被减数,并运算3-1得到2,再将2进栈,如下图左侧
  4. 接着是数字3进栈,如下图右侧
    在这里插入图片描述
  5. 接下来是* ,也就意味着栈中3与2出栈,相乘得到6,再将6入栈,如下图左侧
  6. 再下来是+ ,将6与9出栈,相加得到15,再将15入栈,如下图右侧
    在这里插入图片描述
  7. 接着是10与2两数字进栈,如下图左侧
  8. 接下来是/ ,因此栈顶的2与10出栈,10与2相除得到5,5入栈,如下图右侧
    在这里插入图片描述
  9. 最后是运算符+ ,所以5与15出栈相加得到20,再将20进栈,如下图左侧
  10. 后缀表达式遍历结束,最后栈中结果是20出栈,栈变为空,计算结束,如下图右侧
    在这里插入图片描述

以上可以看出后缀表达式可以很顺利地解决计算问题,因此后缀表达式是很重要的


综上,要想让计算机拥有处理我们通常的标准(中缀)表达式的能力,最重要的是两大步

  • 将中缀表达式转换为后缀表达式(栈用来进出运算的符号)
  • 将后缀表达式进行运算得到结果(栈用来进出运算的数字)

上述过程充分利用了栈的后进先出特性,是栈这种数据结构比较重要的应用

猜你喜欢

转载自blog.csdn.net/huifaguangdemao/article/details/108376983