热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

python教程分享Python数据结构与算法中的栈详解(3)

前序、中序和后序表达式是什么?对于像b∗c这样的算术表达式,可以根据其形式来正确地运算。在b∗c的例子中,由于乘号出现在两个变量之间,因此我们知道应该用

前序、中序和后序表达式是什么?

对于像b∗c 这样的算术表达式,可以根据其形式来正确地运算。在b∗c 的例子中,由于乘号出现在两个变量之间,因此我们知道应该用变量 b 乘以变量 c 。​

因为运算符出现在两个操作数的中间 ,所以这种表达式被称作中序表达式 。

来看另一个中序表达式的例子:a+b∗c。虽然运算符 “ + ” 和 “ * ” 都在操作数之间,但是存在一个问题:它们分别作用于哪些操作数? “ + ” 是否作用于 a 和 b ?“ * ” 是否作用于 b 和 c ?这个表达式看起来存在歧义。​

事实上,我们经常读写这类表达式,并且没有遇到任何问题。这是因为,我们知道运算符的特点。每一个运算符都有一个优先级 。在运算时,高优先级的运算符先于低优先级的运算符参与运算。唯一能够改变运算顺序的就是括号。乘法和除法的优先级高于加法和减法。​

尽管这些规律对于人来说显而易见,计算机却需要明确地知道以何种顺序进行何种运算。一种杜绝歧义的写法是完全括号表达式 。这种表达式对每一个运算符都添加一对括号。由括号决定运算顺序,没有任何歧义,并且不必记忆任何优先级规则。​

比如,a+b∗c+d可以被重写成((a+(b∗c))+d), 以表明乘法优先,然后计算左边的加法表达式。由于加法运算从左往右结合,因此a+b+c+d可以被重写成(((a+b)+c)+d)。​

我们已经了解了中序表达式的原理, 还有另外两种重要的表达式,也许并不能一目了然地看出它们的含义,那就是前序和后序表达式。​

以中序表达式a+b为例。如果把运算符放到两个操作数之前,就得到了前序表达式+ab 。同理,如果把运算符移到最后,会得到后序表达式ab+ 。这两种表达式看起来有点奇怪。​

通过改变运算符与操作数的相对位置,我们分别得到 前序表达式 和 后序表达式 。前序表达式要求所有的运算符出现在它所作用的两个操作数之前,后序表达式则相反。下表列出了一些例子。

中序表达式 前序表达式 后序表达式
a + b + a b a b +
a + b * c + a * b c a b c * +

 a+b∗c可以被重写为前序表达式 + a ∗ b c 

乘号出现在 b 和 c 之前,代表着它的优先级高于加号。加号出现在 a 和乘法结果之前。

a+b∗c对应的后序表达式是 a b c ∗ + 

运算顺序仍然得以正确保留,这是由于乘号紧跟 b 和 c 出现,意味着它的优先级比加号更高。

关于前序表达式和后序表达式,尽管运算符被移到了操作数的前面或者后面,但是运算顺序并没有改变。

再举一个例子:现在来看看中序表达式  (a+b)∗c

括号用来保证加号的优先级高于乘号。但是,当a+b被写成前序表达式时,只需将加号移到操作数之前,即+ab。于是,加法结果就成了乘号的第一个操作数。乘号被移到整个表达式的最前面,从而得到∗+abc。​

同理,后序表达式 a b + a b+ ab+保证优先计算加法。乘法则在得到加法结果之后再计算。因此,正确的后序表达式为 ab+c∗。​

一些中序、前序与后序表达式对应示例:

中序表达式 前序表达式 后序表达式
a + b * c + d + + a * b c d a b c * + d +
(a + b) * (c + d) * + a b + c d a b + c d + *
a * b + c * d + * a b * c d a b * c d * +
a + b + c + d + + + a b c d a b + c + d +

我们为什么要学习前/后序表达式?

在上面的中序表达式变为前/后序表达式的例子中,请注意一个非常重要的变化。在后两个表达式中,括号去哪里了?为什么前序表达式和后序表达式不需要括号?答案是,这两种表达式中的运算符所对应的操作数是明确的。只有中序表达式需要额外的符号来消除歧义。前序表达式和后序表达式的运算顺序完全由运算符的位置决定。​

前序表达式是一种十分有用的表达式,它将中序表达式转换为可以依靠简单的操作就能得到运算结果的表达式。例如, ( a + b ) ∗ ( c + d ) (a+b)*(c+d) (a+b)∗(c+d)转换为 ∗ + a b + c d *+ab+cd ∗+ab+cd。它的优势在于只用两种简单的操作,入栈和出栈就可以解决任何中序表达式的运算。——《百度百科关于前序表达式作用的解释》

从中序向前序和后序转换

到目前为止,我们使用了一种难以言明的方法来将中序表达式转换成对应的前序表达式和后序表达式。正如我们所想的那样,这其中一定存在通用的算法,可用于正确转换任意复杂度的表达式。

一个容易观察到的方法是替换括号法,但前提是使用完全括号表达式

如前所述,可以将a+b∗c写作(a+(b∗c)),以表示乘号的优先级高于加号。进一步观察后会发现,每一对括号其实对应着一个中序表达式(包含两个操作数以及其间的运算符)。 观察子表达式(b∗c)的右括号。如果将乘号移到右括号所在的位置,并且去掉左括号,就会得到bc∗,这实际上是将该子表达式转换成了对应的后序表达式。如果把加号也移到对应的右括号所在的位置,并且去掉对应的左括号,就能得到完整的后序表达式

Python数据结构与算法中的栈详解(3)

如果将运算符移到左括号所在的位置,并且去掉对应的右括号,就能得到前序表达式,如下图所示。实际上,括号对的位置就是其包含的运算符的最终位置。

Python数据结构与算法中的栈详解(3)

因此,若要将任意复杂的中序表达式转换成前序表达式后序表达式,可以先将其写作完全括号表达式, 然后将括号内的运算符移到 左括号处(前序表达式) 或者 右括号处(后序表达式)。

python实现从中序表达式到后序表达式的转换​

我们需要开发一种将任意中序表达式转换成后序表达式的算法。为了完成这个目标,让我们进一步观察转换过程。​

再一次研究a+b∗c这个例子。如前所示,其对应的后序表达式为 abc∗+。操作数 a 、b 和 c 的相对位置保持不变,只有运算符改变了位置。再观察中序表达式中的运算符。从左往右看,第一个出现的运算符是 + 。但是在后序表达式中,由于 * 的优先级更高(写成完全括号表达式后乘法所在的括号先进行运算),因此 * 先于 + 出现。在本例中,中序表达式的运算符顺序与后序表达式的相反。​

在转换过程中,由于运算符右边的操作数还未出现(不知道运算符右边是否还有括号运算), 因此需要先将运算符保存在某处。同时,由于运算符有不同的优先级,因此可能需要反转它们的保存顺序。 本例中的加号与乘号就是这种情况。由于中序表达式中的加号先于乘号出现,但乘号的运算优先级更高,因此后序表达式需要反转它们的出现顺序。鉴于这种反转特性,使用栈来保存运算符就显得十分合理。​

那么对于(a+b)∗c,情况会如何呢?它对应的后序表达式为 ab+c∗。从左往右看,首先出现的运算符是 + 。不过,由于括号改变了运算符的优先级,因此当处理到 * 时,+ 已经被放入结果表达式中了。​

现在可以来总结转换算法: 当遇到左括号时,需要将左括号保存下来,以表示接下来的内容里会遇到高优先级的运算符(就算括号里出现的运算符本身的优先级低,但也因为在括号里而优先级高了起来);那个运算符需要等到左括号对应的右括号出现,才能确定转换为后序表达式之后它应该存在的位置 (回忆一下完全括号表达式的转换法);当右括号出现时,也就是确定了括号内运算符在后序表达式中的存在位置,便可以将左括号和左括号上面的其他运算符(可能有可能没有)从栈中取出来。 (用于完全括号表达式)​

用代码实现转换算法:

​假设中序表达式是一个以空格分隔的标记串。其中, 运算符标记有+−∗/ ,括号标记有“ ( ( (”和“ ) ) )” ,操作数标记有 a 、b 、c 等。下面的步骤会生成一个后序标记串。​

步骤:

1.创建用于保存运算符的空栈 opstack ,以及一个用于保存结果的空列表。

2.使用字符串方法 split 将输入的中序表达式转换成一个列表。

3.从左往右扫描这个标记列表

3.1 如果标记是操作数,将其添加到结果列表的末尾。

3.2 如果标记是左括号,将其压入 opstack 栈中。

3.3 如果标记是右括号,反复从 opstack 栈中移除元素,直到移除对应的左括号。将从栈中取出的每 一个运算符都添加到结果列表的末尾。

3.4 如果标记是运算符,将其压入 opstack 栈中。但是,在这之前,需要先从栈中取出优先级更高或相同的运算符,并将它们添加到结果列表的末尾。

4.当处理完输入表达式以后,检查 opstack 栈。将其中所有残留的运算符全部添加到结果列表的末尾。​

为了在python中实现这一算法,我们使用一个叫作 prec 的字典来保存运算符的优先级值。该字典把每一个运算符都映射成一个整数。通过比较对应的整数,可以确定运算符的优先级(本例随意地使用了3、2、1)。左括号的优先级值最小。这样一来,任何与左括号比较的运算符都会被压入栈中。我们也将导入string 模块,它包含一系列预定义变量。本例使用一个包含所有大写字母的字符(string.ascii_uppercase )来代表所有可能出现的操作数。下述代码展示了完整的转换过程。

计算后序表达式

最后一个关于栈的例子是计算后序表达式。在这个例子中,栈再一次成为适合选择的数据结构。不过,当扫描后序表达式时,需要保存的是操作数,而不是运算符。换一个角度来说,当遇到一个运算符时,需要用离它最近的两个操作数来计算。​

为了进一步理解该算法,考虑后序表达式 4 5 6 * + 。当从左往右扫描该表达式时,首先会遇到操作数 4 和 5 。在遇到下一个符号之前,我们并不确定要对它们进行什么运算。故而将它们都保存在中,便可以在需要时取用。​

在本例中,紧接着 4 和 5 后出现的符号又是一个操作数。因此,将 6 也压入栈中,并继续检查后面的符号。​

现在遇到了运算符 * ,这意味着需要将最近遇到的两个操作数相乘。通过执行两次出栈操作,可以得到相应的操作数,然后进行乘法运算 5 ∗ 6 5*6 5∗6(本例的结果是30)。​

接着,将结果压入栈中。这样一来,当遇到后面的运算符时,它就可以作为操作数。当处理完最后一个运算符之后,栈中就只剩一个值。然后就可以将这个值取出来,并作为表达式的结果返回。​

过程如图所示:

Python数据结构与算法中的栈详解(3)

需要特别注意的是:

处理除法运算时需要非常小心。由于后序表达式只改变运算符的位置,因此操作数的位置与在中序表达式中的位置相同。当从栈中依次取出除号的操作数时,它们的顺序是颠倒的,因此我们要必须保证操作数的顺序不能颠倒。​

用代码实现后序表达式计算过程:

假设后序表达式是一个以空格分隔的标记串。其中, 运算符标记有 ∗ / + − * / + – ∗/+−,操作数标记是一位的整数值。结果是一个整数。​

步骤:

1.创建空栈 operandstack

2.使用字符串方法 split 将输入的后序表达式转换成一个列表

3.从左往右扫描这个标记列表:

(1) 如果标记是操作数,将其转换成整数(因为当前是字符)并且压入 operandstack 栈中

(2) 如果标记是运算符,从 operandstack 栈中取出两个操作数。第一次取出右操作数,第二次取出左操作数。进行相应的算术运算,然后将运算结果压入 operandstack 栈中。

4.当处理完输入表达式时,栈中的值就是结果。将其从栈中返回。​

为了方便运算,我们定义了辅助函数 domath 。它接受一个运算符和两个操作数,并进行相应的运算。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注<编程笔记>的更多内容! 

需要了解更多python教程分享Python数据结构与算法中的栈详解(3),都可以关注python教程分享栏目&#8212;编程笔记


推荐阅读
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 第四章高阶函数(参数传递、高阶函数、lambda表达式)(python进阶)的讲解和应用
    本文主要讲解了第四章高阶函数(参数传递、高阶函数、lambda表达式)的相关知识,包括函数参数传递机制和赋值机制、引用传递的概念和应用、默认参数的定义和使用等内容。同时介绍了高阶函数和lambda表达式的概念,并给出了一些实例代码进行演示。对于想要进一步提升python编程能力的读者来说,本文将是一个不错的学习资料。 ... [详细]
  • Python爬虫中使用正则表达式的方法和注意事项
    本文介绍了在Python爬虫中使用正则表达式的方法和注意事项。首先解释了爬虫的四个主要步骤,并强调了正则表达式在数据处理中的重要性。然后详细介绍了正则表达式的概念和用法,包括检索、替换和过滤文本的功能。同时提到了re模块是Python内置的用于处理正则表达式的模块,并给出了使用正则表达式时需要注意的特殊字符转义和原始字符串的用法。通过本文的学习,读者可以掌握在Python爬虫中使用正则表达式的技巧和方法。 ... [详细]
  • IOS开发之短信发送与拨打电话的方法详解
    本文详细介绍了在IOS开发中实现短信发送和拨打电话的两种方式,一种是使用系统底层发送,虽然无法自定义短信内容和返回原应用,但是简单方便;另一种是使用第三方框架发送,需要导入MessageUI头文件,并遵守MFMessageComposeViewControllerDelegate协议,可以实现自定义短信内容和返回原应用的功能。 ... [详细]
  • 如何提高PHP编程技能及推荐高级教程
    本文介绍了如何提高PHP编程技能的方法,推荐了一些高级教程。学习任何一种编程语言都需要长期的坚持和不懈的努力,本文提醒读者要有足够的耐心和时间投入。通过实践操作学习,可以更好地理解和掌握PHP语言的特异性,特别是单引号和双引号的用法。同时,本文也指出了只走马观花看整体而不深入学习的学习方式无法真正掌握这门语言,建议读者要从整体来考虑局部,培养大局观。最后,本文提醒读者完成一个像模像样的网站需要付出更多的努力和实践。 ... [详细]
  • 本文由编程笔记#小编整理,主要介绍了关于数论相关的知识,包括数论的算法和百度百科的链接。文章还介绍了欧几里得算法、辗转相除法、gcd、lcm和扩展欧几里得算法的使用方法。此外,文章还提到了数论在求解不定方程、模线性方程和乘法逆元方面的应用。摘要长度:184字。 ... [详细]
  • Android实战——jsoup实现网络爬虫,糗事百科项目的起步
    本文介绍了Android实战中使用jsoup实现网络爬虫的方法,以糗事百科项目为例。对于初学者来说,数据源的缺乏是做项目的最大烦恼之一。本文讲述了如何使用网络爬虫获取数据,并以糗事百科作为练手项目。同时,提到了使用jsoup需要结合前端基础知识,以及如果学过JS的话可以更轻松地使用该框架。 ... [详细]
  • 本文详细介绍了Python中正则表达式和re模块的使用方法。首先解释了转义符的作用,以及如何在字符串中包含特殊字符。然后介绍了re模块的功能和常用方法。通过学习本文,读者可以掌握正则表达式的基本概念和使用技巧,进一步提高Python编程能力。 ... [详细]
  • 本文介绍了2015年九月八日的js学习总结及相关知识点,包括参考书《javaScript Dom编程的艺术》、js简史、Dom、DHTML、解释型程序设计和编译型程序设计等内容。同时还提到了最佳实践是将标签放到HTML文档的最后,并且对语句和注释的使用进行了说明。 ... [详细]
  • Python的参数解析argparse模块的学习
    本文介绍了Python中参数解析的重要模块argparse的学习内容。包括位置参数和可选参数的定义和使用方式,以及add_argument()函数的详细参数关键字解释。同时还介绍了命令行参数的操作和可接受数量的设置,其中包括整数类型的参数。通过学习本文内容,可以更好地理解和使用argparse模块进行参数解析。 ... [详细]
  • Python脚本编写创建输出数据库并添加模型和场数据的方法
    本文介绍了使用Python脚本编写创建输出数据库并添加模型数据和场数据的方法。首先导入相应模块,然后创建输出数据库并添加材料属性、截面、部件实例、分析步和帧、节点和单元等对象。接着向输出数据库中添加场数据和历程数据,本例中只添加了节点位移。最后保存数据库文件并关闭文件。文章还提供了部分代码和Abaqus操作步骤。另外,作者还建立了关于Abaqus的学习交流群,欢迎加入并提问。 ... [详细]
  • 本文介绍了Python字典视图对象的示例和用法。通过对示例代码的解释,展示了字典视图对象的基本操作和特点。字典视图对象可以通过迭代或转换为列表来获取字典的键或值。同时,字典视图对象也是动态的,可以反映字典的变化。通过学习字典视图对象的用法,可以更好地理解和处理字典数据。 ... [详细]
  • 正则表达式及其范例
    为什么80%的码农都做不了架构师?一、前言部分控制台输入的字符串,编译成java字符串之后才送进内存,比如控制台打\, ... [详细]
author-avatar
汐玉Shining
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有