
Hello,大家好,国庆的第二天,带来的是数据结构中堆栈部分的后缀表达式,这也是一块有关栈的应用方面困扰了众多同学的一个大难题,今天就让我们一起解决这个难题📕
堆栈应用之后缀表达式
❓初见:什么是后缀表达式
后缀表达式也称为逆波兰表达式,也就是将算术运算符放在操作数的后面
- 例如【1 + 2 * 3】的后缀表达式形式变为【1 2 3 * +】,前面的那个叫做中缀表达式,也就是我们最常用的,遵循的规则是“先乘除,后加减”;
- 那有些小伙伴就很疑惑,为什么要这么去做呢,好好地用我门正常的计算形式去运算不就好了,下面我们就来讲讲后缀表达式
后缀表达式的优势
- 对于后缀表达式,它十分地有用,因为其可以把复杂的表达式转化为依靠简单操作得到的计算结果的表达式。就我们上面这个表达式来看,是非常简单的,但如果换一个复杂一些的呢,你能够在短期时间内把它计算出来吗👇
- 可以看出,对于这个中缀表达式,是比较复杂的,但是我们利用后缀表达式去简化,看起来就没有这么复杂了,利用这个表达式与堆栈的先进后出【FILO】原理,就可以达到事半而功倍的效果
🗡磨砺:怎样将中缀表达式转换为后缀表达式【⭐⭐⭐⭐⭐】
相信这部分是大家最难过的一道坎,让我们来一探究竟🔍
🌳熟悉规则【重点基础掌握】
首先,最重要的一点是你必须知道转换的规则
📚法则一:如果遇到一个运算符op以及左括号“(”,此时栈如果为空,则直接将其进栈
📚法则二:若是遇到了操作数num,就将其直接输出
📚法则三:如果栈不为空,则只有当op的优先级高于栈顶运算符优先级时才将其进栈,否则将栈顶op弹出
📚法则四:若当前op进栈时发现栈中有与之相同op,则出栈其一,再加当前op压入栈中
📚法则五:若遇到右括号,则开始弹出栈字符,直到左括号为止
📚法则六:只要当遇到右括号“)”时,才从栈中弹出左括号“(”,否则一直遍历
- 以上法则是我们要了解后缀表达式转换的基本,你必须将其记牢
🌳层层剖析【堆栈原理展示】
有了固定的发展,接下来就是具体的案例和分析
- 我们以此表达式为例进行讲解【5 + 8*2 + ( 3 * 9 + 6 )*4】
Part1
- 首先是5,根据法则二,直接放入表达式
- 然后是操作符【+】,根据法则一,栈为空,直接入栈
- 接着是8,同理,直接放入表达式
- 其次是操作符【*】,因优先级高于【+】,故直接入栈
- 最后又碰到一个2,同理,直接放入表达式
Part2
【5 + 8*2 + ( 3 * 9 + 6 )*4】
- 读到【+】,这时要注意,引起优先级低于【*】,所以乘号出栈,放入表达式,但此时遍历到的操作符op与栈中的【+】一致,故栈中的【+】也因放入表达式,接着将遍历到的【+】入栈
Part3
【5 + 8*2 + ( 3 * 9 + 6 )*4】
- 首先读到左括号【(】,这代表一个子表达式的开始,因其优先级最高,故直接入栈
- 然后是3,根据法则二,直接放入表达式
- 接着遇到【*】,此时要注意,根据法则六,因为还没有碰到右括号【)】,说明子表达式还没结束,故直接入栈
- 最后是9,同理,放入表达式
Part4
【5 + 8*2 + ( 3 * 9 + 6 )*4】
- 首先我们遇到【+】,这是看到栈顶op为【*】,比其低,根据法则三,弹出栈顶元素,并将当前op入栈
- 然后是6,根据法则二,直接放入表达式
- 最后读到【)】,表示子表达式结束,开始逐个弹出栈顶元素,直到左括号【(】弹出后结束(括号均不放入表达式)

Part5
【5 + 8*2 + ( 3 * 9 + 6 )*4】
- 碰到【*】,因其优先级高于栈中的【+】,故直接入栈
- 然后是4,根据法则二,直接放入表达式
Part6
【5 + 8*2 + ( 3 * 9 + 6 )*4】
- 最后遍历到末尾,栈中还有两个操作符op,直接弹出即可(不放入表示式)
- 最后的postexp即为转换后的后缀表达式【582*+39*6+4】
🌳手撕代码【不信你看不懂】
了解了堆栈的基本原理后,接下来就是代码环节,也是可视化计算的重要一趴
void trans(char* exp, char postexp[]) //将算术表达式exp转换成后缀表达式postexp
{
char e;
SqStack* Optr; //定义运算符栈
InitStack(Optr); //初始化运算符栈
int i = 0; //i作为postexp的下标
while (*exp != '\0') //exp表达式未扫描完时循环
{
switch (*exp)
{
case '(': //判定为左括号
Push(Optr, '('); //左括号进栈
exp++; //继续扫描其他字符
break;
case ')': //判定为右括号
Pop(Optr, e); //出栈元素e
while (e != '(') //不为'('时循环
{
postexp[i++] = e; //将e存放到postexp中
Pop(Optr, e); //继续出栈元素e
}
exp++; //继续扫描其他字符
break;
case '+': //判定为加或减号
case '-':
while (!StackEmpty(Optr)) //栈不空循环
{
GetTop(Optr, e); //取栈顶元素e
if (e != '(') //e不是'('
{
postexp[i++] = e; //将e存放到postexp中
Pop(Optr, e); //出栈元素e
}
else //e是'(时退出循环
break;
}
Push(Optr, *exp); //将'+'或'-'进栈
exp++; //继续扫描其他字符
break;
case '*': //判定为'*'或'/'号
case '/':
while (!StackEmpty(Optr)) //栈不空循环
{
GetTop(Optr, e); //取栈顶元素e
if (e == '*' || e == '/') //将栈顶'*'或'/'运算符出栈并存放到postexp中
{
postexp[i++] = e; //将e存放到postexp中
Pop(Optr, e); //出栈元素e
}
else //e为非'*'或'/'运算符时退出循环
break;
}
Push(Optr, *exp); //将'*'或'/'进栈
exp++; //继续扫描其他字符
break;
default: //处理数字字符
while (*exp >= '0' && *exp <= '9') //判定为数字
{
postexp[i++] = *exp;
exp++;
}
postexp[i++] = ' '; //用#标识一个数值串结束
}
}
while (!StackEmpty(Optr)) //此时exp扫描完毕,栈不空时循环
{
Pop(Optr, e); //出栈元素e
postexp[i++] = e; //将e存放到postexp中
}
postexp[i] = '\0'; //给postexp表达式添加结束标识
DestroyStack(Optr); //销毁栈
}
看完这串代码,不用猜,我知道你已经已经想点下浏览器中这个网页的【×】了。但是何妨不听我讲完再说
- 这里我们需要用到顺序的结构实现原理SqStack,代码在最后给出,这里就展示转换的算法部分
- 整体浏览,这串代码处于一个while循环之中,也就是对给出的字符串进行的一个遍历,【!= ‘\0’】在C语言中表示还没到字符串结尾
- 在大层的while循环之下是一个switch()分支判断,判断的就是当前所遍历的exp,在英文中是expression【表达式】,上面讲到的postexp就是postexpression【后缀表达式】,我们的目的就是要去exp中遍历每一个字符,然后将符合条件的字符按照后缀表达式的法则以及堆栈的原理一个个地放入postexp中,然后我们来分别说说每个case的判断
- 首先是左括号【(】,这个直接进栈即可,exp++表示将遍历下一个字符,break表示的跳出当前的case分支,若是不写这一句,则程序会一直往下走,直到下一个break才会跳出
case '(': //判定为左括号
Push(Optr, '('); //左括号进栈
exp++; //继续扫描其他字符
break;
- 接着是右括号【)】,上面说过了,若是碰到右括号,则开始出栈栈顶元素,知道栈顶元素为左括号【(】时,while体中的语句就是将当前出栈的元素放入postexp后缀表达式中,i++表示为下一个位置留出位置,然后Pop出栈即可,循环结束后继续扫描下一个字符
case ')': //判定为右括号
Pop(Optr, e); //出栈元素e
while (e != '(') //不为'('时循环
{
postexp[

本文详细介绍了如何使用堆栈实现中缀表达式到后缀表达式的转换及求值过程,通过具体实例展示了算法原理。






最低0.47元/天 解锁文章
647

被折叠的 条评论
为什么被折叠?



