中缀表达式构建后缀表达式

news2025/1/12 1:05:19

中缀表达式构建后缀表达式

文章目录

  • 中缀表达式构建后缀表达式
    • 一、构造符号优先关系表
    • 二、构造后缀表达式


一、构造符号优先关系表

首先,我们需要知道什么是优先函数。优先函数是一种用于表示算符优先关系的函数,它有两种形式:f 和 g。f(a) 表示在栈中的算符 a 的优先级,g(b) 表示在输入中的算符 b 的优先级。我们用一个二维数组 pfunc 来存储这两种函数的值,其中 pfunc[0][a] 表示 f(a)pfunc[1][b] 表示 g(b)。我们的目标是根据给定的优先关系表 pt 来计算出 pfunc 的值。
优先关系表

isp\icp+-*/()#d
+>><<<>><
->><<<>><
*>>>><>><
/>>>><>><
(<<<<<=<
)>>>>>
#<<<<<=<
d>>>>>>

优先关系表 pt 是一个 n x n 的矩阵,其中 n 是算符的数量。pt[a][b] 表示算符 ab 之间的优先关系,它有三种可能的值:-101-1 表示 a < b,即 a 的优先级低于 b0 表示 a = b,即 ab 的优先级相同;1 表示 a > b,即 a 的优先级高于 b。例如,pt[0][2] = -1 表示第一个算符(假设是 +)的优先级低于第三个算符(假设是 *)。

如何根据 pt 来计算 pfunc 呢?在这里我们采用一种迭代的方法,每次更新 pfunc 的值,直到它不再变化或者达到最大的迭代次数。更新规则是:

  • 如果 pt[a][b] = 1,即 a > b,且 pfunc[0][a] <= pfunc[1][b],即 f(a) <= g(b),则将 f(a) 的值增加到 g(b) + 1,即 pfunc[0][a] = pfunc[1][b] + 1。这是为了保证 a 在栈中的优先级高于 b 在输入中的优先级,从而可以进行归约操作。
  • 如果 pt[a][b] = -1,即 a < b,且 pfunc[0][a] >= pfunc[1][b],即 f(a) >= g(b),则将 g(b) 的值增加到 f(a) + 1,即 pfunc[1][b] = pfunc[0][a] + 1。这是为了保证 b 在输入中的优先级高于 a 在栈中的优先级,从而可以进行移进操作。
  • 如果 pt[a][b] = 0,即 a = b,且 pfunc[0][a] != pfunc[1][b],即 f(a) != g(b),则将 f(a)g(b) 的值统一为较大的那个,即 pfunc[0][a] = pfunc[1][b] = max(pfunc[0][a], pfunc[1][b])。这是为了保证 ab 的优先级相同,从而可以进行归约或移进操作。
#include <iostream>
#include <vector>

// 根据优先关系表计算优先函数
std::vector<std::vector<int>> getPriorityFunc(std::vector<std::vector<int>>& pt) {
    int n = pt.size();  // 运算符的数量

    // 初始化 isp 和 icp,isp[0] 存储 in-stack precedence,isp[1] 存储 in-coming precedence
    std::vector<std::vector<int>> pfunc(2, std::vector<int>(n, 1));

    // 迭代,直至 flag 不再变动或者超过限制的迭代轮数
    bool flag = false;  // 标记是否发生了更新
    int iter = 1;  // 当前迭代轮数
    int limit_iter = 10;  // 允许的最大迭代轮数

    while (!flag && iter <= limit_iter) {
        std::cout << "迭代轮数:" << iter << std::endl;
        iter++;

        for (int a = 0; a < n; a++) {
            for (int b = 0; b < n; b++) {
                if (pt[a][b] == 1 && pfunc[0][a] <= pfunc[1][b]) {
                    // isp(a) 优先级高于 icp(b) 且 isp(a) <= icp(b),则 isp(a) = icp(b) + 1
                    pfunc[0][a] = pfunc[1][b] + 1;
                    flag = true;
                }
                else if (pt[a][b] == -1 && pfunc[0][a] >= pfunc[1][b]) {
                    // icp(b) 优先级高于 isp(a) 且 icp(b) <= isp(a),则 icp(b) = isp(a) + 1
                    pfunc[1][b] = pfunc[0][a] + 1;
                    flag = true;
                }
                else if (pt[a][b] == 0 && pfunc[0][a] != pfunc[1][b]) {
                    // isp(a) 与 icp(b) 优先级相同,但它们的值不同,根据较大的值来更新
                    if (pfunc[0][a] < pfunc[1][b]) {
                        pfunc[0][a] = pfunc[1][b];
                    }
                    else {
                        pfunc[1][b] = pfunc[0][a];
                    }
                    flag = true;
                }
            }
        }

        if (!flag) {
            return pfunc;
        }
        else {
            flag = false;
        }
    }
    return std::vector<std::vector<int>>();
}

int main() {
    // 定义符号优先关系表 pt
    std::vector<std::vector<int>> pt = {
        {1, 1, -1, -1, -1, 1, 1, -1},
        {1, 1, -1, -1, -1, 1, 1, -1},
        {1, 1, 1, 1, -1, 1, 1, -1},
        {1, 1, 1, 1, -1, 1, 1, -1},
        {-1, -1, -1, -1, -1, 0, -2, -1},
        {1, 1, 1, 1, -2, 1, 1, -2},
        {-1, -1, -1, -1, -1, -2, 0, -1},
        {1, 1, 1, 1, -2, 1, 1, -2}
    };

    // 调用函数计算优先函数
    std::vector<std::vector<int>> pfunc = getPriorityFunc(pt);

    // 打印计算得到的优先函数
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < pfunc[i].size(); j++) {
            std::cout << pfunc[i][j] << " ";
        }
        std::cout << std::endl;
    }

    return 0;
}

代码就是按照这个规则来更新 pfunc 的值的。我们用一个变量 flag 来标记是否发生了更新,如果没有更新,说明 pfunc 已经稳定,就可以返回它了。我们用一个变量 iter 来记录当前的迭代轮数,如果超过了限制的迭代轮数 limit_iter,我们就返回一个空的数组,表示无法计算出优先函数。

main 函数中,我们定义了一个符号优先关系表 pt,它是一个 8 x 8 的矩阵,表示了 8 个算符(+,-,*,/,(,),i,#)之间的优先关系。我们调用 getPriorityFunc 函数来计算优先函数,然后打印出结果。结果是一个 2 x 8 的矩阵,表示了 fg 的值。例如,结果的第一行第一个元素是 3,表示 f(+) = 3,即 + 在栈中的优先级是 3
结果如下:

在这里插入图片描述

isp\icp+-*/()#d
isp33551515
icp22446116

二、构造后缀表达式

中缀表达式是我们日常使用的算术表达式,例如 a + b ∗ c a+b*c a+bc,它遵循运算符的优先级和结合性规则。后缀表达式是一种不需要括号的表达式,它将运算符放在操作数的后面,例如 a b c ∗ + abc*+ abc+,它遵循栈的先进后出原则。后缀表达式的优点是它可以方便地用计算机进行求值,而不需要考虑运算符的优先级和结合性。

为了将中缀表达式转换为后缀表达式,需要使用一个栈来存储运算符,以及两个映射表来记录运算符的栈内优先级和栈外优先级。栈内优先级是指运算符在栈顶时的优先级,栈外优先级是指运算符在表达式中时的优先级。我们还需要在表达式的两端添加一个特殊的符号 #,表示表达式的开始和结束。

转换的过程如下:

  1. 从左到右扫描表达式,遇到操作数则直接输出到后缀表达式,遇到运算符则进行下一步。
  2. 比较栈顶运算符的栈内优先级和当前运算符的栈外优先级,如果栈内优先级低于栈外优先级,则将当前运算符入栈,如果栈内优先级高于栈外优先级,则将栈顶运算符出栈并输出到后缀表达式,重复此步骤直到栈内优先级小于栈外优先级或栈为空。
  3. 如果栈内优先级等于栈外优先级,通常表示两个运算符是一对括号或者两个 #,则将栈顶运算符出栈,但不输出到后缀表达式,如果是一对 #,则表示转换结束,否则继续扫描表达式。
#include <iostream>
#include <stack>
#include <map>

using namespace std;

class infixToPostfix {
public:
    infixToPostfix(const string& infix_expression) : infix(infix_expression), postfix("") {
        isp = { {'+', 3}, {'-', 3}, {'*', 5}, {'/', 5}, {'(', 1}, {')', 5}, {'#', 1}, {'d', 5} };
        icp = { {'+', 2}, {'-', 2}, {'*', 4}, {'/', 4}, {'(', 6}, {')', 1}, {'#', 1}, {'d', 6} };
    }

    int ispFunc(char c) {
        int priority = isp.count(c) ? isp[c] : -1;
        if (priority == -1) {
            cerr << "error: 出现未知符号!" << endl;
            exit(1);  // 异常退出
        }
        return priority;
    }

    int icpFunc(char c) {
        int priority = icp.count(c) ? icp[c] : -1;
        if (priority == -1) {
            cerr << "error: 出现未知符号!" << endl;
            exit(1);  // 异常退出
        }
        return priority;
    }

    void inToPost() {
        string infixWithHash = infix + "#";
        stack<char> stack;
        int loc = 0;
        while (!stack.empty() || loc < infixWithHash.size()) {
            char c1 = (stack.empty()) ? '#' : stack.top();  // 栈顶操作符
            char c2 = infixWithHash[loc];  // 当前字符

            if (ispFunc(c1) < icpFunc(c2)) {
                // 栈顶操作符优先级低于当前字符,将当前字符入栈
                stack.push(c2);
                loc++;  // 前进到下一个字符
            }
            else if (ispFunc(c1) > icpFunc(c2)) {
                // 栈顶操作符优先级高于当前字符,将栈顶操作符出栈并添加到后缀表达式
                postfix += c1;
                stack.pop();
            }
            else {
                if (c1 == '#' && c2 == '#') {
                    // 遇到两个 #,表达式结束
                    break;
                }
                // 优先级相等,通常不需要弹出或入栈,或者根据结合性规则来决定
                stack.pop();    //其中右括号遇到左括号时会抵消,左括号出栈,右括号不入栈
                loc++;
            }
        }
    }

    string getResult() {
        inToPost();
        return postfix;
    }

private:
    string infix;
    string postfix;
    map<char, int> isp;  // 栈内优先级
    map<char, int> icp;  // 栈外优先级
};

int main() {
    string infix_expression = "(d+d)*d";  // 测试 (d+d)*d
    cout << "infix_expression: " << infix_expression << endl;
    infixToPostfix solution(infix_expression);
    cout << "postfix_expression: " << solution.getResult() << endl;
    return 0;
}

代码中的类 infixToPostfix 封装了这个转换的过程,它有以下几个成员变量和函数:

  • infix:存储中缀表达式的字符串。
  • postfix:存储后缀表达式的字符串。
  • isp:存储运算符的栈内优先级的映射表,其中 # 的优先级为 1,( 的优先级为 1,) 的优先级为 5,+ 和 - 的优先级为 3,* 和 / 的优先级为 5,d 的优先级为 5(d 表示操作数)。
  • icp:存储运算符的栈外优先级的映射表,其中 # 的优先级为 1,( 的优先级为 6,) 的优先级为 1,+ 和 - 的优先级为 2,* 和 / 的优先级为 4,d 的优先级为 6(d 表示操作数)。
  • ispFunc:根据运算符返回其栈内优先级,如果运算符不存在于映射表中,则报错并退出。
  • icpFunc:根据运算符返回其栈外优先级,如果运算符不存在于映射表中,则报错并退出。
  • inToPost:执行转换的主要函数,它首先在中缀表达式的末尾添加一个 #,然后创建一个栈,从左到右扫描表达式,按照上述的规则进行入栈、出栈和输出操作,直到遇到两个 # 为止。
  • getResult:调用 in2post 函数并返回后缀表达式的字符串。

代码中的 main 函数是用来测试的,它创建了一个infixToPostfix 的对象,并传入了中缀表达式 ( d + d ) ∗ d (d+d)*d (d+d)d,然后调用 getResult 函数并输出了后缀表达式 d d + d ∗ dd+d* dd+d

下面是用表格表示的执行过程(如有错误欢迎指正,程序是对的):

步骤当前字符后缀表达式
1(#
2(#(
3d#(d
4+#(d
5+#(+d
6d#(+dd
7)#(+dd
8)#dd+
9*#dd+
10*#*dd+
11d#*dd+d
12##*dd+d
13##dd+d*

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python练习题(四)

本文主要是【Python】——Python练习题的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304;每日一句&#xff1a;狠狠沉淀&a…

openmmlab环境搭建及模拟kitti数据集跑pointpillars模型

点云训练—openmmlab环境搭建及模拟kitti数据集跑pointpillars模型 1 环境搭建 在我的 linux 服务器上&#xff0c;基于ubuntu20.04 参见&#xff1a;开始你的第一步 — MMDetection3D 1.3.0 文档 1.1 本地环境已安装anaconda. anaconda的安装参见博文&#xff1a;DS6.1-Y…

NAND Flash和NOR Flash的异同

NAND Flash和NOR Flash是两种常见的闪存类型。 NOR Flash是Intel于1988年首先开发出来的存储技术&#xff0c;改变了原先由EPROM和EEPROM一统天下的局面。 NAND Flash是东芝公司于1989年发布的存储结构&#xff0c;强调降低每比特的成本&#xff0c;更高的性能&#xff0c;并…

自动配置原理

自动配置原理 变更自动配置 视频地址&#xff1a; https://www.bilibili.com/video/BV15b4y1a7yG/?p160&spm_id_frompageDriver&vd_sourcef6debc5a79e3f424f9dde2f13891b158

上海亚商投顾:沪指探底回升 AI应用方向集体爆发

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 三大指数早间震荡调整&#xff0c;深成指盘中跌超1%&#xff0c;午后探底回升全线翻红&#xff0c;北证50指数…

MySQL 预写日志

什么是预写日志机制&#xff1f; 一般情况下&#xff0c;大部分数据库都是将表和索引存储在磁盘文件中。当新增数据时&#xff0c;数据库系统会先写入内存&#xff0c;然后将其写入磁盘上的数据文件。 那为什么不直接写入磁盘嘞&#xff1f;主要是每次新增都直接写入磁盘性能很…

智慧能源:数字孪生压缩空气储能管控平台

压缩空气储能在解决可再生能源不稳定性和提供可靠能源供应方面具有重要的优势。压缩空气储能&#xff0c;是指在电网负荷低谷期将电能用于压缩空气&#xff0c;在电网负荷高峰期释放压缩空气推动汽轮机发电的储能方式。通过提高能量转换效率、增加储能密度、快速启动和调节能力…

PicoScope 7 软件报警功能可实现自动保存和循环捕捉

最近很多用户提到&#xff0c;怎么让虹科Pico示波器采集信号到缓冲区满了之后自动保存在电脑里&#xff0c;然后清出缓存空间继续采集&#xff0c;如此循环工作。这里不得不向大家介绍一下PicoScope软件的强大功能之一&#xff1a;报警功能&#xff01; 报警在软件的工具菜单下…

专业爬虫框架 -- scrapy初识及基本应用

scrapy基本介绍 Scrapy一个开源和协作的框架&#xff0c;其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的&#xff0c;使用它可以以快速、简单、可扩展的方式从网站中提取所需的数据。 但目前Scrapy的用途十分广泛&#xff0c;可用于如数据挖掘、监测和自动化测试等领域…

HarmonyOS4.0之安装DevEco Studio开发工具

第一步 打开网址&#xff1a;https://developer.huawei.com/consumer/cn/ 点击后是这样的界面 第二步 鼠标移入到开发点击DevEco Studio 第三步 我们往下滑动找到以下界面 我们根据自己的需要点击下载图标 这里演示Window系统 下载好后解压文件 我们解压文件后 第四步…

VS2022配置WinPcap开发

winpcap 官网&#xff1a;http://www.winpcap.org/ 1.首先下载安装 winpcap.exe&#xff0c;http://www.winpcap.org/install/default.htm 目的是安装相关驱动和 dll&#xff0c;安装完成之后基于 winpcap 的应用程序才能够正常运行。 2.下载 winpcap 的开发包&#xff0c;头文…

34、AD/DA

AD/DA介绍 AD&#xff08;Analog to Digital&#xff09;&#xff1a;模拟-数字转换&#xff0c;将模拟信号转换为计算机可操作的数字信号 DA&#xff08;Digital to Analog&#xff09;&#xff1a;数字-模拟转换&#xff0c;将计算机输出的数字信号转换为模拟信号 AD/DA转换…

离线数仓构建案例一

数据采集 日志数据&#xff08;文件&#xff09;到Kafka 自己写个程序模拟一些用户的行为数据&#xff0c;这些数据存在一个文件夹中。 接着使用flume监控采集这些文件&#xff0c;然后发送给kafka中待消费。 1、flume采集配置文件 监控文件将数据发给kafka的flume配置文件…

python scipy.cluster.hierarchy.dendrogram学习详记——(待完善)

1.Python scipy.cluster.hierarchy.dendrogram用法及代码示例 2.python dendrogram_Python中的凝聚层次聚类示例

微信小程序在线客服 全端通吃版+PC官网客服+H5网站客服+微信公众号客服 附带完整的搭建教程

随着互联网的快速发展&#xff0c;在线客服系统已经成为企业与用户沟通的重要桥梁。然而&#xff0c;许多企业在构建自己的在线客服系统时&#xff0c;往往面临多种平台、多端口的困扰&#xff0c;如何实现全端通吃的客服系统成为一项迫切的需求。为此&#xff0c;我们推出了一…

android.view.WindowLeaked解决方法

问题 我在使用WindowManager添加一个button&#xff0c; windowManager.addView(button,layoutParams);然后关闭当前的这个Activity的时候遇到了WindowLeak这个问题&#xff0c;也就是所谓的窗体泄露。 原因 主要原因是因为android只允许在UI主线程操作&#xff0c;我在使用W…

geoserver维度time

postgis创建date类型的字段 写入测试数据&#xff0c;对应flag&#xff0c;flag有不同的样式&#xff0c;这样方便观测 geoserver发布图层的时候设置“维度”启用 测试&#xff0c;设置了根据flag展示不同的颜色

本地源文件-丰富的图表-

D:\FineReport_11.0\webapps\webroot\WEB-INF\reportlets\demo\basic 图表类型:http://localhost:8075/webroot/help/demo.html 可视化图表&#xff0c;丰富的图表:help/demo.html http://localhost:8075/webroot/decision#management/directory 参数查询/条件查询与图…

使用Redis构建简易社交网站(1)-创建用户与动态界面

目的 本文目的&#xff1a;实现简易社交网站中创建新用户和创建新动态功能。&#xff08;完整代码附在文章末尾&#xff09; 相关知识 本文将教会你掌握&#xff1a;1.redis基本命令&#xff0c;2.python基本命令。 redis基本命令 hget&#xff1a;从哈希中获取指定域的值…

h5进行svga动画礼物特效播放的代码实现队列按顺序播放

需求描述&#xff1a; 在直播场景中&#xff0c;有很多的礼物特效动画&#xff0c;如采用Svga动画的播放方案&#xff0c;则会遇到以下问题&#xff1b; 1.svga文件的预加载&#xff0c; 2.动画的顺序播放队列。即前一个动画播放完了&#xff0c;才会播放下一个动画。 1.svg…