首先,我们需要知道什么是优先函数。优先函数是一种用于表示算符优先关系的函数,它有两种形式: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]
表示算符 a
和 b
之间的优先关系,它有三种可能的值:-1
,0
,1
。-1
表示 a < b
,即 a
的优先级低于 b
;0
表示 a = b
,即 a
和 b
的优先级相同;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])
。这是为了保证 a
和 b
的优先级相同,从而可以进行归约或移进操作。#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
的矩阵,表示了 f
和 g
的值。例如,结果的第一行第一个元素是 3
,表示 f(+) = 3
,即 +
在栈中的优先级是 3
。
结果如下:
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+b∗c,它遵循运算符的优先级和结合性规则。后缀表达式是一种不需要括号的表达式,它将运算符放在操作数的后面,例如 a b c ∗ + abc*+ abc∗+,它遵循栈的先进后出原则。后缀表达式的优点是它可以方便地用计算机进行求值,而不需要考虑运算符的优先级和结合性。
为了将中缀表达式转换为后缀表达式,需要使用一个栈来存储运算符,以及两个映射表来记录运算符的栈内优先级和栈外优先级。栈内优先级是指运算符在栈顶时的优先级,栈外优先级是指运算符在表达式中时的优先级。我们还需要在表达式的两端添加一个特殊的符号 #,表示表达式的开始和结束。
转换的过程如下:
#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* |