首页 > 编程语言 >【C++复习】栈-下篇

【C++复习】栈-下篇

时间:2024-11-16 21:57:48浏览次数:1  
标签:操作数 出栈 复习 int 栈顶 C++ 下篇 表达式 中缀

大家好,这里是不会写开场白的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!

标签:操作数,出栈,复习,int,栈顶,C++,下篇,表达式,中缀
From: https://www.cnblogs.com/yinph/p/18549887

相关文章

  • 【C++】static(静态)
    类外静态变量或函数意味着,当需要将这些变量或函数与实际定义的符号链接时,链接器不会在这个翻译单元的作用域之外寻找那个符号定义,即只会在这个翻译单元内部链接(文件内可用)如果这句话并不理解,可以看一下【C++】HowtheC++CompilerWorks和【C++】HowtheC++LinkerWork......
  • 根据二叉树的前序和中序构建树,并按层次输出(C++)vector存树
    L2-006树的遍历#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;#defineendl'\n'intpo[35];intino[35];vector<int>ans[50];intdfs(intl1,intr1,intl2,intr2){ for(inti=l2;i<=r2;i++){ if......
  • 【C++进阶实战】个人财务管理系统
    功能说明添加收入和支出记录:用户可以添加新的收入和支出记录。查看记录:用户可以查看所有的收入和支出记录。生成财务报告:用户可以生成总收入、总支出和剩余金额的报告。设定预算:用户可以设定每月的预算,并查看是否超出预算。文件存储:使用文件来存储记录,以便下次启动程序时仍然......
  • 【C++进阶实战】电子词典
    功能说明查询单词:用户可以输入一个单词,程序将显示该单词的释义。文件存储:使用文件来存储单词和释义,以便下次启动程序时仍然可用。用户界面:提供简单的命令行界面,让用户选择查询单词或退出程序。示例代码词库文件 dictionary.txt假设我们有一个词库文件dictionary.txt,内容......
  • 穿越数据迷宫:C++哈希表的奇幻旅程
    文章目录前言......
  • [C++] 智能指针
    文章目录智能指针的使用原因及场景分析为什么需要智能指针?异常抛出导致的资源泄漏问题分析智能指针与RAIIC++常用智能指针使用智能指针优化代码优化后的代码优化点分析析构函数中的异常问题解决方法RAII和智能指针的设计思路详解什么是RAII?RAII的工作原理智能......
  • 【C++】深入理解自定义 list 容器中的 list_iterator:迭代器实现详解
    个人主页:起名字真南的CSDN博客个人专栏:【数据结构初阶】......
  • CSP/信奥赛C++语法基础刷题训练(9):洛谷P1035:[NOIP2002 普及组] 级数求和
    CSP/信奥赛C++语法基础刷题训练(9):洛谷P1035:[NOIP2002普及组]级数求和题目描述已知:Sn=1......
  • CSP/信奥赛C++语法基础刷题训练(10):洛谷P1307:[NOIP2011 普及组] 数字反转
    CSP/信奥赛C++语法基础刷题训练(10):洛谷P1307:[NOIP2011普及组]数字反转题目描述给定一个整数NNN,请将该数各个位上数字反转得到一个新数。新数也应满足整数的常见形式,......
  • C++ 创建一个线程
            C++11标准库引入了对多线程编程的支持,使得开发者能够以更加标准化的方式创建和管理线程。主要的线程管理方式是通过std::thread类,它可以用来创建、启动和管理线程。下面我将详细介绍如何使用C++标准库创建线程的方法,以及其他一些相关的工具类和概念。1.......