【C++复习】栈-下篇

news/2024/11/16 22:05:35/文章来源:https://www.cnblogs.com/yinph/p/18549887

大家好,这里是不会写开场白的Yinph。

今天我们先来复习一下中缀表达式、前缀表达式和后缀表达式,以及如何用栈来实现它们之间的运算。

一、中缀表达式

‌中缀表达式‌是一种算术或逻辑公式的表示方法,其中操作符位于操作数的中间。这种表示方法符合人们的日常书写习惯,因此被广泛使用。

然而,对于计算机来说,处理中缀表达式相对复杂,因为计算机更擅长处理前缀或后缀表达式。中缀表达式的主要特点是将运算符放在操作数的中间,例如“3 + 4”、“114514*1919810”都是中缀表达式。

二、前缀表达式

(1)什么是前缀表达式?

前缀表达式,也称为波兰表达式,是一种算术或逻辑公式的表示方法,其中操作符位于操作数之前。

前缀表达式的主要特点是去掉了中缀表达式中用于明确运算顺序的括号,通过运算符的位置直接表明运算的优先级和顺序。这种表示方式使得表达式的计算过程更加简洁和直观(对计算机来说)。

前缀表达式在计算机科学中主要用于算法设计和实现,特别是在需要高效处理大量数学运算的场景中。例如,在编译器的设计中,前缀表达式可以帮助简化表达式的解析和计算过程。

例如,中缀表达式3 + 4 * 5转换成前缀表达式为+ 3 * 4 5

(2)中缀表达式转前缀表达式

中缀表达式转前缀表达式有很多种方法,其中一种简单的方法就是添加括号法。

主要分成三个步骤进行:

  1. 根据运算符的优先级对中缀表达式加括号,将优先级高的运算符放在括号内

  2. 将运算符移到对应的括号前面

  3. 去掉所有括号

例如,中缀表达式82 + 3 * (20 - 8) / 2转换成前缀表达式为+ 82 * 3 / - 20 8 2

文字表达大家可能不太理解,我这里用一张图片来解释:

转前缀表达式

(3)计算过程

前缀表达式在计算过程中主要分成四步来进行:

  1. 从右到左遍历表达式;

  2. 遇到数字就进栈;

  3. 遇到符号,就将栈顶元素出栈作为第一操作数,接着将新的栈顶元素出栈作为第二操作数,进行运算,然后将结果进栈;

  4. 重复以上操作直到表达式遍历结束,栈中的元素就是结果。

对于第一操作数和第二操作数是什么这个问题,一张图片就能解释了:

第一操作数和第二操作数

计算过程我来详细解释下:

我们以前缀表达式- 1 * + 3 4 5为例:

首先,我们从右往左,先将数字5入栈,接着将数字4入栈,最后将数字3入栈。

我们遇到操作符+,出栈,将栈顶元素3出栈作为第一操作数,接着将新的栈顶元素4出栈作为第二操作数,3 + 4 = 7,然后将结果7进栈。

计算过程

接着,我们将符号*出栈,将栈顶元素7出栈作为第一操作数,接着将新的栈顶元素5出栈作为第二操作数,7 * 5 = 35,与遇到的数字1同时进栈。

计算过程

最后,我们遇到操作符-,将目前栈顶元素1出栈作为第一操作数,接着将新的栈顶元素35出栈作为第二操作数,1 - 35 = -34,然后将结果-34进栈。

计算过程

最终,我们得到答案-34.

三、后缀表达式

(1)什么是后缀表达式?

后缀表达式,也称为逆波兰表达式,是一种算术或逻辑公式的表示方法,其中操作符位于操作数之后。

它简化了表达式的解析和计算过程,去掉了中缀表达式中用于明确运算顺序的括号,通过运算符的位置直接表明运算的优先级和顺序。这种表示方式也使得表达式的计算过程更加简洁和直观。

同一个例子:中缀表达式中3 + 4 * 5转换成后缀表达式为3 4 5 * +

(2)中缀表达式转后缀表达式

同样分成三个步骤进行:

  1. 根据运算符的优先级对中缀表达式加括号,将优先级高的运算符放在括号内

  2. 将运算符移到对应的括号后面

  3. 去掉所有括号

同一个例子,中缀表达式82 + 3 * (20 - 8) / 2转换成后缀表达式为82 3 20 8 - * 2 / +

转后缀表达式

(3)计算过程

当然,后缀表达式在计算过程中也主要分成四步来进行:

  1. 从左到右遍历表达式;

  2. 遇到数字就进栈;

  3. 遇到符号,就将栈顶元素出栈作为第二操作数,接着将新的栈顶元素出栈作为第一操作数,进行运算,然后将结果进栈;

  4. 重复以上操作直到表达式遍历结束,栈中的元素就是结果。

大家可能还不是很理解,我再来详细解释下:

我们以后缀表达式82 3 20 8 - * 2 / +为例:

首先,我们从左到右,先将数字82入栈,接着将数字3入栈,然后将数字20入栈,最后将数字8入栈。

我们遇到操作符-,出栈,将栈顶元素8出栈作为第二操作数,接着将新的栈顶元素20出栈作为第一操作数,20 - 8 = 12,然后将结果12进栈。

计算过程

接着,我们将符号*出栈,将栈顶元素12出栈作为第二操作数,接着将新的栈顶元素3出栈作为第一操作数,3 * 12 = 36,然后将结果36进栈。

计算过程

然后,我们将数字2入栈,遇到符号/出栈,将栈顶元素2出栈作为第二操作数,接着将新的栈顶元素36出栈作为第一操作数,36 / 2 = 18,然后将结果18进栈。

计算过程

最后,我们遇到操作符+,将目前栈顶元素18出栈作为第二操作数,接着将新的栈顶元素82出栈作为第一操作数,82 + 18 = 100,然后将结果100进栈。

计算过程

最终,我们得到答案100.

现在,我们已经了解了前、中、后缀表达式,以及它们之间的计算。接下来,我们就用代码来实现它们之间的运算。

四、代码解决前、后缀表达式运算

:::note
声明一下,下文给出的代码仅演示加、减、乘、除四个常见操作符。有需要可自行添加。
:::

(1)数组模拟

首先,我们可以用数组来模拟栈,来实现前缀表达式和后缀表达式的运算。缺点就是,数组模拟栈的效率较低,且代码较多,不好理解。

代码……开写!

转前缀表达式

#include <iostream>
#include <string>
#include <cmath>
using namespace std;
int st[n+1]; // int数组模拟栈
int TOP = 0; // 栈顶指针初始化(归0)
void push(int x)//入栈
{	TOP++;	st[TOP] = x;	
}
void pop( ) //出栈
{	TOP--;	
}
int top( ) // 获取栈顶元素	
{	return st[TOP];	
}
int main()
{string a;getline(cin, a);for (int i = a.length() - 1; i >= 0; i--){// 初始化sum和idx来构建数字int sum = 0, idx = 0;// 当当前字符是数字时,构建数字  while (a[i] >= '0' && a[i] <= '9'){sum = sum + (a[i] - '0') * pow(10, idx);idx++;i--; // 递减i以继续检查前一个字符  }if (sum != 0) // 如果遇到符号,则将其推入栈中  {push(sum);}// 如果当前字符是运算符,则处理运算符  if (a[i] == '+' || a[i] == '-' || a[i] == '*' || a[i] == '/'){int num1 = top();pop(); // 弹出栈顶元素int num2 = top();pop();// 加、减、乘、除运算:if (a[i] == '+') push(num2 + num1);else if (a[i] == '-')  push(num2 - num1);else if (a[i] == '*')  push(num2 * num1);else if (a[i] == '/')  push(num2 / num1);}}cout << top(); // 输出结果return 0;
}

转后缀表达式

#include <iostream>
#include <string>
#include <cmath>
using namespace std;
int st[n+1]; // int数组模拟栈
int TOP = 0; // 栈顶指针初始化(归0)
void push(int x)//入栈
{	TOP++;	st[TOP] = x;	
}
void pop( ) //出栈
{	TOP--;	
}
int top( ) // 获取栈顶元素	
{	return st[TOP];	
}
int main()
{string a;getline(cin, a);for (int i = 0; i < a.length() - 1; i++){// 初始化sum来构建数字int sum = 0;// 当当前字符是数字时,构建数字while (a[i] >= '0' && a[i] <= '9'){sum = sum * 10 + (a[i] - '0');i++;}if (sum != 0){push(sum);}if (a[i] == '+' || a[i] == '-' || a[i] == '*' || a[i] == '/'){int num2 = top();pop(); int num1 = top();pop();if (a[i] == '+') push(num1 + num2);else if (a[i] == '-')  push(num1 - num2);else if (a[i] == '*')  push(num1 * num2);else if (a[i] == '/')  push(num1 / num2);}}cout << top(); // 输出结果return 0;
}

(2)标准模板库(STL)模拟

上篇文章,我们认识了标准模板库(STL),在栈中使用代码更加方便。那么,我们今天就来用STL来实现一下前缀表达式和后缀表达式的运算。

主要核心不变,只是相关函数更改而已。我还是把代码展示一下吧:

转前缀表达式

#include <iostream>
#include <string>
#include <stack>
#include <cmath>
using namespace std;int main()
{stack <int> s;string a;getline(cin, a);for (int i = a.length() - 1; i >= 0; i--){// 初始化sum和idx来构建数字int sum = 0, idx = 0;// 当当前字符是数字时,构建数字  while (a[i] >= '0' && a[i] <= '9'){sum = sum + (a[i] - '0') * pow(10, idx);idx++;i--; // 递减i以继续检查前一个字符  }if (sum != 0) // 如果遇到符号,则将其推入栈中  {s.push(sum);}// 如果当前字符是运算符,则处理运算符  if (a[i] == '+' || a[i] == '-' || a[i] == '*' || a[i] == '/'){int num1 = s.top();s.pop(); // 弹出栈顶元素int num2 = s.top();s.pop();// 加、减、乘、除运算:if (a[i] == '+') s.push(num2 + num1);else if (a[i] == '-')  s.push(num2 - num1);else if (a[i] == '*')  s.push(num2 * num1);else if (a[i] == '/')  s.push(num2 / num1);}}cout << s.top(); // 输出结果return 0;
}

转后缀表达式

#include <iostream>
#include <string>
#include <stack>
#include <cmath>
using namespace std;int main()
{stack <int> s;string a;getline(cin, a);for (int i = 0; i < a.length() - 1; i++){// 初始化sum来构建数字int sum = 0;// 当当前字符是数字时,构建数字while (a[i] >= '0' && a[i] <= '9'){sum = sum * 10 + (a[i] - '0');i++;}if (sum != 0){push(sum);}if (a[i] == '+' || a[i] == '-' || a[i] == '*' || a[i] == '/'){int num2 = top();pop(); int num1 = top();pop();if (a[i] == '+') push(num1 + num2);else if (a[i] == '-')  push(num1 - num2);else if (a[i] == '*')  push(num1 * num2);else if (a[i] == '/')  push(num1 / num2);}}cout << top(); // 输出结果return 0;
}

五、总结一下

在本文中,我们主要学习了前缀表达式、后缀表达式与如何使用栈来解决运算问题。

希望这篇文章能帮助你们更好地理解栈,如果你有任何问题,欢迎在评论区留言。

OK,以上就是今天要讲的内容。大家喜欢就点个赞吧,我会尽快更新!ヾ(•ω•`)o!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/834811.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

gofiber: 模板:判断if条件

一,代码: 1,controller func (dc *ArticleController) GetArticle(c *fiber.Ctx) error {// 处理获取文章的逻辑article := new(Article)article.Id = 1article.Title = "三国演义金圣叹批本"article.Author = "罗贯中"user:=c.Query("user")m…

starrycan的pwn随笔——ELF文件和延迟绑定机制

一.ELF文件结构 0x01什么是ELF文件 1.linux环境中,二进制可持性文件的类型是ELF(Executable and LinkableFormat)文件。类似windows下的exe 2.elf文件的格式比较简单,我们需要了解的就是elf文件中的各个节、段等概念 3.程序elf的基本信息存在于elf的头部信息中,这些信息包括…

达梦数据库DM管理工具如何浏览数据,用条件筛选数据

前言 大家好,我是小徐啊。达梦数据库是我们一款常用的国产数据库,我之前一直在使用它。用起来和mysql和postgresql比起来,还是差不多的。而且它自带了数据库连接工具DM管理工具,使我们很方便的连接它。 今天,小徐就来介绍下如何用DM管理工具浏览数据,并且用条件去筛选数据…

标注图片怎么导出VOC格式和COCO格式

图片怎么标注参考 https://www.cnblogs.com/minseo/p/18549804下载 从github下载代码,或压缩包# git clone https://github.com/LabelMe/labelme转换 示例文件在以下路径# labelme-main\examples\instance_segmentation目录以及各个目录的用途如下打过标签的图片和json文件放以…

vscode Markdown文件如何使代码超出屏幕可视区域不换行

在编写Markdown文件的表格时,单元格内过长的内容会使得行超出可视区域,Markdown处理方式为单行用多行表示,如下图所示:这样会破坏表格的可读性,利用快捷键Alt+Z,可变为如下形式:使得表格每列对齐以增加可读性

20222325 2024-2025-1 《网络与系统攻防技术》实验六实验报告

1.实验内容 本实践目标是掌握metasploit的用法。 指导书参考Rapid7官网的指导教程。 https://docs.rapid7.com/metasploit/metasploitable-2-exploitability-guide/ 下载官方靶机Metasploitable2,完成下面实验内容。 (1)前期渗透 ①主机发现(可用Aux中的arp_sweep,search一…

[Linux]gdb基本使用

gdb基本使用 前提 gcc/g++编译出的程序默认是realease版本,要使用gdb调试,首先要在编译的时候加上-g选项。使用readelf -S [程序名]查看可执行文件的节区信息。使用gdb [程序名]:开始调试。q:退出调试。list/l [行号]:从给定的位置显示程序的源代码,每次十行。break/b [行…

美团商家联系电话采集软件团购外卖信息批量提取器

定义目标:明确需要采集的数据,如商家名称、地址、评分、销量等。 分析页面结构:通过浏览器的开发者工具,分析美团团购或外卖页面的HTML结构,找出包含所需数据的标签。 模拟用户请求:使用requests库模拟用户访问美团页面,可能需要处理登录、反爬虫机制(如Cookies、Heade…

# 团队作业4——项目冲刺-6

团队作业4——项目冲刺-6信息项 内容课程名称 广工计院计科34班软工作业要求位置 作业要求作业目标 在七天敏捷冲刺中,完成工大严选开发,记录每日进展和问题,更新燃尽图、签入代码,并发布集合日志总结成果GitHub链接 GitHub一、团队简介队名:小飞棍队团队成员:姓名 学号罗…

20222427 2024-2025-1 《网络与系统攻防技术》实验五实验报告

1.实验内容 1.1 本周内容总结使用了Metasploit框架,其是一个功能强大的渗透测试框架。在使用的过程当中,Metasploit 提供了种类繁多的攻击模块,涵盖了远程代码执行、服务拒绝、提权等多种攻击方式,支持对多种操作系统和应用程序进行测试。除了漏洞利用,它还具备强大的后渗…

20222306 2024-2025-1《网络与系统攻防技术》实验六实验报告

1.实验内容 1.1内容回顾总结 这周都重点在于Metasploit工具的使用,我深入了解了对其功能和使用流程。Metasploit 是一个功能强大的渗透测试框架,广泛应用于网络安全领域。它为安全专家、渗透测试人员和红队提供了一个全面的工具集,支持漏洞利用、攻击模拟和安全评估。Metaspl…

关于HDFS路径文件夹名称的问题

问题发现 ​ 最开始的需求:修改/origin_data/gmall/db目录下所有以inc结尾的文件夹里的文件夹(名称为2024-11-15)修改为2020-6-14 问gpt写了个脚本: #!/bin/bash# 遍历 /origin_data/gmall/db 下所有以 "inc" 结尾的文件夹 for dir in $(hdfs dfs -ls /origin_da…