This commit is contained in:
HuangHai
2025-08-30 18:35:01 +08:00
commit 1f397eca87
6258 changed files with 1487241 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
#include <bits/stdc++.h>
using namespace std;
const int N = 30;
/*
思路
1、数据处理,通过记录入度,找到入度为0的点,找到根节点
2、二元运算必须有符号右边那个式子,即根据数据构建完树后的右孩子
故在递归遍历树的时候,只要判断该节点是否有孩子,
然后返回的字符串数: 递归左子树的string值 + 该节点的string值 + 递归右子树的string值
3、如果该节点不是根节点,则在该返回值的左右两边加上括号
4、最后输出返回值
黄海总结:
1、乍一看此题,感觉无从下手,因为不知道每个点都是什么节点号!后来慢慢从测试用例1中发现,
data 都是什么 *,a,b,-,c之类,不是节点号,应该是具体的操作符,也就是值。
那节点号在哪里呢?再看一下后面的left_child,right_child,发现是
8 7
-1 -1
4 1
2 5
这个来理解一下,应该是指8号节点,7号节点,-1代表的是NULL节点,也就是到头了,是叶子。
再来仔细看一下,发现出现的数字是:1 2 4 5 6 7 8,单单缺少了一个3!
为啥缺少3呢?
因为3不是任何人的left_child,right_child,它是根!!!!,这样的话,整个图就完整了!
*/
int n;
string w[N];
int l[N], r[N], in[N];
int isleaf[N];
/*
8
* 8 7
a -1 -1
* 4 1
+ 2 5
b -1 -1
d -1 -1
- -1 6
c -1 -1
(a+b)*(c*(-d))
*/
string dfs(int u) {
// 表达式树特点:叶子节点都是真实数字,非叶子节点都是操作符
string left, right;
// if (isleaf[u]) return w[u]; // 递归终点,如果是叶子,本句话可以省略掉
// 这是因为isleaf[u]时,l[u]==-1 && r[u]==-1 ,则下面的两个分支都不会走,直接到了return那句,left,right都是空,还是返回w[u],一样的
if (l[u] != -1) {
left = dfs(l[u]); // 递归左儿子,
if (!isleaf[l[u]]) left = "(" + left + ")"; // 如果左儿子不是叶子,需要把表达式加上括号
}
if (r[u] != -1) { // 右儿子不为空
right = dfs(r[u]); // 递归右儿子
if (!isleaf[r[u]]) right = "(" + right + ")"; // 如果右儿子不是叶子,需要把表达式加上括号
}
// 返回中序遍历结果
return left + w[u] + right;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> w[i] >> l[i] >> r[i]; // data left_child right_child
if (l[i] != -1) in[l[i]]++; // 记录入度
if (r[i] != -1) in[r[i]]++; // 记录入度
if (l[i] == -1 && r[i] == -1) isleaf[i] = 1; // 叶子
}
// 找根
int root;
for (root = 1; root <= n; root++)
if (!in[root]) break; // 入度为零的是根
// 通过中序遍历,构建中缀表达式
cout << dfs(root) << endl;
return 0;
}

View File

@@ -0,0 +1,83 @@
#include <bits/stdc++.h>
using namespace std;
/*
测试用例I:
(2+2)*(1+1)
答案:8
测试用例II:
2+(3*4)-((5*9-5)/8-4)
答案:13
*/
stack<int> stk; // 数字栈
stack<char> op; // 操作符栈
// 优先级表
unordered_map<char, int> g{{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}};
/**
* 功能:计算两个数的和差积商
*/
void eval() {
int a = stk.top(); // 第二个操作数
stk.pop();
int b = stk.top(); // 第一个操作数
stk.pop();
char p = op.top(); // 运算符
op.pop();
int r; // 结果
// 计算结果
if (p == '+') r = b + a;
if (p == '-') r = b - a;
if (p == '*') r = b * a;
if (p == '/') r = b / a;
// 结果入栈
stk.push(r);
}
int main() {
// 读入表达式
string s;
cin >> s;
// 遍历字符串的每一位
for (int i = 0; i < s.size(); i++) {
// ① 如果是数字,则入栈
if (isdigit(s[i])) {
// 读出完整的数字
int x = 0;
while (i < s.size() && s[i] >= '0' && s[i] <= '9') {
x = x * 10 + s[i] - '0';
i++;
}
i--; // 加多了一位,需要减去
stk.push(x); // 数字入栈
}
// ② 左括号无优先级,入栈
else if (s[i] == '(')
op.push(s[i]);
// ③ 右括号时,需计算最近一对括号里面的值
else if (s[i] == ')') {
// 从栈中向前找,一直找到左括号
while (op.top() != '(') eval(); // 将左右括号之间的计算完,维护回栈里
// 左括号出栈
op.pop();
} else { // ④ 运算符
// 如果待入栈运算符优先级低,则先计算
while (op.size() && g[op.top()] >= g[s[i]]) eval();
op.push(s[i]); // 操作符入栈
}
}
while (op.size()) eval(); // ⑤ 剩余的进行计算
printf("%d\n", stk.top()); // 输出结果
return 0;
}

View File

@@ -0,0 +1,86 @@
#include <bits/stdc++.h>
using namespace std;
// 测试用例:
// (23+124)*(98-2)
// 中缀转后缀
unordered_map<char, int> g{{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}}; // 操作符优化级
string infixToPostfix(string s) {
stack<char> stk; // 栈
string t; // 结果串
for (int i = 0; i < s.size(); i++) {
// ①数字
if (isdigit(s[i])) {
int x = 0;
while (i < s.size() && isdigit(s[i])) {
x = x * 10 + s[i] - '0';
i++;
}
i--;
t += to_string(x) + " ";
} else if (isalpha(s[i])) // ②字符,比如a,b,c
t.push_back(s[i]), t.push_back(' ');
else if (s[i] == '(') // ③左括号
stk.push(s[i]); // 左括号入栈
else if (s[i] == ')') { // ④右括号
while (stk.top() != '(') { // 让栈中元素(也就是+-*/和左括号)一直出栈,直到匹配的左括号出栈
t.push_back(stk.top()), t.push_back(' ');
stk.pop();
}
stk.pop(); // 左括号也需要出栈
} else {
// ⑤操作符 +-*/
while (stk.size() && g[s[i]] <= g[stk.top()]) {
t.push_back(stk.top()), t.push_back(' ');
stk.pop();
}
stk.push(s[i]); // 将自己入栈
}
}
// 当栈不为空时,全部输出
while (stk.size()) {
t.push_back(stk.top()), t.push_back(' ');
stk.pop();
}
return t;
}
// 判断字符是否是操作符
bool isOp(char c) {
return c == '+' || c == '-' || c == '*' || c == '/';
}
// 后缀表达式求值
int eval(string s) {
stack<int> stk; // 后缀表达式求值要使用到一个栈
// 构造一个字符串流,方便处理带空格的后缀表达式
stringstream s1(s);
string c;
while (s1 >> c) {
if (isOp(c[0])) { // 如果是操作数,将其转换为整数并入栈
int a = stk.top();
stk.pop();
int b = stk.top();
stk.pop();
if (c == "+") stk.push(b + a);
if (c == "-") stk.push(b - a);
if (c == "*") stk.push(b * a);
if (c == "/") stk.push(b / a);
} else
stk.push(stoi(c));
}
return stk.top();
}
int main() {
string s;
cin >> s;
// 中缀转后缀
s = infixToPostfix(s);
// 后缀表达式求值
cout << eval(s) << endl;
return 0;
}

View File

@@ -0,0 +1,61 @@
#include <bits/stdc++.h>
using namespace std;
const int N = 30;
int n;
string w[N];
int l[N], r[N], in[N];
int isleaf[N];
/*
题目描述:
给出中缀表达式的语法树 请你将后缀表达式输出 括号体现优先级 操作符不可用括号包含。
思路:
后缀表达式就是把运算符后置的表达式,可以抽象成一棵二叉树,其中的的节点有两种情况:
(1)有左右儿子
(2)只有右儿子
只有右儿子的情况就是负数啦~ 测试用例: - -1 6
然后就是找根,这个很简单,统计所有节点的入度,唯一的一个入度是 0 的节点就是根。
再写个简单的递归就可以了。
8
* 8 7
a -1 -1
* 4 1
+ 2 5
b -1 -1
d -1 -1
- -1 6
c -1 -1
输出样例1:
(((a)(b)+)((c)(-(d))*)*)
*/
string dfs(int u) {
if (isleaf[u]) return "(" + w[u] + ")";
if (l[u] == -1 && r[u] != -1) return "(" + w[u] + dfs(r[u]) + ")"; // 负号
return "(" + dfs(l[u]) + dfs(r[u]) + w[u] + ")"; // 后缀表达式
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> w[i] >> l[i] >> r[i];
if (l[i] != -1) in[l[i]]++;
if (r[i] != -1) in[r[i]]++;
if (l[i] == -1 && r[i] == -1) isleaf[i] = 1; // 叶子
}
// 找根
int root;
for (root = 1; root <= n; root++)
if (!in[root]) break; // 入度为零的是根
cout << dfs(root) << endl;
return 0;
}

View File

@@ -0,0 +1,147 @@
#include <bits/stdc++.h>
using namespace std;
// 中缀转后缀
unordered_map<char, int> g{{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}}; // 操作符优先级
string infixToPostfix(string s) {
string t; // 结果串
stack<char> stk; // 栈
for (int i = 0; i < s.size(); i++) {
// ①数字
if (isdigit(s[i])) {
int x = 0;
while (i < s.size() && isdigit(s[i])) {
x = x * 10 + s[i] - '0';
i++;
}
i--;
t += to_string(x), t += ' ';
} else if (isalpha(s[i])) // ②字符,比如a,b,c
t += s[i], t += ' ';
else if (s[i] == '(') // ③左括号
stk.push(s[i]); // 左括号入栈
else if (s[i] == ')') { // ④右括号
while (stk.top() != '(') { // 让栈中元素(也就是+-*/和左括号)一直出栈,直到匹配的左括号出栈
t += stk.top(), t += ' ';
stk.pop();
}
stk.pop(); // 左括号也需要出栈
} else {
// ⑤操作符 +-*/
while (stk.size() && g[s[i]] <= g[stk.top()]) {
t += stk.top(), t += ' ';
stk.pop();
}
stk.push(s[i]); // 将自己入栈
}
}
// 当栈不为空时,全部输出
while (stk.size()) {
t += stk.top(), t += ' ';
stk.pop();
}
return t;
}
// 后缀转表达式树
const int N = 500, M = N << 1;
#define ls e[h[u]] // 左儿子
#define rs e[ne[h[u]]] // 右儿子,由于表达式树是二叉树,有右儿子时一定有左儿子
// 链式前向星
int e[M], h[N], idx, ne[M];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
char a[N];
int al = 25; // a[]:节点号对应的字符,0~25为数字a~z在表达式树中节点的编号,26及以上为每一个操作符的编号,注意:如果前后出现多次同一运算符,它们的节点号是不一样的
// 构造表达式树
int build(string postfix) {
stack<int> stk; // 节点号栈
// 链式前向星初始化
memset(h, -1, sizeof h);
idx = 0, al = 25;
// 后缀表达式->表达式树,可以转化:前缀表达式,中缀表达式,后缀表达式
for (int i = 0; i < postfix.size(); i++) { // 枚举后缀表达式每一个字符
char c = postfix[i];
if (c == ' ') continue;
if (isalpha(c))
stk.push(c - 'a'); // 不是操作符,是数字,按字符-'a'的数字号入栈
else { // 如果是运算符
int x = stk.top(); // 取出两个节点
stk.pop();
int y = stk.top();
stk.pop();
a[++al] = c; // 操作符,申请一个节点号,并且,记录下来节点号与原字符的关系
add(al, x), add(al, y); // 由操作符向左右两个儿子连出边
stk.push(al); // 操作符节点入栈
}
}
return stk.top(); // 栈顶部的字符就是根
}
// 输出节点u
char out(int u) {
if (u >= 26) return a[u]; // 节点号大于等于26的是操作符+-*/
return u + 'a'; // a~z,记录的是0~25,输出需要加上偏移量'a'
}
// 判断操作符
bool isOp(char c) {
return c == '+' || c == '-' || c == '*' || c == '/';
}
// 获取符合题意的中缀表达式
string inorder(int u) {
string ans = "";
if (h[u] == -1) {
ans += out(u);
return ans;
}
string lstr = inorder(ls);
string rstr = inorder(rs);
// 下面的代码是本题的题意要求
if (isOp(a[ls]) && g[a[u]] > g[a[ls]]) { // 如果左儿子是操作符,并且当前运算符优先级大于左儿子运算优先级,需要左儿子加上括号
ans += '(';
ans += lstr;
ans += ')';
} else
ans += lstr; // 否则,左儿子不加括号
ans += out(u); // 左儿子完事,把自己加上
if (isOp(a[rs]) && g[a[rs]] <= g[a[u]]) { // 如果右儿子是操作符,并且,右儿子运算符优先级小于等于当前运算符优先级
if ((a[u] == '+' && a[rs] == '-') // 当前是+,右儿子是-
|| (a[u] == '*' && a[rs] == '/') // 当前是*,右儿子是/
|| (a[u] == '+' && a[rs] == '+') // 当前是+,右儿子是+
|| (a[u] == '*' && a[rs] == '*')) // 当前是*,右儿子是*
ans += rstr; // 都不需要加括号
else
ans += '(' + rstr + ')'; // 否则需要加括号
} else
ans += rstr; // 否则右儿子不加括号
return ans;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("HDU1123.in", "r", stdin);
freopen("HDU1123.out", "w", stdout);
#endif
int n;
cin >> n;
while (n--) {
string infix;
cin >> infix;
// 中缀转后缀
string postfix = infixToPostfix(infix);
// 根据后缀表达式创建表达式树
int root = build(postfix);
cout << inorder(root) << endl;
}
return 0;
}

View File

@@ -0,0 +1,9 @@
8
(a+(b*c))
((a+b)*c)
(a*(b*c))
(a*(b/c)*d)
((a/(b/c))/d)
((x))
(a+b)-(c-d)-(e/f)
(a+b)+(c-d)-(e+f)

View File

@@ -0,0 +1,8 @@
a+b*c
(a+b)*c
a*b*c
a*b/c*d
a/(b/c)/d
x
a+b-(c-d)-e/f
a+b+c-d-(e+f)

View File

@@ -0,0 +1,51 @@
#include <bits/stdc++.h>
using namespace std;
const int N = 10010, M = N << 1; // 最大顶点数,最大边数
/*
链式前向星:保存的是每一个顶点的所有出边
h[u] 表示点u为出发点的第一条边的编号
e[i] 表示第i条边的指向终点,有时也写作to[i]
ne[i] 表示第i条边的下一条边的编号
idx 记录边的数量,表现在代码中就是一个计数器
*/
int e[M], h[N], idx, w[M], ne[M];
// 添加一条从 a 到 b 的边,边权为c
void add(int a, int b, int c = 0) {
// e[idx]=b 第idx号边,是指向点b的
// ne[idx]=h[a] 第idx号边,它的下一条边是原来h[a]指向的边
// w[idx]=c 第idx号边,权值是c
// h[a]=idx++ 以a点为出发点的所有边组成的链表中,第一个是idx号边,头插法
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
int main() {
// 初始化
memset(h, -1, sizeof h);
int n = 5; // 顶点数
// 添加边
add(0, 1);
add(0, 2);
add(1, 3);
add(2, 3);
add(3, 4);
// 遍历从每个顶点出发的边
for (int u = 0; u < n; u++) {
// cout << "从 " << u << "号节点引出的边: " << endl;
for (int i = h[u]; ~i; i = ne[i]) { // 枚举从点u出发的每条边
int v = e[i];
// u 到点 v 的边
cout << u << " -> " << v << endl;
}
cout << endl;
}
return 0;
}

View File

@@ -0,0 +1,7 @@
x1 x2 & x3 |
3
1 0 1
3
1
2
3

View File

@@ -0,0 +1,38 @@
#include <bits/stdc++.h>
using namespace std;
// 判断字符是否是操作符
bool isOp(string c) {
return c == "+" || c == "-" || c == "*" || c == "/";
}
// 后缀表达式求值
int eval(string s) {
stack<int> stk; // 后缀表达式求值要使用到一个栈
// 构造一个字符串流,方便处理带空格的后缀表达式
stringstream s1(s);
string c;
while (s1 >> c) {
if (isOp(c)) { // 如果是操作数,将其转换为整数并入栈
int a = stk.top();
stk.pop();
int b = stk.top();
stk.pop();
if (c == "+") stk.push(b + a);
if (c == "-") stk.push(b - a);
if (c == "*") stk.push(b * a);
if (c == "/") stk.push(b / a);
} else
stk.push(stoi(c)); //"24"-->24
}
return stk.top();
}
int main() {
string s = "24 9 6 3 / + 5 - * 4 +";
cout << "answer: " << eval(s) << endl;
return 0;
}
/*
中缀表达式: (24*(9+6/3-5)+4)
后缀表达式: 24 9 6 3 / + 5 - * 4 +
值=148
*/

View File

@@ -0,0 +1,83 @@
#include <bits/stdc++.h>
using namespace std;
/*
测试用例I:
(2+2)*(1+1)
答案:8
测试用例II:
2+(3*4)-((5*9-5)/8-4)
答案:13
*/
stack<int> num; // 数字栈
stack<char> op; // 操作符栈
// 优先级表
unordered_map<char, int> g{{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}};
/**
* 功能:计算两个数的和差积商
*/
void eval() {
int a = num.top(); // 第二个操作数
num.pop();
int b = num.top(); // 第一个操作数
num.pop();
char p = op.top(); // 运算符
op.pop();
int r; // 结果
// 计算结果
if (p == '+') r = b + a;
if (p == '-') r = b - a;
if (p == '*') r = b * a;
if (p == '/') r = b / a;
// 结果入栈
num.push(r);
}
int main() {
// 读入表达式
string s;
cin >> s;
// 遍历字符串的每一位
for (int i = 0; i < s.size(); i++) {
// ① 如果是数字,则入栈
if (s[i] >= '0' && s[i] <= '9') {
// 读出完整的数字
int x = 0;
while (i < s.size() && s[i] >= '0' && s[i] <= '9') {
x = x * 10 + s[i] - '0';
i++;
}
i--; // 加多了一位,需要减去
num.push(x); // 数字入栈
}
// ② 左括号无优先级,入栈
else if (s[i] == '(')
op.push(s[i]);
// ③ 右括号时,需计算最近一对括号里面的值
else if (s[i] == ')') {
// 从栈中向前找,一直找到左括号
while (op.top() != '(') eval(); // 将左右括号之间的计算完,维护回栈里
// 左括号出栈
op.pop();
} else { // ④ 运算符
// 如果待入栈运算符优先级低,则先计算
while (op.size() && g[op.top()] >= g[s[i]]) eval();
op.push(s[i]); // 操作符入栈
}
}
while (op.size()) eval(); // ⑤ 剩余的进行计算
printf("%d\n", num.top()); // 输出结果
return 0;
}

View File

@@ -0,0 +1,51 @@
#include <bits/stdc++.h>
using namespace std;
/*
0&(1|0)|(1|1|1&0)
答案:1
(0|1&0|1|1|(1|1))&(0&1&(1|0)|0|1|0)&0
答案:0
*/
unordered_map<char, int> g{{'|', 1}, {'&', 2}};
stack<int> num;
stack<char> op;
void eval() {
int a = num.top();
num.pop();
int b = num.top();
num.pop();
char p = op.top();
op.pop();
int r;
if (p == '|') r = b | a;
if (p == '&') r = b & a;
num.push(r);
}
int main() {
string s;
cin >> s;
for (int i = 0; i < s.size(); i++) {
if (isdigit(s[i]))
num.push(s[i] - '0');
else if (s[i] == '(')
op.push(s[i]);
else if (s[i] == ')') {
while (op.top() != '(') eval();
op.pop();
} else {
while (op.size() && g[op.top()] >= g[s[i]]) eval();
op.push(s[i]);
}
}
while (op.size()) eval();
printf("%d\n", num.top());
return 0;
}

View File

@@ -0,0 +1,43 @@
#include <bits/stdc++.h>
using namespace std;
/*
中缀逻辑表达式转后缀逻辑表达式
测试用例:
0&(0|1|0)
答案:
001|0|&
*/
unordered_map<char, int> g{{'|', 1}, {'&', 2}};
string s, t;
stack<char> stk;
int main() {
cin >> s;
for (int i = 0; i < s.size(); i++) {
if (isdigit(s[i]) || isalpha(s[i]))
t += s[i];
else if (s[i] == '(')
stk.push(s[i]);
else if (s[i] == ')') {
while (stk.top() != '(') {
t += stk.top();
stk.pop();
}
stk.pop();
} else {
while (stk.size() && g[s[i]] <= g[stk.top()]) {
t += stk.top();
stk.pop();
}
stk.push(s[i]);
}
}
while (stk.size()) {
t += stk.top();
stk.pop();
}
cout << t << endl;
return 0;
}

View File

@@ -0,0 +1,77 @@
#include <bits/stdc++.h>
using namespace std;
// 中缀表达式转后缀表达式
/*
测试用例1:
a+b*c+(d*e+f)*g
答案:
abc*+de*f+g*+
测试用例2:
(6+3*(7-4))-8/2
答案:
6 3 7 4 - * + 8 2 / -
测试用例3:
(24*(9+6/3-5)+4)
答案:
24 9 6 3 / + 5 - * 4 +
测试用例4:
2*3*2+5/3
*/
unordered_map<char, int> g{{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}}; // 操作符优化级
string t; // 结果串
stack<char> stk; // 栈
// 中缀转后缀
string infixToPostfix(string s) {
for (int i = 0; i < s.size(); i++) {
// ①数字
if (isdigit(s[i])) {
int x = 0;
while (i < s.size() && isdigit(s[i])) {
x = x * 10 + s[i] - '0';
i++;
}
i--;
t += to_string(x), t += ' ';
} else if (isalpha(s[i])) // ②字符,比如a,b,c
t += s[i], t += ' ';
else if (s[i] == '(') // ③左括号
stk.push(s[i]); // 左括号入栈
else if (s[i] == ')') { // ④右括号
while (stk.top() != '(') { // 让栈中元素(也就是+-*/和左括号)一直出栈,直到匹配的左括号出栈
t += stk.top(), t += ' ';
stk.pop();
}
stk.pop(); // 左括号也需要出栈
} else {
// ⑤操作符 +-*/
while (stk.size() && g[s[i]] <= g[stk.top()]) {
t += stk.top(), t += ' ';
stk.pop();
}
stk.push(s[i]); // 将自己入栈
}
}
// 当栈不为空时,全部输出
while (stk.size()) {
t += stk.top(), t += ' ';
stk.pop();
}
return t;
}
int main() {
string infix = "(24*(9+6/3-5)+4)";
string postfix = infixToPostfix(infix);
cout << "中缀表达式: " << infix << endl;
cout << "后缀表达式: " << postfix << endl;
return 0;
}

View File

@@ -0,0 +1,45 @@
#include <bits/stdc++.h>
using namespace std;
/*
测试样例:
+ 23 34
/ + 4 2 * 3 5
*/
// 将前缀表达式转换为后缀表达式(带空格版本)
// 判断字符是否是操作符
bool isOp(string c) {
return c == "+" || c == "-" || c == "*" || c == "/";
}
string prefixToPostfix(string s) {
stack<string> stk;
string c; // 子串
vector<string> q;
stringstream s1(s); // 构造字符串流,处理带空格的后缀表达式
while (s1 >> c) q.push_back(c); // 将每个子串抽取出来
for (int i = q.size() - 1; i >= 0; i--) {
string c = q[i];
if (isOp(c)) {
string a = stk.top();
stk.pop();
string b = stk.top();
stk.pop();
stk.push(a + " " + b + " " + c);
} else
stk.push(c); // 如果是操作数,将其转换为整数并入栈
}
return stk.top();
}
int main() {
string prefix = "/ + 4 2 * 3 5"; // / + 4 2 * 3 5 --> vector<>={/ + 4 2 * 3 5}
string postfix = prefixToPostfix(prefix);
cout << "前缀表达式: " << prefix << endl;
cout << "后缀表达式: " << postfix << endl;
return 0;
}

View File

@@ -0,0 +1,37 @@
#include <bits/stdc++.h>
using namespace std;
// 判断字符是否是操作符
bool isOp(string c) {
return c == "+" || c == "-" || c == "*" || c == "/";
}
string prefixToPostfix(string s) {
stack<string> stk;
for (int i = s.size() - 1; i >= 0; i--) {
string c = string(1, s[i]);
if (isOp(c)) { // 操作符
string a = stk.top();
stk.pop();
string b = stk.top();
stk.pop();
stk.push(a + b + c);
} else
stk.push(c); // 字母a~z
}
return stk.top();
}
int main() {
// * + 12 23 * 3 + 45 6
string prefix = "*+ab*c+de";
string postfix = prefixToPostfix(prefix);
cout << "前缀表达式: " << prefix << endl;
cout << "后缀表达式: " << postfix << endl;
return 0;
}
/*
Prefix Expression: *+ab*c+de
Postfix Expression: ab+cde+**
Infix Expression: ((a+b)*(c*(d+e)))
*/

View File

@@ -0,0 +1,30 @@
#include <bits/stdc++.h>
using namespace std;
bool isOp(string c){
return c == "+" || c == "-" || c == "*" || c == "/";
}
int eval(string s){
stack<int> stk;
stringstream s1(s);
string c;
while (s1>>c){
if(isOp(c)){
int a = stk.top();
stk.pop();
int b = stk.top();
stk.pop();
if (c == "+") stk.push(b + a);
if (c == "-") stk.push(b - a);
if (c == "*") stk.push(b * a);
if (c == "/") stk.push(b / a);
}else
stk.push(stoi(c));
}
return stk.top();
}
int main() {
string s = "24 9 6 3 / + 5 - * 4 +";
cout << "answer:" << eval(s) << endl;
return 0;
}

View File

@@ -0,0 +1,100 @@
#include <bits/stdc++.h>
using namespace std;
const int N = 1000010, M = N << 1;
#define ls e[h[u]] // 左儿子
#define rs e[ne[h[u]]] // 右儿子,由于表达式树的特殊性,有右儿子时一定有左儿子
stack<int> stk; // 节点号栈
int a[N], al = 25; // a[]:节点号对应的字符,0~25为数字a~z在表达式树中节点的编号,26及以上为每一个操作符的编号,注意:如果前后出现多次同一运算符,它们的节点号是不一样的
// 链式前向星
int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c = 0) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
// 节点u
char out(int u) {
if (u >= 26) return a[u]; // 节点号大于等于26的是操作符+-*/
return u + 'a'; // a~z,记录的是0~25,输出需要加上偏移量'a'
}
// 前缀表达式
string preorder(int u) {
string ans = "";
if (h[u] == -1) {
ans += out(u);
return ans;
}
ans = out(u) + preorder(ls) + preorder(rs);
return ans;
}
// 中缀表达式
string inorder(int u) {
string ans = "";
if (h[u] == -1) {
ans += out(u);
return ans;
}
if (u >= 26) ans += "("; // 如果当前标记是一个操作符,打印左括号
ans += inorder(ls) + out(u) + inorder(rs);
if (u >= 26) ans += ")"; // 如果当前标记是运算符,则打印右括号
return ans;
}
// 后缀表达式
string postorder(int u) {
string ans = "";
if (h[u] == -1) {
ans += out(u);
return ans;
}
ans = postorder(ls) + postorder(rs) + out(u);
return ans;
}
// 通过后缀表达式构建表达式树
int build(string postfix) {
// 链式前向星初始化
memset(h, -1, sizeof h);
// 后缀表达式->表达式树,可以转化:前缀表达式,中缀表达式,后缀表达式
for (int i = 0; i < postfix.size(); i++) { // 枚举后缀表达式每一个字符
char c = postfix[i];
if (c == ' ') continue; // 如果后缀表达式中存在空格这样的字符,跳过
if (isalpha(c))
stk.push(c - 'a'); // 不是操作符,是数字,按字符-'a'的数字号入栈
else { // 如果是运算符+-*/
int x = stk.top(); // 取出两个节点
stk.pop();
int y = stk.top();
stk.pop();
a[++al] = c; // 操作符,申请一个节点号,并且,记录下来节点号与原字符的关系
add(al, x), add(al, y); // 由操作符向左右两个儿子连出边
stk.push(al); // 操作符节点入栈
}
}
return stk.top(); // 栈顶部的字符就是根
}
/*
Prefix Expression: *+ab*c+de
Postfix Expression: ab+cde+**
Infix Expression: ((a+b)*(c*(d+e)))
*/
int main() {
// 后缀表达式
string postfix = "ab+cde+**";
// 创建树
int root = build(postfix);
cout << preorder(root) << endl; // 输出前缀表达式
cout << postorder(root) << endl; // 输出后缀表达式
cout << inorder(root) << endl; // 输出中缀表达式
return 0;
}

View File

@@ -0,0 +1,20 @@
#include <bits/stdc++.h>
using namespace std;
int main() {
int a, b, c, d, x, e, y;
cin >> a >> b >> c >> d >> x >> e >> y;
for (int i = 1; i <= 500; i++)
for (int j = 1; j <= 500; j++)
for (int k = 1; k <= 500; k++)
if (i + j * b == a * k * b && i + j * d == c * k * d) {
for (int u = 1; u <= 500; u++)
if (u * x * k + (u - e) * y * k == (x + y) * j + i) {
cout << u << endl;
exit(0);
}
}
return 0;
}

View File

@@ -0,0 +1,80 @@
/*
测试用例1:
0&(1|0)|(1|1|1&0)
答案:
1
1 2
测试用例2:
(0|1&0|1|1|(1|1))&(0&1&(1|0)|0|1|0)&0
答案:
0
2 3
*/
#include <bits/stdc++.h>
using namespace std;
struct Node {
int v, a, b; // v:代表当前的结果值,a: &短路的次数 b:|短路的次数
};
stack<Node> num;
stack<char> stk;
unordered_map<char, int> h{{'|', 1}, {'&', 2}};
void eval() {
//这里要注意从栈中弹出元素的顺序,先出来的是y,后出来的是x
Node y = num.top();
num.pop();
Node x = num.top();
num.pop();
char p = stk.top();
stk.pop();
Node r;
if (p == '|') {
if (x.v == 1)
r = {1, x.a, x.b + 1};
else
r = {y.v, x.a + y.a, x.b + y.b};
} else if (p == '&') {
if (x.v == 1)
r = {y.v, x.a + y.a, x.b + y.b};
else
r = {0, x.a + 1, x.b};
}
num.push(r);
}
int main() {
string s;
cin >> s;
for (int i = 0; i < s.size(); i++) {
if (isdigit(s[i])) {
int x = 0;
while (i < s.size() && isdigit(s[i])) {
x = x * 10 + s[i] - '0';
i++;
}
i--;
num.push({x, 0, 0});
} else if (s[i] == '(')
stk.push(s[i]);
else if (s[i] == ')') {
while (stk.top() != '(') eval();
stk.pop();
} else {
while (stk.size() && h[stk.top()] >= h[s[i]]) eval();
stk.push(s[i]);
}
}
while (stk.size()) eval();
// printf("%d\n", num.top().v);
printf("%d %d\n", num.top().a, num.top().b);
return 0;
}

View File

@@ -0,0 +1,775 @@
## 表达式求值专题
### 一、理论知识
#### [$22$张图带你深入剖析前缀、中缀、后缀表达式以及表达式求值](https://www.cnblogs.com/littlehb/p/16706908.html)
####[前缀/中缀/后缀----表达式之间的相互转换](https://blog.csdn.net/qq_40877281/article/details/115824864)
![](http://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/2023/04/9cc1e6409de974500ebc4934e320190b.png)
### 二、练习题
#### 1、中缀转后缀
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
// 中缀表达式转后缀表达式
/*
测试用例1:
a+b*c+(d*e+f)*g
答案:
abc*+de*f+g*+
测试用例2:
(6+3*(7-4))-8/2
答案:
6 3 7 4 - * + 8 2 / -
测试用例3:
(24*(9+6/3-5)+4)
答案:
24 9 6 3 / + 5 - * 4 +
测试用例4:
2*3*2+5/3
*/
unordered_map<char, int> g{{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}}; // 操作符优化级
string t; // 结果串
stack<char> stk; // 栈
// 中缀转后缀
string infixToPostfix(string s) {
for (int i = 0; i < s.size(); i++) {
// ①数字
if (isdigit(s[i])) {
int x = 0;
while (i < s.size() && isdigit(s[i])) {
x = x * 10 + s[i] - '0';
i++;
}
i--;
t += to_string(x), t += ' ';
} else if (isalpha(s[i])) // ②字符,比如a,b,c
t += s[i], t += ' ';
else if (s[i] == '(') // ③左括号
stk.push(s[i]); // 左括号入栈
else if (s[i] == ')') { // ④右括号
while (stk.top() != '(') { // 让栈中元素(也就是+-*/和左括号)一直出栈,直到匹配的左括号出栈
t += stk.top(), t += ' ';
stk.pop();
}
stk.pop(); // 左括号也需要出栈
} else {
// ⑤操作符 +-*/
while (stk.size() && g[s[i]] <= g[stk.top()]) {
t += stk.top(), t += ' ';
stk.pop();
}
stk.push(s[i]); // 将自己入栈
}
}
// 当栈不为空时,全部输出
while (stk.size()) {
t += stk.top(), t += ' ';
stk.pop();
}
return t;
}
int main() {
string infix = "(24*(9+6/3-5)+4)";
string postfix = infixToPostfix(infix);
cout << "中缀表达式: " << infix << endl;
cout << "后缀表达式: " << postfix << endl;
return 0;
}
```
#### 2、中缀转后缀(逻辑)
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
/*
中缀逻辑表达式转后缀逻辑表达式
测试用例:
0&(0|1|0)
答案:
001|0|&
*/
unordered_map<char, int> g{{'|', 1}, {'&', 2}};
string s, t;
stack<char> stk;
int main() {
cin >> s;
for (int i = 0; i < s.size(); i++) {
if (isdigit(s[i]) || isalpha(s[i]))
t += s[i];
else if (s[i] == '(')
stk.push(s[i]);
else if (s[i] == ')') {
while (stk.top() != '(') {
t += stk.top();
stk.pop();
}
stk.pop();
} else {
while (stk.size() && g[s[i]] <= g[stk.top()]) {
t += stk.top();
stk.pop();
}
stk.push(s[i]);
}
}
while (stk.size()) {
t += stk.top();
stk.pop();
}
cout << t << endl;
return 0;
}
```
#### 3、后缀表达式求值
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
// 计算后缀表达式的值
// 判断字符是否是操作符
bool isOp(char c) {
return c == '+' || c == '-' || c == '*' || c == '/';
}
// 后缀表达式求值
int eval(string s) {
stack<int> stk; // 后缀表达式求值要使用到一个栈
// 构造一个字符串流,方便处理带空格的后缀表达式
stringstream s1(s);
string c;
while (s1 >> c) {
if (isOp(c[0])) { // 如果是操作数,将其转换为整数并入栈
int a = stk.top();
stk.pop();
int b = stk.top();
stk.pop();
if (c == "+") stk.push(b + a);
if (c == "-") stk.push(b - a);
if (c == "*") stk.push(b * a);
if (c == "/") stk.push(b / a);
} else
stk.push(stoi(c));
}
return stk.top();
}
int main() {
/*
中缀表达式: (24*(9+6/3-5)+4)
后缀表达式: 24 9 6 3 / + 5 - * 4 +
值=148
*/
string s = "24 9 6 3 / + 5 - * 4 +";
cout << "计算结果为: " << eval(s) << endl;
return 0;
}
```
#### [4、$AcWing$ $3302$. 中缀表达式求值](https://www.acwing.com/problem/content/3305/)
> 加减乘除四则运算,**利用栈**,**中缀表达式求值**,运算符优先顺序表
**直接计算版本**
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
/*
测试用例I:
(2+2)*(1+1)
答案:8
测试用例II:
2+(3*4)-((5*9-5)/8-4)
答案:13
*/
stack<int> num; // 数字栈
stack<char> op; // 操作符栈
// 优先级表
unordered_map<char, int> g{{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}};
/**
* 功能:计算两个数的和差积商
*/
void eval() {
int a = num.top(); // 第二个操作数
num.pop();
int b = num.top(); // 第一个操作数
num.pop();
char p = op.top(); // 运算符
op.pop();
int r; // 结果
// 计算结果
if (p == '+') r = b + a;
if (p == '-') r = b - a;
if (p == '*') r = b * a;
if (p == '/') r = b / a;
// 结果入栈
num.push(r);
}
int main() {
// 读入表达式
string s;
cin >> s;
// 遍历字符串的每一位
for (int i = 0; i < s.size(); i++) {
// ① 如果是数字,则入栈
if (s[i] >= '0' && s[i] <= '9') {
// 读出完整的数字
int x = 0;
while (i < s.size() && s[i] >= '0' && s[i] <= '9') {
x = x * 10 + s[i] - '0';
i++;
}
i--; // 加多了一位,需要减去
num.push(x); // 数字入栈
}
// ② 左括号无优先级,入栈
else if (s[i] == '(')
op.push(s[i]);
// ③ 右括号时,需计算最近一对括号里面的值
else if (s[i] == ')') {
// 从栈中向前找,一直找到左括号
while (op.top() != '(') eval(); // 将左右括号之间的计算完,维护回栈里
// 左括号出栈
op.pop();
} else { // ④ 运算符
// 如果待入栈运算符优先级低,则先计算
while (op.size() && g[op.top()] >= g[s[i]]) eval();
op.push(s[i]); // 操作符入栈
}
}
while (op.size()) eval(); // ⑤ 剩余的进行计算
printf("%d\n", num.top()); // 输出结果
return 0;
}
```
**中转后,后求值版本**
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
// 测试用例:
// (23+124)*(98-2)
// 中缀转后缀
unordered_map<char, int> g{{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}}; // 操作符优化级
string infixToPostfix(string s) {
stack<char> stk; // 栈
string t; // 结果串
for (int i = 0; i < s.size(); i++) {
// ①数字
if (isdigit(s[i])) {
int x = 0;
while (i < s.size() && isdigit(s[i])) {
x = x * 10 + s[i] - '0';
i++;
}
i--;
t += to_string(x) + " ";
} else if (isalpha(s[i])) // ②字符,比如a,b,c
t.push_back(s[i]), t.push_back(' ');
else if (s[i] == '(') // ③左括号
stk.push(s[i]); // 左括号入栈
else if (s[i] == ')') { // ④右括号
while (stk.top() != '(') { // 让栈中元素(也就是+-*/和左括号)一直出栈,直到匹配的左括号出栈
t.push_back(stk.top()), t.push_back(' ');
stk.pop();
}
stk.pop(); // 左括号也需要出栈
} else {
// ⑤操作符 +-*/
while (stk.size() && g[s[i]] <= g[stk.top()]) {
t.push_back(stk.top()), t.push_back(' ');
stk.pop();
}
stk.push(s[i]); // 将自己入栈
}
}
// 当栈不为空时,全部输出
while (stk.size()) {
t.push_back(stk.top()), t.push_back(' ');
stk.pop();
}
return t;
}
// 判断字符是否是操作符
bool isOp(char c) {
return c == '+' || c == '-' || c == '*' || c == '/';
}
// 后缀表达式求值
int eval(string s) {
stack<int> stk; // 后缀表达式求值要使用到一个栈
// 构造一个字符串流,方便处理带空格的后缀表达式
stringstream s1(s);
string c;
while (s1 >> c) {
if (isOp(c[0])) { // 如果是操作数,将其转换为整数并入栈
int a = stk.top();
stk.pop();
int b = stk.top();
stk.pop();
if (c == "+") stk.push(b + a);
if (c == "-") stk.push(b - a);
if (c == "*") stk.push(b * a);
if (c == "/") stk.push(b / a);
} else
stk.push(stoi(c));
}
return stk.top();
}
int main() {
string s;
cin >> s;
// 中缀转后缀
s = infixToPostfix(s);
// 后缀表达式求值
cout << eval(s) << endl;
return 0;
}
```
#### 5、表达式求值(逻辑)
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
/*
0&(1|0)|(1|1|1&0)
答案:1
(0|1&0|1|1|(1|1))&(0&1&(1|0)|0|1|0)&0
答案:0
*/
unordered_map<char, int> g{{'|', 1}, {'&', 2}};
stack<int> num;
stack<char> op;
void eval() {
int a = num.top();
num.pop();
int b = num.top();
num.pop();
char p = op.top();
op.pop();
int r;
if (p == '|') r = b | a;
if (p == '&') r = b & a;
num.push(r);
}
int main() {
string s;
cin >> s;
for (int i = 0; i < s.size(); i++) {
if (isdigit(s[i]))
num.push(s[i] - '0');
else if (s[i] == '(')
op.push(s[i]);
else if (s[i] == ')') {
while (op.top() != '(') eval();
op.pop();
} else {
while (op.size() && g[op.top()] >= g[s[i]]) eval();
op.push(s[i]);
}
}
while (op.size()) eval();
printf("%d\n", num.top());
return 0;
}
```
#### [6、$AcWing$ $1623$. 二叉表达式树转中缀表达式](https://www.acwing.com/problem/content/1625/)
> - 给定一个**二叉表达式树**,请你输出相应的**中缀表达式**,并利用括号反映运算符的优先级。
>**$dfs$中序遍历** 可以求中缀表达式的值,仔细观察用例,可以知道根不是任何人的儿子,需要记录入度找到入度为$0$的点,就是根
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
const int N = 30;
/*
思路
1、数据处理,通过记录入度,找到入度为0的点,找到根节点
2、二元运算必须有符号右边那个式子,即根据数据构建完树后的右孩子
故在递归遍历树的时候,只要判断该节点是否有孩子,
然后返回的字符串数: 递归左子树的string值 + 该节点的string值 + 递归右子树的string值
3、如果该节点不是根节点,则在该返回值的左右两边加上括号
4、最后输出返回值
黄海总结:
1、乍一看此题,感觉无从下手,因为不知道每个点都是什么节点号!后来慢慢从测试用例1中发现,
data 都是什么 *,a,b,-,c之类,不是节点号,应该是具体的操作符,也就是值。
那节点号在哪里呢?再看一下后面的left_child,right_child,发现是
8 7
-1 -1
4 1
2 5
这个来理解一下,应该是指8号节点,7号节点,-1代表的是NULL节点,也就是到头了,是叶子。
再来仔细看一下,发现出现的数字是:1 2 4 5 6 7 8,单单缺少了一个3!
为啥缺少3呢?
因为3不是任何人的left_child,right_child,它是根!!!!,这样的话,整个图就完整了!
*/
int n;
string w[N];
int l[N], r[N], in[N];
int isleaf[N];
/*
8
* 8 7
a -1 -1
* 4 1
+ 2 5
b -1 -1
d -1 -1
- -1 6
c -1 -1
(a+b)*(c*(-d))
*/
string dfs(int u) {
// 表达式树特点:叶子节点都是真实数字,非叶子节点都是操作符
string left, right;
// if (isleaf[u]) return w[u]; // 递归终点,如果是叶子,本句话可以省略掉
// 这是因为isleaf[u]时,l[u]==-1 && r[u]==-1 ,则下面的两个分支都不会走,直接到了return那句,left,right都是空,还是返回w[u],一样的
if (l[u] != -1) {
left = dfs(l[u]); // 递归左儿子,
if (!isleaf[l[u]]) left = "(" + left + ")"; // 如果左儿子不是叶子,需要把表达式加上括号
}
if (r[u] != -1) { // 右儿子不为空
right = dfs(r[u]); // 递归右儿子
if (!isleaf[r[u]]) right = "(" + right + ")"; // 如果右儿子不是叶子,需要把表达式加上括号
}
// 返回中序遍历结果
return left + w[u] + right;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> w[i] >> l[i] >> r[i]; // data left_child right_child
if (l[i] != -1) in[l[i]]++; // 记录入度
if (r[i] != -1) in[r[i]]++; // 记录入度
if (l[i] == -1 && r[i] == -1) isleaf[i] = 1; // 叶子
}
// 找根
int root;
for (root = 1; root <= n; root++)
if (!in[root]) break; // 入度为零的是根
// 通过中序遍历,构建中缀表达式
cout << dfs(root) << endl;
return 0;
}
```
#### [7、$AcWing$ $4274$. 二叉表达式树转后缀表达式](https://www.acwing.com/problem/content/4277/)
> - 给定一个**二叉表达式树**,请你输出相应的**后缀表达式**,要求使用括号反映运算符的优先级。
>**$dfs$后序遍历** 可以求后缀表达式的值,仔细观察用例,可以知道根不是任何人的儿子,需要记录入度找到入度为$0$的点,就是根
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
const int N = 30;
int n;
string w[N];
int l[N], r[N], in[N];
int isleaf[N];
/*
题目描述:
给出中缀表达式的语法树 请你将后缀表达式输出 括号体现优先级 操作符不可用括号包含。
思路:
后缀表达式就是把运算符后置的表达式,可以抽象成一棵二叉树,其中的的节点有两种情况:
(1)有左右儿子
(2)只有右儿子
只有右儿子的情况就是负数啦~ 测试用例: - -1 6
然后就是找根,这个很简单,统计所有节点的入度,唯一的一个入度是 0 的节点就是根。
再写个简单的递归就可以了。
8
* 8 7
a -1 -1
* 4 1
+ 2 5
b -1 -1
d -1 -1
- -1 6
c -1 -1
输出样例1:
(((a)(b)+)((c)(-(d))*)*)
*/
string dfs(int u) {
if (isleaf[u]) return "(" + w[u] + ")";
if (l[u] == -1 && r[u] != -1) return "(" + w[u] + dfs(r[u]) + ")"; // 负号
return "(" + dfs(l[u]) + dfs(r[u]) + w[u] + ")"; // 后缀表达式
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> w[i] >> l[i] >> r[i];
if (l[i] != -1) in[l[i]]++;
if (r[i] != -1) in[r[i]]++;
if (l[i] == -1 && r[i] == -1) isleaf[i] = 1; // 叶子
}
// 找根
int root;
for (root = 1; root <= n; root++)
if (!in[root]) break; // 入度为零的是根
cout << dfs(root) << endl;
return 0;
}
```
####[8、$HDU1123$ $Complicated$ $Expressions$](http://acm.hdu.edu.cn/showproblem.php?pid=1123)
> **题意**:
给定一个 **中缀表达式**(只有小写字母,加减乘除,左右括号),**会带有很多冗余的括号**,要求去除这些括号时满足以下的要求:
($1$)、完全没有意义的括号去除;
($2$)、加法和乘法的结合律 -> a+(b+c) 等价于 a+b+c ,a*(b*c) 等价于 a * b * c;故这种括号要去除
($3$)、有明显的不影响运算的括号去除,如a+(b * c) -> a+b*c
**思路:**
先不考虑上述要求,
($1$)对于给定的中缀表达式,先转化成后缀表达式(中缀->后缀);
($2$)然后利用后缀表达式建立一颗二叉树(**后缀二叉树建立表达式树**);
($3$)利用表达式树得到中缀表达式。
做完以上要求后,把要求的括号去除与否的规则考虑进去就可以了
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
// 中缀转后缀
unordered_map<char, int> g{{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}}; // 操作符优先级
string infixToPostfix(string s) {
string t; // 结果串
stack<char> stk; // 栈
for (int i = 0; i < s.size(); i++) {
// ①数字
if (isdigit(s[i])) {
int x = 0;
while (i < s.size() && isdigit(s[i])) {
x = x * 10 + s[i] - '0';
i++;
}
i--;
t += to_string(x), t += ' ';
} else if (isalpha(s[i])) // ②字符,比如a,b,c
t += s[i], t += ' ';
else if (s[i] == '(') // ③左括号
stk.push(s[i]); // 左括号入栈
else if (s[i] == ')') { // ④右括号
while (stk.top() != '(') { // 让栈中元素(也就是+-*/和左括号)一直出栈,直到匹配的左括号出栈
t += stk.top(), t += ' ';
stk.pop();
}
stk.pop(); // 左括号也需要出栈
} else {
// ⑤操作符 +-*/
while (stk.size() && g[s[i]] <= g[stk.top()]) {
t += stk.top(), t += ' ';
stk.pop();
}
stk.push(s[i]); // 将自己入栈
}
}
// 当栈不为空时,全部输出
while (stk.size()) {
t += stk.top(), t += ' ';
stk.pop();
}
return t;
}
// 后缀转表达式树
const int N = 500, M = N << 1;
#define ls e[h[u]] // 左儿子
#define rs e[ne[h[u]]] // 右儿子,由于表达式树是二叉树,有右儿子时一定有左儿子
// 链式前向星
int e[M], h[N], idx, ne[M];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
char a[N];
int al = 25; // a[]:节点号对应的字符,0~25为数字a~z在表达式树中节点的编号,26及以上为每一个操作符的编号,注意:如果前后出现多次同一运算符,它们的节点号是不一样的
// 构造表达式树
int build(string postfix) {
stack<int> stk; // 节点号栈
// 链式前向星初始化
memset(h, -1, sizeof h);
idx = 0, al = 25;
// 后缀表达式->表达式树,可以转化:前缀表达式,中缀表达式,后缀表达式
for (int i = 0; i < postfix.size(); i++) { // 枚举后缀表达式每一个字符
char c = postfix[i];
if (c == ' ') continue;
if (isalpha(c))
stk.push(c - 'a'); // 不是操作符,是数字,按字符-'a'的数字号入栈
else { // 如果是运算符
int x = stk.top(); // 取出两个节点
stk.pop();
int y = stk.top();
stk.pop();
a[++al] = c; // 操作符,申请一个节点号,并且,记录下来节点号与原字符的关系
add(al, x), add(al, y); // 由操作符向左右两个儿子连出边
stk.push(al); // 操作符节点入栈
}
}
return stk.top(); // 栈顶部的字符就是根
}
// 输出节点u
char out(int u) {
if (u >= 26) return a[u]; // 节点号大于等于26的是操作符+-*/
return u + 'a'; // a~z,记录的是0~25,输出需要加上偏移量'a'
}
// 判断操作符
bool isOp(char c) {
return c == '+' || c == '-' || c == '*' || c == '/';
}
// 获取符合题意的中缀表达式
string inorder(int u) {
string ans = "";
if (h[u] == -1) {
ans += out(u);
return ans;
}
string lstr = inorder(ls);
string rstr = inorder(rs);
// 下面的代码是本题的题意要求
if (isOp(a[ls]) && g[a[u]] > g[a[ls]]) { // 如果左儿子是操作符,并且当前运算符优先级大于左儿子运算优先级,需要左儿子加上括号
ans += '(';
ans += lstr;
ans += ')';
} else
ans += lstr; // 否则,左儿子不加括号
ans += out(u); // 左儿子完事,把自己加上
if (isOp(a[rs]) && g[a[rs]] <= g[a[u]]) { // 如果右儿子是操作符,并且,右儿子运算符优先级小于等于当前运算符优先级
if ((a[u] == '+' && a[rs] == '-') // 当前是+,右儿子是-
|| (a[u] == '*' && a[rs] == '/') // 当前是*,右儿子是/
|| (a[u] == '+' && a[rs] == '+') // 当前是+,右儿子是+
|| (a[u] == '*' && a[rs] == '*')) // 当前是*,右儿子是*
ans += rstr; // 都不需要加括号
else
ans += '(' + rstr + ')'; // 否则需要加括号
} else
ans += rstr; // 否则右儿子不加括号
return ans;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("HDU1123.in", "r", stdin);
freopen("HDU1123.out", "w", stdout);
#endif
int n;
cin >> n;
while (n--) {
string infix;
cin >> infix;
// 中缀转后缀
string postfix = infixToPostfix(infix);
// 根据后缀表达式创建表达式树
int root = build(postfix);
cout << inorder(root) << endl;
}
return 0;
}
```
### 三、$TODO$ 以后再做
#### [【洛谷】$P1449$ 后缀表达式](https://www.cnblogs.com/littlehb/p/15074451.html)
#### [【洛谷】$P7073$ [$CSP-J2020$] 表达式](https://www.luogu.com.cn/problem/P7073)
#### [【洛谷】$P8815$ [$CSP-J2022$] 逻辑表达式](https://www.luogu.com.cn/problem/P8815)
#### [【洛谷】$P1981$ $[NOIP2013$ 普及组] 表达式求值](https://www.luogu.com.cn/problem/P1981)
#### [【洛谷】$P1310$ $[NOIP2011$ 普及组] 表达式的值](https://www.luogu.com.cn/problem/P1310)
#### [$AcWing$ $2769$. 表达式](https://www.acwing.com/problem/content/2771/)【中等】
#### [$AcWing$ $151$.表达式计算$4$](https://www.acwing.com/problem/content/153/)【中等】
#### [$AcWing$ $448$. 表达式的值](https://www.acwing.com/problem/content/450/)【中等】
#### [$AcWing$ $454$. 表达式求值](https://www.acwing.com/problem/content/456/)【中等】
#### [$AcWing$ $486$. 等价表达式](https://www.acwing.com/problem/content/488/)【中等】
#### [$AcWing$ $1247$. 后缀表达式](https://www.acwing.com/problem/content/1249/)【中等】
#### [$AcWing$ $1330$. 表达式求值](https://www.acwing.com/problem/content/1332/)【中等】
#### [$AcWing$ $4730$. 逻辑表达式](https://www.acwing.com/problem/content/4733/)【中等】

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -0,0 +1,173 @@
##[$AcWing$ $826$. 单链表 ](https://www.acwing.com/problem/content/828/)
### 一、题目描述
实现一个单链表,链表初始为空,支持三种操作:
向链表头插入一个数;删除第 $k$ 个插入的数后面的数;
在第 $k$ 个插入的数后插入一个数。
现在要对该链表进行 $M$ 次操作,进行完所有操作后,从头到尾输出整个链表。
注意:题目中第 $k$ 个插入的数并不是指当前链表的第 $k$ 个数。例如操作过程中一共插入了 $n$ 个数,则按照插入的时间顺序,这 $n$ 个数依次为:第 $1$ 个插入的数,第 $2$ 个插入的数,…第 $n$ 个插入的数。
**输入格式**
第一行包含整数 $M$,表示操作次数。
接下来 $M$ 行,每行包含一个操作命令,操作命令可能为以下几种:
> `H x`,表示向链表头插入一个数 `x`。
`D k`,表示删除第 `k` 个插入的数后面的数(当 `k` 为 `0` 时,表示删除头结点)。
`I k x`,表示在第 `k` 个插入的数后面插入一个数 `x`(此操作中 `k` 均大于 `0`)。
**输出格式**
共一行,将整个链表从头到尾输出。
**数据范围**
$1≤M≤100000$所有操作保证合法。
**输入样例:**
```cpp {.line-numbers}
10
H 9
I 1 1
D 1
D 0
H 6
I 3 6
I 4 5
I 4 5
I 3 4
D 6
```
**输出样例:**
```cpp {.line-numbers}
6 4 6 5
```
### 二、链表有什么用,用数组不就可以了吗?
数组是一种支持随机访问,但不支持在任意位置插入或删除元素的数据结构。与之相对应,**链表支持在任意位置插入或删除,但只能按顺序依次访问其中的元素**。
本节主要用数组模拟链表,速度更快,并且为静态存储。
$e[N]$存储节点的值,$ne[N]$存储该节点下一节点的下标。
注意:下标从零开始,第$k$个数对应数组$e[k-1]$;在删除节点时,要判断该节点是否为头节点,若为头节点,直接$head$指向该节点所指向的位置,即$head = ne[head]$;
![](https://img2020.cnblogs.com/blog/8562/202109/8562-20210909104344568-1479835633.jpg)
1、将链表初始化,$head$指向$-1$。
2、将$x$插到头结点。
![](https://img2020.cnblogs.com/blog/8562/202109/8562-20210909104351989-981575149.jpg)
3、在第$k$个数后面插入$x$
![](https://img2020.cnblogs.com/blog/8562/202109/8562-20210909104357566-642699058.jpg)
4、删除第$k$个节点后面的数
![](https://img2020.cnblogs.com/blog/8562/202109/8562-20210909104403345-440403937.jpg)
### 三、C++ 代码
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
// 链表头:hh,空链表时,hh = φ 用数字-1来表示φ; 非空链表时,hh指向链表的第一个结点
// 链表结点:① 位置: idx ② 值: e[idx] , ③ 下一个结点的位置 : ne[idx]
// idx:下一个可用结点编号
int e[N], ne[N], idx, hh = -1;
void add_to_head(int x) {
e[idx] = x, ne[idx] = hh, hh = idx++;
}
void add(int k, int x) {
e[idx] = x, ne[idx] = ne[k], ne[k] = idx++;
}
void remove(int k) {
ne[k] = ne[ne[k]];
}
int main() {
int m;
cin >> m;
while (m--) {
int k, x;
char op;
cin >> op;
if (op == 'H') { // 表示向链表头插入一个数 x
cin >> x;
add_to_head(x);
} else if (op == 'D') { // 表示删除第 k 个插入的数后面的数
cin >> k;
if (k == 0)
hh = ne[hh];
else
remove(k - 1);
} else { // 表示在第 k 个插入的数后面插入一个数 x
cin >> k >> x;
add(k - 1, x);
}
}
for (int i = hh; ~i; i = ne[i]) printf("%d ", e[i]);
return 0;
}
```
### 链式前向星代码解析
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
const int N = 10010, M = N << 1; // 最大顶点数,最大边数
/*
链式前向星:保存的是每一个顶点的所有出边
h[u] 表示点u为出发点的第一条边的编号
e[i] 表示第i条边的指向终点,有时也写作to[i]
ne[i] 表示第i条边的下一条边的编号
idx 记录边的数量,表现在代码中就是一个计数器
*/
int e[M], h[N], idx, w[M], ne[M];
// 添加一条从 a 到 b 的边,边权为c
void add(int a, int b, int c = 0) {
// e[idx]=b 第idx号边,是指向点b的
// ne[idx]=h[a] 第idx号边,它的下一条边是原来h[a]指向的边
// w[idx]=c 第idx号边,权值是c
// h[a]=idx++ 以a点为出发点的所有边组成的链表中,第一个是idx号边,头插法
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
int main() {
// 初始化
memset(h, -1, sizeof h);
int n = 5; // 顶点数
// 添加边
add(0, 1);
add(0, 2);
add(1, 3);
add(2, 3);
add(3, 4);
// 遍历从每个顶点出发的边
for (int u = 0; u < n; u++) {
cout << "从 " << u << "号节点引出的边: " << endl;
for (int i = h[u]; ~i; i = ne[i]) { // 枚举从点u出发的每条边
int v = e[i];
// u 到点 v 的边
cout << u << " -> " << v << endl;
}
cout << endl;
}
return 0;
}
```