AcWing《PAT甲级辅导课》第5章 树
2021/12/1 23:10:00
本文主要是介绍AcWing《PAT甲级辅导课》第5章 树,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
第5章 树
1004. Counting Leaves
笔记
- 统计树每层叶子的个数,可用DFS或BFS
- 在DFS加入参数
depth
,可表示当前层号,但还需要全局变量记录树的层数 - 可用邻接表存储树
#include <iostream> #include <cstring> using namespace std; const int N = 110, M = 210, ROOT = 1; int n, m; int h[N], e[M], ne[M], idx; int cnt[N], max_depth; void add(int u, int v) { e[idx] = v; ne[idx] = h[u]; h[u] = idx++; } void dfs(int u, int depth) { if (h[u] == -1) { cnt[depth]++; // 叶节点 max_depth = max(max_depth, depth); return; } for (int p = h[u]; ~p; p = ne[p]) dfs(e[p], depth + 1); } int main() { cin >> n >> m; memset(h, -1, sizeof h); int u, son, k; for (int i = 0; i < m; i++) { cin >> u >> k; while(k--) { cin >> son; add(u, son); } } dfs(ROOT, 0); cout << cnt[0]; for (int i = 1; i <= max_depth; i++) cout << ' ' << cnt[i]; cout << endl; return 0; }
1020. Tree Traversals
笔记
- 根据中序遍历和后序遍历(或前序遍历)可唯一确定一棵二叉树
- 后序遍历序列 = m 1 m_1 m1个数构成的左子树后序遍历序列 + m 2 m_2 m2个数构成的左子树后序遍历序列 + 根节点
- 中序遍历序列 = m 1 m_1 m1个数构成的左子树中序遍历序列 + 根节点 + m 2 m_2 m2个数构成的左子树中序遍历序列
- 后序遍历最后一个位置是根节点,查找其在中序遍历的位置,可把中序遍历分成两段,左段是左子树的中序遍历,右段是右子树的中序遍历。根据左子树的中序遍历序列,可确定左子树的结点个数 m m m,从而后序遍历前 m m m个数构成的就是左子树的后序遍历序列,之后的数至倒数第二个就是右子树的后序遍历序列,借助递归则可继续做下去
- 构造完二叉树后可用BFS实现层序遍历
#include <iostream> #include <unordered_map> using namespace std; const int N = 35; int n; int inorder[N], postorder[N]; unordered_map<int, int> l, r, pos; int build(int in_l, int in_r, int post_l, int post_r) { int root = postorder[post_r]; // 当前后序遍历段的根节点下标 int k = pos[root]; // 根节点在中序遍历的位置(哈希表优化查找) if (in_l < k) { int m = (k - 1) - in_l + 1; // 左子树结点个数 l[root] = build(in_l, k - 1, post_l, post_l + (k - 1 - in_l)); } if (k < in_r) { int m = in_r - (k + 1) + 1; // 右子树结点个数 r[root] = build(k + 1, in_r, post_l + (k - 1 - in_l) + 1, post_r - 1); } return root; } int q[N], hh, tt; void bfs(int root) { q[++tt] = root; while(hh < tt) { int u = q[++hh]; if (l.count(u)) q[++tt] = l[u]; if (r.count(u)) q[++tt] = r[u]; } cout << q[1]; for (int i = 2; i <= n; i++) cout << ' ' << q[i]; cout << endl; } int main() { cin >> n; for (int i = 0; i < n; i++) cin >> postorder[i]; for (int i = 0; i < n; i++) { cin >> inorder[i]; pos[inorder[i]] = i; // 根据结点值查找其中序序列的位置 } int root = build(0, n - 1, 0, n - 1); bfs(root); return 0; }
1021. Deepest Root
笔记
- 可用并查集查找连通个数,每成功合并一次,则说明是树的一条边,如果给的所有边都能成功合并,则是一棵树,否则不能合并的边数就是连通数
- 可参照“树的中心”那题寻找各个点的最大深度,在本题中,各个边的权重都是1,不存在负数权值,能简化代码
#include <iostream> #include <cstring> using namespace std; const int N = 1e4 + 10, M = 2 * N; // 邻接表 int n; int h[N], e[M], ne[M], idx; void add(int u, int v) { e[idx] = v; ne[idx] = h[u]; h[u] = idx++; } // 并查集 int parent[N]; int find(int x) { if (parent[x] != x) parent[x] = find(parent[x]); return parent[x]; } // 树的中心 int d1[N], d2[N], p1[N], up[N]; int dfs_down(int u, int father) { for (int p = h[u]; ~p; p = ne[p]) { int v = e[p]; if (v == father) continue; // 不往上走 int d = 1 + dfs_down(v, u); if (d > d1[u]) { d2[u] = d1[u]; d1[u] = d; p1[u] = v; } else if (d > d2[u]) d2[u] = d; } return d1[u]; } void dfs_up(int u, int father) { for (int p = h[u]; ~p; p = ne[p]) { int v = e[p]; if (v == father) continue; // 不往上走 if (p1[u] == v) up[v] = 1 + max(up[u], d2[u]); // u的最长路径经过v else up[v] = 1 + max(up[u], d1[u]); // u的最长路径不经过v dfs_up(v, u); } } int main() { cin >> n; int u, v; for (int i = 1; i <= n; i++) parent[i] = i; // 初始化并查集 memset(h, -1, sizeof h); // 初始化邻接表 int cnt = n; for (int i = 0; i < n - 1; i++) { cin >> u >> v; add(u, v); add(v, u); if (find(u) != find(v)) { parent[find(u)] = find(v); // 合并 cnt--; } } if (cnt > 1) printf("Error: %d components\n", cnt); else { // 找树的中心(树形DP) dfs_down(1, -1); dfs_up(1, -1); int max_depth = -1; for (int i = 1; i <= n; i++) max_depth = max(max_depth, max(up[i], d1[i])); for (int i = 1; i <= n; i++) if (max(up[i], d1[i]) == max_depth) cout << i << endl; } return 0; }
1043. Is It a Binary Search Tree
笔记
- 把二叉搜索树的前序序列排序即可得到中序序列
- 若能根据前序序列和中序序列构造二叉树,则说明是一个二叉搜索树
- 可在构造二叉搜索树时,计算后序序列
#include <iostream> #include <algorithm> using namespace std; const int N = 1010; int n, cnt; int pre[N], in[N], post[N]; bool build(int pre_l, int pre_r, int in_l, int in_r) { if (in_l > in_r) return true; int root = pre[pre_l]; int k; for (k = in_l; k <= in_r; k++) if (in[k] == root) break; // 从前往后找第1个root(重复元素的第1个) if (k > in_r) return false; bool res = true; if(!build(pre_l + 1, pre_l + 1 + (k - 1 - in_l), in_l, k - 1)) res = false; if(!build(pre_l + 1 + (k - 1 - in_l) + 1, pre_r, k + 1, in_r)) res = false; // res = res && build(pre_l + 1, pre_l + 1 + (k - 1 - in_l), in_l, k - 1); // res = res && build(pre_l + 1 + (k - 1 - in_l) + 1, pre_r, k + 1, in_r); post[cnt++] = root; return res; } bool build_r(int pre_l, int pre_r, int in_l, int in_r) { if (in_l > in_r) return true; int root = pre[pre_l]; int k; for (k = in_r; k >= in_l; k--) if (in[k] == root) break; // 从后往前找第1个root(重复元素的最后1个) if (k < in_l) return false; bool res = true; if(!build_r(pre_l + 1, pre_l + 1 + (k - 1 - in_l), in_l, k - 1)) res = false; if(!build_r(pre_l + 1 + (k - 1 - in_l) + 1, pre_r, k + 1, in_r)) res = false; // res = res && build(pre_l + 1, pre_l + 1 + (k - 1 - in_l), in_l, k - 1); // res = res && build(pre_l + 1 + (k - 1 - in_l) + 1, pre_r, k + 1, in_r); post[cnt++] = root; return res; } int main() { cin >> n; for (int i = 0; i < n; i++) { cin >> pre[i]; in[i] = pre[i]; } sort(in, in + n); if (build(0, n - 1, 0, n - 1)) { puts("YES"); cout << post[0]; for (int i = 1; i < n; i++) cout << ' ' << post[i]; cout << endl; } else { reverse(in, in + n); cnt = 0; if (build_r(0, n - 1, 0, n - 1)) { puts("YES"); cout << post[0]; for (int i = 1; i < n; i++) cout << ' ' << post[i]; cout << endl; } else puts("NO"); } return 0; }
1064. Complete Binary Search Tree
笔记
- 采用一维数组存储完全二叉树
- 根据中序遍历次序填入排好序的二叉搜索树元素构成的序列
- 按顺序输出一维数组即可得到层次遍历结果
#include <iostream> #include <algorithm> using namespace std; const int N = 1010; int n, a[N], b[N]; int cnt = 1; void inorder(int u) { if (2 * u <= n) inorder(2 * u); b[u] = a[cnt++]; if (2 * u + 1 <= n) inorder(2 * u + 1); } int main() { cin >> n; for (int i = 1; i <= n; i++) cin >> a[i]; sort(a + 1, a + n + 1); inorder(1); // 按中序遍历填入 cout << b[1]; for (int i = 2; i <= n; i++) cout << ' ' << b[i]; return 0; }
1086. Tree Traversals Again
笔记
- 非递归后序遍历具有如下特点
- 首次一定是
push
,压入的元素一定是root
- 如果上一次操作是
push
,则当前结点是上一个结点的左孩子 - 如果上一次操作是
pop
,则当前结点是上一个结点的右孩子
- 首次一定是
- 记录所有结点的左右孩子后,即可用DFS从
root
开始输出其后序遍历次序
#include <iostream> #include <stack> using namespace std; const int N = 35; int n, l[N], r[N]; // 左右孩子 string output; void post_order(int u) { if (!u) return; // 空节点 post_order(l[u]); post_order(r[u]); output += to_string(u) + " "; } int main() { cin >> n; stack<int> s; string op; int root, x; int last = 0; // 上一个结点 int type = 0; // 0表示push,1表示pop for (int i = 0; i < 2 * n; i++) { cin >> op; if (op == "Push") { cin >> x; if (!i) root = x; // 首次压入是根节点 else { if (type == 0) l[last] = x; // 上次是push,当前结点是上个结点的左孩子 else r[last] = x; // 上次是pop,当前结点是上个结点的左孩子 } s.push(x); // 模拟栈压入 last = x; type = 0; } else { // pop last = s.top(); // 模拟栈弹出 s.pop(); type = 1; } } post_order(root); cout << output.substr(0, output.size() - 1) << endl; // 去掉末尾空格 return 0; }
1099. Build A Binary Search Tree
笔记
- 先用中序遍历把排好序的数填入,然后再用层序遍历输出
~p
等价于p != -1
#include <iostream> #include <algorithm> using namespace std; const int N = 110, ROOT = 0; int n, l[N], r[N], w[N], a[N]; int cnt; void inorder(int u) { if (u == -1) return; inorder(l[u]); a[u] = w[cnt++]; // 填数 inorder(r[u]); } string res; int q[N]; void bfs(int root) { int hh = 0, tt = 0; q[++tt] = root; while(hh < tt) { int x = q[++hh]; if (l[x] != -1) q[++tt] = l[x]; if (r[x] != -1) q[++tt] = r[x]; } for (int i = 1; i <= n; i++) res += to_string(a[q[i]]) + " "; } int main() { cin >> n; for (int i = 0; i < n; i++) cin >> l[i] >> r[i]; for (int i = 0; i < n; i++) cin >> w[i]; sort(w, w + n); inorder(ROOT); // 中序遍历填数 bfs(ROOT); cout << res.substr(0, res.size() - 1) << endl; return 0; }
1102. Invert a Binary Tree
笔记
- 两种方法
- 方法1 不翻转
- 实现层序遍历时,先加入右孩子,再加入左孩子
- 实现中序遍历时,先遍历右子树,再遍历左子树
- 方法2 翻转
- 采用后序遍历翻转,即访问时交换左右孩子
- 正常实现层序遍历和中序遍历即可
- 方法1 不翻转
方法1 不翻转
#include <iostream> #include <cstring> using namespace std; const int N = 15; int n, l[N], r[N]; bool has_father[N]; string res_1; int q[N]; void bfs(int root) { int hh = 0, tt = 0; q[++tt] = root; while(hh < tt) { int x = q[++hh]; if (~r[x]) q[++tt] = r[x]; if (~l[x]) q[++tt] = l[x]; } for (int i = 1; i <= n; i++) res_1 += to_string(q[i]) + " "; } string res_2; void inorder(int u) { if (u == -1) return; inorder(r[u]); res_2 += to_string(u) + " "; inorder(l[u]); } int main() { cin >> n; memset(l, -1, sizeof l); memset(r, -1, sizeof r); string ls, rs; for (int i = 0; i < n; i++) { cin >> ls >> rs; if (ls != "-") { l[i] = ls[0] - '0'; has_father[l[i]] = true; } if (rs != "-") { r[i] = rs[0] - '0'; has_father[r[i]] = true; } } int root = -1; for (int i = 0; i < n; i++) if (!has_father[i]) { root = i; break; } bfs(root); inorder(root); cout << res_1.substr(0, res_1.size() - 1) << endl; cout << res_2.substr(0, res_2.size() - 1) << endl; return 0; }
方法2 翻转
#include <iostream> #include <cstring> using namespace std; const int N = 15; int n, l[N], r[N]; bool has_father[N]; void reverse(int u) { if (u == -1) return; reverse(l[u]); reverse(r[u]); swap(l[u], r[u]); } string res_1; int q[N]; void bfs(int root) { int hh = 0, tt = 0; q[++tt] = root; while(hh < tt) { int x = q[++hh]; if (~l[x]) q[++tt] = l[x]; if (~r[x]) q[++tt] = r[x]; } for (int i = 1; i <= n; i++) res_1 += to_string(q[i]) + " "; } string res_2; void inorder(int u) { if (u == -1) return; inorder(l[u]); res_2 += to_string(u) + " "; inorder(r[u]); } int main() { cin >> n; memset(l, -1, sizeof l); memset(r, -1, sizeof r); string ls, rs; for (int i = 0; i < n; i++) { cin >> ls >> rs; if (ls != "-") { l[i] = ls[0] - '0'; has_father[l[i]] = true; } if (rs != "-") { r[i] = rs[0] - '0'; has_father[r[i]] = true; } } int root = -1; for (int i = 0; i < n; i++) if (!has_father[i]) { root = i; break; } reverse(root); bfs(root); inorder(root); cout << res_1.substr(0, res_1.size() - 1) << endl; cout << res_2.substr(0, res_2.size() - 1) << endl; return 0; }
1110. Complete Binary Tree
笔记
- 模拟构造完全二叉树,如果所有结点的最大编号刚好为 n n n(从1开始编号),则是一个完全二叉树,否则不是
#include <iostream> #include <cstring> using namespace std; const int N = 25; int n, l[N], r[N]; bool has_father[N]; int max_u = -1, max_k = -1; void dfs(int u, int k) { if (u == -1) return; if (k > max_k) { max_u = u; max_k = k; } dfs(l[u], 2 * k); dfs(r[u], 2 * k + 1); } int main () { cin >> n; memset(l, -1, sizeof l); memset(r, -1, sizeof r); string ls, rs; for (int i = 0; i < n; i++) { cin >> ls >> rs; if (ls != "-") { l[i] = stoi(ls); has_father[l[i]] = true; } if (rs != "-") { r[i] = stoi(rs); has_father[r[i]] = true; } } int root = 0; while(has_father[root]) root++; dfs(root, 1); // 从1开始填完全二叉树 if (max_k == n) printf("YES %d\n", max_u); else printf("NO %d\n", root); return 0; }
1115. Counting Nodes in a BST
笔记
- 构造二叉搜索树,注意从1开始编号,
insert(int& u, int x)
可自动更新左右子树指针 - 用DFS遍历所有结点,计算各层结点数,同时还要保存最大层数
#include <iostream> using namespace std; const int N = 1010; int n, l[N], r[N], v[N], idx; int cnt[N], max_depth; void insert(int &u, int x) { // 从结点u开始插入x if (!u) { // 首次插入 u = ++idx; // 计算可用结点位置(数组下标表示),并保存(引用可修改变量) v[u] = x; // 保存该结点 } else if (x <= v[u]) insert(l[u], x); // 若插入成功,则会修改l[u]的值,指向新结点地址 else insert(r[u], x); // 若插入成功,则会修改r[u]的值,指向新结点地址 } void dfs(int u, int depth) { if (!u) return; cnt[depth]++; max_depth = max(max_depth, depth); dfs(l[u], depth + 1); dfs(r[u], depth + 1); } int main() { cin >> n; int x; int root = 0; // 0表示首次插入 for (int i = 0; i < n; i++) { cin >> x; insert(root, x); // 自动更新当前root位置 } dfs(root, 0); int n1 = cnt[max_depth], n2 = cnt[max_depth - 1]; printf("%d + %d = %d\n", n1, n2, n1 + n2); return 0; }
1119. Pre- and Post-order Traversals
笔记
- 用DFS搜索,如果找到一种情况,则继续搜索,如果找到一种以上的情况,则可停止
- 可根据前序遍历序列划分左右子树,即根节点 + 左子树区间 + 右子树区间,可在左右边界穷举所有情况
#include <iostream> using namespace std; const int N = 40; int pre[N], post[N], in[N]; // 返回合法方案数 int dfs(int pre_l, int pre_r, int post_l, int post_r, string& res) { if (pre_l > pre_r) return 1; // 合法情况 if (pre[pre_l] != post[post_r]) return 0; // 非法情况 int cnt = 0; for (int k = pre_l; k <= pre_r; k++) { string l_res, r_res; int l_cnt = dfs(pre_l + 1, k, post_l, post_l + k - pre_l - 1, l_res); int r_cnt = dfs(k + 1, pre_r, post_l + k - pre_l - 1 + 1, post_r - 1, r_res); if (l_cnt && r_cnt) { cnt += l_cnt * r_cnt; res = l_res + to_string(pre[pre_l]) + " " + r_res; if (cnt > 1) break; } } return cnt; } int main() { int n; cin >> n; for (int i = 0; i < n; i++) cin >> pre[i]; for (int i = 0; i < n; i++) cin >> post[i]; string res; int cnt = dfs(0, n - 1, 0, n - 1, res); if (cnt > 1) puts("No"); else puts("Yes"); cout << res.substr(0, res.size() - 1) << endl; return 0; }
1127. ZigZagging on a Tree
笔记
- 重建二叉树,再层序遍历
- 可在
BFS
中确定二叉树每一层结点的范围,内嵌一层while
即可做到
#include <iostream> #include <algorithm> #include <unordered_map> using namespace std; const int N = 35; unordered_map<int, int> l, r, pos; // 左右孩子 int n, in[N], post[N]; int build(int in_l, int in_r, int post_l, int post_r) { int root = post[post_r]; int k = pos[root]; // 查找root在中序遍历的下标 if (in_l < k) l[root] = build(in_l, k - 1, post_l, post_l + k - 1 - in_l); if (k < in_r) r[root] = build(k + 1, in_r, post_l + k - 1 - in_l + 1, post_r - 1); return root; } string res; int q[N]; void bfs(int root) { int hh = 0, tt = 0; q[0] = root; bool flag = true; while(hh <= tt) { int head = hh, tail = tt; while(hh <= tail) { int u = q[hh++]; if (l.count(u)) q[++tt] = l[u]; if (r.count(u)) q[++tt] = r[u]; } if (flag) reverse(q + head, q + tail + 1); flag = !flag; } for (int i = 0; i < n; i++) res += to_string(q[i]) + ' '; res.pop_back(); } int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n; for (int i = 0; i < n; i++) { cin >> in[i]; pos[in[i]] = i; // 哈希表,加快查找下标 } for (int i = 0; i < n; i++) cin >> post[i]; int root = build(0, n - 1, 0, n - 1); bfs(root); cout << res << endl; return 0; }
1138. Postorder Traversal
笔记
- DFS后序构造二叉树,但不保存左右孩子位置
- 由于只需记录后序遍历首次遍历的结点,故只需判段用来保存的变量是否变更即可
#include <iostream> #include <unordered_map> using namespace std; const int N = 5e4 + 10; unordered_map<int, int> pos; int n, in[N], pre[N]; int post; // 后序遍历第1个结点 void build(int in_l, int in_r, int pre_l, int pre_r) { int root = pre[pre_l]; int k = pos[root]; if (in_l < k) build(in_l, k - 1, pre_l + 1, pre_l + 1 + k - 1 - in_l); if (k < in_r) build(k + 1, in_r, pre_l + 1 + k - 1 - in_l + 1, pre_r); if (!post) post = root; // 后序遍历第1个结点 } int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n; for (int i = 0; i < n; i++) cin >> pre[i]; for (int i = 0; i < n; i++) { cin >> in[i]; pos[in[i]] = i; } build(0, n - 1, 0, n - 1); cout << post << endl; return 0; }
1066. Root of AVL Tree
笔记
- 插入一个新结点可分为三步:① 插入结点; ② 调整位置; ③ 更新高度
- 插入结点的过程与查找二叉树一样
- 调整位置共有4类情况(LL、LR、RR、RL),可通过计算左右孩子的高度差判断。每种情况可通过两个基本步骤左旋和右旋的组合来解决。在把孩子变成根节点前,需要把孩子多余的部分移到原来的根节点上
- 更新高度需要从下往上更新
- 性质:左旋和右旋只会改变平衡二叉树的位置和高度,但中序遍历次序不变
- 代码实现需要修改根节点指向,因此需要用引用型参数
&
#include <iostream> #include <cstring> using namespace std; const int N = 30; int l[N], r[N], w[N], idx; // 左右儿子法实现平衡二叉树 int h[N]; // 树高 int get_balance(int u) { return h[l[u]] - h[r[u]]; // 平衡度 } void update(int u) { h[u] = max(h[l[u]], h[r[u]]) + 1; // 更新结点高度 } void R(int& u) { int p = l[u]; l[u] = r[p]; // 不然左孩子的右孩子不为空,放不了根节点 r[p] = u; update(u); // 从下往上更新 update(p); u = p; // 更新根节点 } void L(int& u) { int p = r[u]; r[u] = l[p]; // 不然左孩子的右孩子不为空,放不了根节点 l[p] = u; update(u); // 从下往上更新 update(p); u = p; // 更新根节点 } void insert(int &u, int v) { if (!u) { u = ++idx; w[u] = v; } else if (v < w[u]) { // 先插入,再调整,最后更新 insert(l[u], v); if (get_balance(u) == 2) { if (get_balance(l[u]) == 1) R(u); else { L(l[u]); R(u); } } } else { insert(r[u], v); if (get_balance(u) == -2) { if (get_balance(r[u]) == -1) L(u); else { R(r[u]); L(u); } } } update(u); } int main() { int n, v; cin >> n; int root = 0; // 从第0个位置开始插入 for (int i = 0; i < n; i++) { cin >> v; insert(root, v); // 插入到平衡二叉树中 } cout << w[root] << endl; return 0; }
1123. Is It a Complete AVL Tree.
笔记
- 首先构造平衡二叉树,然后采用BFS边输出层序遍历,边判断是否是完全二叉树,如果插入结点的位置超过 n n n,则认为不是完全二叉树
#include <iostream> using namespace std; const int N = 25; int n, l[N], r[N], w[N], h[N], idx; int get_balance(int u) { return h[l[u]] - h[r[u]]; } void update(int u) { h[u] = max(h[l[u]], h[r[u]]) + 1; } void R(int& u) { int p = l[u]; l[u] = r[p]; r[p] = u; update(u); update(p); u = p; } void L(int& u) { int p = r[u]; r[u] = l[p]; l[p] = u; update(u); update(p); u = p; } void insert(int& u, int x) { if (!u) { u = ++idx; w[u] = x; } else if (x < w[u]) { insert(l[u], x); if (get_balance(u) == 2) { if (get_balance(l[u]) == 1) R(u); else { L(l[u]); R(u); } } } else { insert(r[u], x); if (get_balance(u) == -2) { if (get_balance(r[u]) == -1) L(u); else { R(r[u]); L(u); } } } update(u); } int q[N], pos[N]; void bfs(int root) { int hh = 0, tt = -1; q[++tt] = root; pos[root] = 1; // 模拟完全二叉树 bool flag = true; // 是否是二叉树 while(hh <= tt) { int u = q[hh++]; if (pos[u] > n) flag = false; // 超出完全二叉树的保存范围 if (l[u]) { q[++tt] = l[u]; pos[l[u]] = 2 * pos[u]; } if (r[u]) { q[++tt] = r[u]; pos[r[u]] = 2 * pos[u] + 1; } } string res; for (int i = 0; i < n; i++) res += to_string(w[q[i]]) + ' '; res.pop_back(); cout << res << endl; if (flag) puts("YES"); else puts("NO"); } int main() { cin >> n; int x, root = 0; for (int i = 0; i < n; i++) { cin >> x; insert(root, x); } bfs(root); }
1135. Is It A Red-Black Tree
笔记
- 实际上只需要检查三个条件
- 根节点为黑(在建树后检查)
- 红结点的孩子都为黑结点(建树中检查)
- 根节点到叶节点的路径具有相同的黑结点个数(建树中检查)
- 建树依据
- 红黑树是一个平衡二叉树,其中序遍历序列就是所有结点的升序排序序列,
- 把前序遍历序列排序后就能得到中序序列,从而有可能根据前序和中序序列构造唯一的二叉树
- 注意
- 红色是负权值,在排序获取中序遍历序列时需要去掉符号再排序
#include <iostream> #include <algorithm> #include <unordered_map> using namespace std; const int N = 35; int n, pre[N], in[N]; unordered_map<int, int> pos; bool flag; int build(int pre_l, int pre_r, int in_l, int in_r, int& sum) { int root = pre[pre_l]; int k = pos[abs(root)]; if (k < in_l || k > in_r) { flag = false; return 0; } int left = 0, right = 0, ls = 0, rs = 0; if (k > in_l) left = build(pre_l + 1, pre_l + 1 + k - 1 - in_l, in_l, k - 1, ls); if (k < in_r) right = build(pre_l + 1 + k - 1 - in_l + 1, pre_r, k + 1, in_r, rs); if (ls != rs) flag = false; // 黑结点个数不等 sum = ls; // 更新sum的值 if (root < 0) { if (left < 0 || right < 0) flag = false; // 红色根节点存在黑结点孩子 } else sum++; // 根节点是黑结点,到叶节点的黑结点个数+1 return root; } int main() { int t; cin >> t; while(t--) { cin >> n; for (int i = 0; i < n; i++) { cin >> pre[i]; in[i] = abs(pre[i]); } sort(in, in + n); // for (int i = 0; i < n; i++) cout << pre[i] << endl; for (int i = 0; i < n; i++) pos[in[i]] = i; int sum = 0; flag = true; int root = build(0, n - 1, 0, n - 1, sum); if (root < 0) flag = false; if (flag) puts("Yes"); else puts("No"); pos.clear(); } return 0; }
1053. Path of Equal Weight
笔记
- 可采用邻接矩阵存储多叉树
- 可用额外的叶节点数组标记结点是否是叶节点,提高算法效率
- 采用DFS即可找到所有到叶节点的路径
#include <iostream> #include <vector> #include <algorithm> using namespace std; const int N = 110, ROOT = 0; int n, m, target; // 结点个数,非叶子结点个数,目标权值 int w[N]; // 权重 bool g[N][N]; // 邻接矩阵存储多叉树 bool is_leaf[N]; // 是否是叶节点 vector<vector<int>> res; void dfs(int u, int s, vector<int>& path) { if (is_leaf[u] && s == target) res.push_back(path); else { for (int v = 0; v < n; v++) if (g[u][v]) { path.push_back(w[v]); dfs(v, s + w[v], path); path.pop_back(); } } } int main() { cin >> n >> m >> target; for (int i = 0; i < n; i++) cin >> w[i]; while(m--) { int u, k, v; cin >> u >> k; is_leaf[u] = true; // 先标记非叶节点,等会儿再处理 while(k--) { cin >> v; g[u][v] = true; // 构造邻接矩阵 } } for (int i = 0; i < n; i++) is_leaf[i] = !is_leaf[i]; // 变成正确含义 vector<int> path{w[ROOT]}; dfs(ROOT, w[ROOT], path); sort(res.begin(), res.end(), greater<vector<int>>()); for (auto elem : res) { string line; for (int item : elem) line += to_string(item) + ' '; line.pop_back(); cout << line << endl; } return 0; }
1094. The Largest Generation
笔记
- 用BFS找到结点数最多的层
- BFS不仅能用
queue
实现,还可以用vector
实现
#include <iostream> #include <vector> using namespace std; const int N = 110, ROOT = 1; int n, m; // 结点个数,非叶子结点个数 bool g[N][N]; // 邻接矩阵 vector<int> level[N]; void bfs(int root) { int l = 1; level[1].push_back(root); while(level[l].size()) { for (auto u : level[l]) for (int v = 1; v <= n; v++) if (g[u][v]) level[l + 1].push_back(v); l++; } int max_l = 0, max_nodes = 0; for (int i = 1; i < l; i++) if (max_nodes < level[i].size()) { max_l = i; max_nodes = level[i].size(); } cout << max_nodes << ' ' << max_l << endl; } int main() { cin >> n >> m; while(m--) { int u, k, v; cin >> u >> k; while(k--) { cin >> v; g[u][v] = true; } } bfs(ROOT); return 0; }
这篇关于AcWing《PAT甲级辅导课》第5章 树的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享