日志规则逆波兰转换(2026.10.28)
2026年最新华为OD机考题库(按考点分类): 2026年最新华为OD机考题库
题目描述
系统中的日志采集模块支持用户配置日志过滤规则。为了避免每次匹配日志时重新分析规则,需要将中缀逻辑表达式转换为后缀表达式,也称逆波兰表达式(RPN)。
给定一条合法的日志过滤表达式,请输出其对应的后缀表达式。后缀表达式将运算符放在对应的操作数之后,不再需要括号。
表达式的操作数仅包含以下五种日志关键字,必须完全匹配,且区分大小写:
ERROR NETWORK TIMEOUT DISK CPU
支持的运算符如下:
| 运算符 | 类型 | 含义 | 优先级 | 结合性 |
|---|---|---|---|---|
| NOT | 前缀一元运算符 | 逻辑非 | 3 | 右结合 |
| AND | 二元运算符 | 逻辑与 | 2 | 左结合 |
| OR | 二元运算符 | 逻辑或 | 1 | 左结合 |
优先级数值越大,运算优先级越高。同级的 AND 或 OR 按从左到右的顺序结合;NOT 为右结合,例如 NOT NOT ERROR 表示 NOT (NOT ERROR)。
表达式可以使用英文圆括号改变运算顺序。括号保证成对出现,嵌套正确。空格可以连续出现,括号可以紧贴关键字;相邻的单词之间至少有一个空格。
输入描述
expression:一个字符串,表示一条合法的日志过滤表达式。表达式长度 1 到 1000,括号嵌套深度不超过 10,相邻单词之间至少有一个空格,括号可以紧贴关键字。
输出描述
返回一个字符串,表示转换后的后缀表达式,操作数与运算符之间用一个英文空格分隔,首尾不含空格。
用例输入 1
"ERROR AND NETWORK"
用例输出 1
"ERROR NETWORK AND"
用例解释 1
- ERROR 和 NETWORK 是 AND 的两个操作数,先按原顺序输出两个操作数,再输出 AND。
用例输入 2
"ERROR AND (NETWORK OR TIMEOUT)"
用例输出 2
"ERROR NETWORK TIMEOUT OR AND"
用例解释 2
- 括号内的 NETWORK OR TIMEOUT 先结合,转换为 NETWORK TIMEOUT OR,再与 ERROR 通过 AND 结合。
用例输入 3
"NOT ERROR OR TIMEOUT"
用例输出 3
"ERROR NOT TIMEOUT OR"
用例解释 3
- NOT 的优先级高于 OR,先将 NOT ERROR 转换为 ERROR NOT,再与 TIMEOUT 通过 OR 结合。
来源
本题来源于网络、非官方材料、仅供学习交流,侵删
🧠 解题思路
第一步:什么是逆波兰(后缀)表达式
平时写的 ERROR AND NETWORK 叫中缀表达式:运算符写在两个操作数中间。后缀表达式把运算符写在它的操作数后面:ERROR NETWORK AND。
后缀表达式不需要括号,也不用管优先级:从左往右读,遇到运算符就作用在它前面刚算出的结果上。例如 ERROR NETWORK TIMEOUT OR AND 的意思是:先算 NETWORK OR TIMEOUT,再和 ERROR 做 AND,也就是 ERROR AND (NETWORK OR TIMEOUT)。
第二步:先把字符串拆成单词
括号可能紧贴关键字,例如 (NETWORK。所以逐个字符扫:空格跳过;(、) 各自单独作为一个单词;其余连续的字母组成一个单词(ERROR、AND、NOT 等)。
第三步:用一个栈暂存运算符
转换规律是:操作数的先后顺序不变,运算符要等它右边的操作数都输出以后才能输出。所以操作数直接输出,运算符先放进一个栈里等着,时机到了再弹出来。从左到右处理每个单词:
- 操作数(ERROR 等):直接放进输出;
- 左括号:压栈,作为一道“隔板”;
- 右括号:不断弹出栈顶运算符放进输出,直到碰到左括号,左括号弹掉扔掉(括号不输出);
- NOT:直接压栈。它是写在操作数前面的一元运算符,要等后面的操作数输出以后才轮到它;
- AND / OR:入栈之前,先把栈顶那些优先级大于或等于它的运算符依次弹出、放进输出(碰到左括号就停),再把自己压栈;
- 全部处理完,把栈里剩下的运算符依次弹出、放进输出。
优先级是 NOT 3、AND 2、OR 1,数字越大越先算。
第四步:为什么第 5 步要弹出“大于或等于”的
- 栈顶优先级更高:比如栈里是 NOT,现在来了 OR。NOT 应该先算,它的操作数已经输出了,所以先把 NOT 输出;
- 优先级相等:AND、OR 都是左结合,
A OR B OR C要先算左边的 A OR B,所以前一个 OR 要先输出,得到A B OR C OR; - 栈顶优先级更低:比如栈里是 OR,来了 AND,AND 要先算,OR 留在栈里继续等。
用样例走一遍
样例 2:ERROR AND (NETWORK OR TIMEOUT)
| 单词 | 动作 | 输出 | 栈(底 → 顶) |
|---|---|---|---|
| ERROR | 操作数,输出 | ERROR | 空 |
| AND | 栈空,压栈 | ERROR | AND |
| ( | 压栈 | ERROR | AND ( |
| NETWORK | 输出 | ERROR NETWORK | AND ( |
| OR | 栈顶是左括号,停,压栈 | ERROR NETWORK | AND ( OR |
| TIMEOUT | 输出 | ERROR NETWORK TIMEOUT | AND ( OR |
| ) | 弹出 OR,扔掉左括号 | ERROR NETWORK TIMEOUT OR | AND |
| 结束 | 弹出 AND | ERROR NETWORK TIMEOUT OR AND | 空 |
样例 3:NOT ERROR OR TIMEOUT。NOT 压栈;ERROR 输出;来了 OR,栈顶 NOT 的优先级 3 ≥ 1,弹出 NOT;OR 压栈;TIMEOUT 输出;最后弹出 OR。结果 ERROR NOT TIMEOUT OR。
⚠️ 易错点
- 括号可能紧贴关键字,拆单词时
(、)要单独拆出来。 - NOT 是一元前缀运算符,遇到时直接压栈,不要按二元运算符的规则先弹栈。
- AND、OR 弹栈的条件是栈顶优先级“大于或等于”当前运算符,写成“大于”会把左结合变成右结合。
- 弹栈碰到左括号就停;遇到右括号时左括号要弹掉,但不输出。
- 输出用一个空格连接,首尾不能有空格。
⏱️ 复杂度
时间复杂度:O(n),n 是表达式长度,每个单词进出栈各一次。
空间复杂度:O(n),单词列表、输出和运算符栈。
💻 参考代码(Java)
import java.util.*;
class Solution {
public String toPostfix(String expression) {
// 拆分 token,处理括号紧贴关键字的情况
List<String> tokens = new ArrayList<>();
int i = 0, n = expression.length();
while (i < n) {
char ch = expression.charAt(i);
if (ch == ' ') {
i++;
} else if (ch == '(' || ch == ')') {
tokens.add(String.valueOf(ch));
i++;
} else {
int j = i;
while (j < n && expression.charAt(j) != ' ' && expression.charAt(j) != '(' && expression.charAt(j) != ')') j++;
tokens.add(expression.substring(i, j));
i = j;
}
}
// 运算符优先级
Map<String, Integer> priority = new HashMap<>();
priority.put("OR", 1);
priority.put("AND", 2);
priority.put("NOT", 3);
List<String> output = new ArrayList<>();
Deque<String> stack = new ArrayDeque<>();
for (String token : tokens) {
if (token.equals("(")) {
stack.push(token);
} else if (token.equals(")")) {
// 弹出直到左括号
while (!stack.isEmpty() && !stack.peek().equals("(")) {
output.add(stack.pop());
}
stack.pop();
} else if (token.equals("NOT")) {
// NOT 为前缀一元运算符,直接入栈
stack.push(token);
} else if (token.equals("AND") || token.equals("OR")) {
// 左结合:弹出优先级大于等于当前运算符的栈顶运算符
while (!stack.isEmpty() && !stack.peek().equals("(") && priority.get(stack.peek()) >= priority.get(token)) {
output.add(stack.pop());
}
stack.push(token);
} else {
// 操作数直接输出
output.add(token);
}
}
while (!stack.isEmpty()) {
output.add(stack.pop());
}
return String.join(" ", output);
}
}
💻 参考代码(C++)
class Solution {
public:
string toPostfix(string expression) {
// 拆分 token,处理括号紧贴关键字的情况
vector<string> tokens;
int i = 0, n = expression.size();
while (i < n) {
char ch = expression[i];
if (ch == ' ') {
i++;
} else if (ch == '(' || ch == ')') {
tokens.push_back(string(1, ch));
i++;
} else {
int j = i;
while (j < n && expression[j] != ' ' && expression[j] != '(' && expression[j] != ')') j++;
tokens.push_back(expression.substr(i, j - i));
i = j;
}
}
// 运算符优先级
map<string, int> priority;
priority["OR"] = 1;
priority["AND"] = 2;
priority["NOT"] = 3;
vector<string> output;
vector<string> st;
for (string token : tokens) {
if (token == "(") {
st.push_back(token);
} else if (token == ")") {
// 弹出直到左括号
while (!st.empty() && st.back() != "(") {
output.push_back(st.back());
st.pop_back();
}
st.pop_back();
} else if (token == "NOT") {
// NOT 为前缀一元运算符,直接入栈
st.push_back(token);
} else if (token == "AND" || token == "OR") {
// 左结合:弹出优先级大于等于当前运算符的栈顶运算符
while (!st.empty() && st.back() != "(" && priority[st.back()] >= priority[token]) {
output.push_back(st.back());
st.pop_back();
}
st.push_back(token);
} else {
// 操作数直接输出
output.push_back(token);
}
}
while (!st.empty()) {
output.push_back(st.back());
st.pop_back();
}
string result;
for (int k = 0; k < (int)output.size(); k++) {
if (k > 0) result += " ";
result += output[k];
}
return result;
}
};
💻 参考代码(Python)
class Solution:
def toPostfix(self, expression: str) -> str:
# 拆分 token,处理括号紧贴关键字的情况
tokens = []
i = 0
n = len(expression)
while i < n:
ch = expression[i]
if ch == ' ':
i += 1
elif ch == '(' or ch == ')':
tokens.append(ch)
i += 1
else:
j = i
while j < n and expression[j] != ' ' and expression[j] != '(' and expression[j] != ')':
j += 1
tokens.append(expression[i:j])
i = j
# 运算符优先级
priority = {'OR': 1, 'AND': 2, 'NOT': 3}
output = []
stack = []
for token in tokens:
if token == '(':
stack.append(token)
elif token == ')':
# 弹出直到左括号
while stack and stack[-1] != '(':
output.append(stack.pop())
stack.pop()
elif token == 'NOT':
# NOT 为前缀一元运算符,直接入栈
stack.append(token)
elif token == 'AND' or token == 'OR':
# 左结合:弹出优先级大于等于当前运算符的栈顶运算符
while stack and stack[-1] != '(' and priority[stack[-1]] >= priority[token]:
output.append(stack.pop())
stack.append(token)
else:
# 操作数直接输出
output.append(token)
while stack:
output.append(stack.pop())
return ' '.join(output)
💻 参考代码(JavaScript)
class Solution {
toPostfix(expression) {
// 拆分 token,处理括号紧贴关键字的情况
const tokens = [];
let i = 0;
const n = expression.length;
while (i < n) {
const ch = expression[i];
if (ch === ' ') {
i++;
} else if (ch === '(' || ch === ')') {
tokens.push(ch);
i++;
} else {
let j = i;
while (j < n && expression[j] !== ' ' && expression[j] !== '(' && expression[j] !== ')') j++;
tokens.push(expression.substring(i, j));
i = j;
}
}
// 运算符优先级
const priority = { 'OR': 1, 'AND': 2, 'NOT': 3 };
const output = [];
const stack = [];
for (const token of tokens) {
if (token === '(') {
stack.push(token);
} else if (token === ')') {
// 弹出直到左括号
while (stack.length > 0 && stack[stack.length - 1] !== '(') {
output.push(stack.pop());
}
stack.pop();
} else if (token === 'NOT') {
// NOT 为前缀一元运算符,直接入栈
stack.push(token);
} else if (token === 'AND' || token === 'OR') {
// 左结合:弹出优先级大于等于当前运算符的栈顶运算符
while (stack.length > 0 && stack[stack.length - 1] !== '(' && priority[stack[stack.length - 1]] >= priority[token]) {
output.push(stack.pop());
}
stack.push(token);
} else {
// 操作数直接输出
output.push(token);
}
}
while (stack.length > 0) {
output.push(stack.pop());
}
return output.join(' ');
}
}
module.exports = Solution;
205



被折叠的 条评论
为什么被折叠?



