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

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

文章目录

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


一、构造符号优先关系表

首先,我们需要知道什么是优先函数。优先函数是一种用于表示算符优先关系的函数,它有两种形式: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 
#include 

// 根据优先关系表计算优先函数
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
结果如下:

中缀表达式构建后缀表达式_第1张图片

isp\icp + - * / ( ) # d
isp 3 3 5 5 1 5 1 5
icp 2 2 4 4 6 1 1 6

二、构造后缀表达式

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

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

转换的过程如下:

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

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 ( #(
3 d #( d
4 + #( d
5 + #(+ d
6 d #(+ dd
7 ) #(+ dd
8 ) # dd+
9 * # dd+
10 * #* dd+
11 d #* dd+d
12 # #* dd+d
13 # # dd+d*

中缀表达式构建后缀表达式_第2张图片

你可能感兴趣的:(编译原理,数据结构,算法,编译原理,数据结构)