数叶子结点
由于给出的是非叶子结点和它对应的子节点,所以采用邻接表的形式来建树
判断叶子结点:h[u] == -1
#include <time.h>#include <iostream>#include <cstring>using namespace std;const int N = 110;int h[N], e[N], ne[N], idx;int cnt[N], max_depth;void add(int a, int b) {e[idx] = b;ne[idx] = h[a];h[a] = idx++;}void dfs(int u, int depth) {if (h[u] == -1) {cnt[depth]++;max_depth = max(max_depth, depth);return;}for (int i = h[u]; i != -1; i = ne[i]) {dfs(e[i], depth + 1);}}int main() {#ifdef SUBMITfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);long _begin_time = clock();#endifmemset(h, -1, sizeof h);int n, m;cin >> n >> m;while (m--) {int fa, k;cin >> fa >> k;while (k--) {int id;cin >> id;add(fa, id);}}dfs(1, 0);cout << cnt[0];for (int i = 1; i <= max_depth; i++)cout << ' ' << cnt[i];cout << endl;#ifdef SUBMITlong _end_time = clock();printf("\n\ntime = %ld ms", _end_time - _begin_time);#endifreturn 0;}
树的遍历
模板:由后序/前序遍历+中序遍历得到层序遍历
#define SUBMIT#include <time.h>#include <iostream>#include <unordered_map>#include <queue>using namespace std;const int N = 40;int post[N], in[N];unordered_map<int, int> l, r, pos;queue<int> q;int build(int il, int ir, int pl, int pr) {int root = post[pr];int k = pos[root];if (il < k) {l[root] = build(il, k - 1, pl, pl + k - 1 - il);}if (ir > k) {r[root] = build(k + 1, ir, pl + k - il, pr - 1);}return root;}void bfs(int root) {q.push(root);while (!q.empty()) {int t = q.front();q.pop();if (l.count(t)) q.push(l[t]);if (r.count(t)) q.push(r[t]);cout << t;if (!q.empty()) cout << ' ';}}int main() {#ifdef SUBMITfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);long _begin_time = clock();#endifint n;cin >> n;for (int i = 0; i < n; i++) cin >> post[i];for (int i = 0; i < n; i++) {cin >> in[i];pos[in[i]] = i;}int root = build(0, n - 1, 0, n - 1);bfs(root);#ifdef SUBMITlong _end_time = clock();printf("\n\ntime = %ld ms", _end_time - _begin_time);#endifreturn 0;}
最深的根
一个无环连通图可以被视作一个树,注意是图中是无向边
判断是不是连通图可以看连通分量的个数是否为1,采用并查集
遍历时需要避开父结点,这题不好直接用h[u] == -1来判断根节点,因为相邻的结点至少有父节点
#include <time.h>#include <iostream>#include <cstring>#include <vector>using namespace std;const int N = 1e4 + 10, M = N * 2;int p[N];int h[N], e[M], ne[M], idx;void add(int a, int b) {e[idx] = b;ne[idx] = h[a];h[a] = idx++;}int find(int x) {if (x != p[x]) p[x] = find(p[x]);return p[x];}int dfs(int u, int pa) {int ans = 0;for (int i = h[u]; i != -1; i = ne[i]) {if (e[i] != pa) {ans = max(ans, dfs(e[i], u) + 1);}}return ans;}int main() {#ifdef SUBMITfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);long _begin_time = clock();#endifmemset(h, -1, sizeof h);int n;cin >> n;int k = n;for (int i = 1; i <= n; i++) p[i] = i;for (int i = 0; i < n; i++) {int a, b;scanf("%d%d", &a, &b);add(a, b), add(b, a);int fa = find(a), fb = find(b);if (fa != fb) {p[fa] = fb;k--;}}if (k > 1) {printf("Error: %d components", k);}else {vector<int> ans;int max_depth = -1;for (int i = 1; i <= n; i++) {int depth = dfs(i, -1);if (depth > max_depth) {max_depth = depth;ans.clear();ans.push_back(i);}else if (depth == max_depth) {ans.push_back(i);}}for (auto r: ans) {printf("%d\n", r);}}#ifdef SUBMITlong _end_time = clock();printf("\n\ntime = %ld ms", _end_time - _begin_time);#endifreturn 0;}
判断二叉搜索树
整数序列从小到大排序的结果就是二叉搜索树中序遍历
判断它是否可能是某个二叉搜索树或其镜像进行前序遍历的结果,就是看能否构建出这样一棵树
若它的右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值,提示我们根节点是第一个遍历到的结点,对于镜像,遍历的方向要反过来
#include <time.h>#include <iostream>#include <algorithm>using namespace std;const int N = 1010;int pre[N], in[N], post[N];int cnt;bool build(int il, int ir, int pl, int pr, int type) {int root = pre[pl];int k;if (type == 0) {for (k = il; k <= ir; k++) {if (in[k] == root) {break;}}if (k > ir) return false;}else {for (k = ir; k >= il; k--) {if (in[k] == root) {break;}}if (k < il) return false;}bool flag = true;if (il < k) {flag &= build(il, k - 1, pl + 1, pl + k - il, type);}if (ir > k) {flag &= build(k + 1, ir, pl + k - il + 1, pr, type);}post[cnt++] = root;return flag;}void printpost(int n) {cout << post[0];for (int i = 1; i < n; i++) {cout << ' ' << post[i];}cout << endl;}int main() {#ifdef SUBMITfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);long _begin_time = clock();#endifint n;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, 0)) {cout << "YES" << endl;printpost(n);}else {reverse(in, in + n);cnt = 0;if (build(0, n - 1, 0, n - 1, 1)) {cout << "YES" << endl;printpost(n);}else cout << "NO" << endl;}#ifdef SUBMITlong _end_time = clock();printf("\n\ntime = %ld ms", _end_time - _begin_time);#endifreturn 0;}
完全二叉搜索树
已知中序遍历,由于是完全二叉树,所以可以唯一确定,进行中序遍历填充树即可
完全二叉树的数组表示就是层序遍历
#include <time.h>#include <iostream>#include <algorithm>using namespace std;const int N = 1010;int a[N], t[N];int cnt, n;void dfs(int u) {if (u * 2 <= n) dfs(u * 2);t[u] = a[cnt++];if (u * 2 + 1 <= n) dfs(u * 2 + 1);}int main() {#ifdef SUBMITfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);long _begin_time = clock();#endifcin >> n;for (int i = 0; i < n; i++) cin >> a[i];sort(a, a + n);dfs(1);cout << t[1];for (int i = 2; i <= n; i++) cout << ' ' << t[i];cout << endl;#ifdef SUBMITlong _end_time = clock();printf("\n\ntime = %ld ms", _end_time - _begin_time);#endifreturn 0;}
再次树遍历
使用栈可以以非递归方式实现二叉树的中序遍历
push前面如果也是push,那么该结点就是上面一个结点的左儿子,不然是右儿子
两个儿子表示法即可
#define SUBMIT#include <time.h>#include <iostream>#include <stack>using namespace std;const int N = 40;stack<int> s;int l[N], r[N];void dfs(int u, int root) {if (!u) return;dfs(l[u], root);dfs(r[u], root);cout << u;if (u != root) cout << ' ';}int main() {#ifdef SUBMITfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);long _begin_time = clock();#endifint n;cin >> n;int root;int last = -1;int type;for (int i = 0; i < 2 * n; i++) {string op;cin >> op;if (op == "Push") {int x;cin >> x;if (last == -1) {root = x;}else if (type == 0) {l[last] = x;}else {r[last] = x;}type = 0;last = x;s.push(x);}else {int t = s.top();s.pop();last = t;type = 1;}}dfs(root, root);cout << endl;#ifdef SUBMITlong _end_time = clock();printf("\n\ntime = %ld ms", _end_time - _begin_time);#endifreturn 0;}
完全二叉树
判断完全二叉树的话,考虑数组表示法,下标从1开始计数,这样比较最大下标与结点数目之间的关系,完全二叉树应该是正好n个
#include <time.h>#include <iostream>#include <cstring>using namespace std;const int N = 30;int l[N], r[N];int has_fa[N];int maxk, maxid;void dfs(int u, int k) {if (u == -1) return;if (k > maxk) {maxk = k;maxid = u;}dfs(l[u], k * 2);dfs(r[u], k * 2 + 1);}int main() {#ifdef SUBMITfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);long _begin_time = clock();#endifint n;cin >> n;memset(l, -1, sizeof l);memset(r, -1, sizeof r);for (int i = 0; i < n; i++) {string s1, s2;cin >> s1 >> s2;if (s1 != "-") {l[i] = stoi(s1);has_fa[l[i]] = true;}if (s2 != "-") {r[i] = stoi(s2);has_fa[r[i]] = true;}}int root = -1;while (has_fa[++root]);dfs(root, 1);if (maxk == n) {cout << "YES" << ' ' << maxid << endl;}else {cout << "NO" << ' ' << root << endl;}#ifdef SUBMITlong _end_time = clock();printf("\n\ntime = %ld ms", _end_time - _begin_time);#endifreturn 0;}
二叉搜索树最后两层结点数量
构建二叉搜索树模板
dfs获取最大深度,记录每个深度对应的结点数目
#include <time.h>#include <iostream>using namespace std;const int N = 1010;int l[N], r[N], idx, w[N];int max_depth, num[N];void insert(int &root, int v) { // root可变if (!root) {root = ++idx;w[root] = v;return;}if (v <= w[root]) insert(l[root], v);else insert(r[root], v);}void dfs(int u, int depth) {if (!u) return;num[depth]++;max_depth = max(max_depth, depth);dfs(l[u], depth + 1);dfs(r[u], depth + 1);}int main() {#ifdef SUBMITfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);long _begin_time = clock();#endifint n;cin >> n;int root = 0;for (int i = 0; i < n; i++) {int x;cin >> x;insert(root, x);}dfs(root, 0);int n1 = num[max_depth], n2 = num[max_depth - 1];printf("%d + %d = %d\n", n1, n2, n1 + n2);#ifdef SUBMITlong _end_time = clock();printf("\n\ntime = %ld ms", _end_time - _begin_time);#endifreturn 0;}
前序和后序遍历
如果只通过前序遍历和后序遍历,则有可能无法确定唯一二叉树
那么枚举前序遍历当中左子树的边界就好了,注意可以没有左子树
中序遍历如果需要右子树来判断合法性,此时使用字符串拼接是个好办法,先暂存左右子树的结果
#include <time.h>#include <iostream>using namespace std;const int N = 40;int pre[N], post[N];int dfs(int l1, int r1, int l2, int r2, string &s) {if (l1 > r1) return 1;if (pre[l1] != post[r2]) return 0;int root = pre[l1];int cnt = 0;for (int i = l1; i <= r1; i++) { // 可以没有左子树string l, r;int lcnt = dfs(l1 + 1, i, l2, l2 + i - l1 - 1, l);int rcnt = dfs(i + 1, r1, l2 + i - l1, r2 - 1, r);if (lcnt && rcnt) {s = l + to_string(root) + ' ' + r; // 中序遍历cnt += lcnt * rcnt;}}return cnt;}int main() {#ifdef SUBMITfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);long _begin_time = clock();#endifint n;cin >> n;for (int i = 0; i < n; i++) cin >> pre[i];for (int i = 0; i < n; i++) cin >> post[i];string ans;int cnt = dfs(0, n - 1, 0, n - 1, ans);if (cnt > 1) {puts("No");}else {puts("Yes");}ans.pop_back();cout << ans << endl;#ifdef SUBMITlong _end_time = clock();printf("\n\ntime = %ld ms", _end_time - _begin_time);#endifreturn 0;}
AVL树的根
模板
#include <time.h>#include <iostream>using namespace std;const int N = 30;int l[N], r[N], h[N], v[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 L(int &u) {int p = r[u];r[u] = l[p], l[p] = u;update(u), update(p);u = p;}void R(int &u) {int p = l[u];l[u] = r[p], r[p] = u;update(u), update(p);u = p;}void insert(int &u, int w) {if (!u) u = ++idx, v[u] = w;else if (w < v[u]) {insert(l[u], w);if (get_balance(u) == 2) {if (get_balance(l[u]) == 1) R(u);else L(l[u]), R(u);}}else {insert(r[u], w);if (get_balance(u) == -2) {if (get_balance(r[u]) == -1) L(u);else R(r[u]), L(u);}}update(u);}int main() {#ifdef SUBMITfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);long _begin_time = clock();#endifint n;cin >> n;int root = 0;while (n--) {int w;cin >> w;insert(root, w);}cout << v[root] << endl;#ifdef SUBMITlong _end_time = clock();printf("\n\ntime = %ld ms", _end_time - _begin_time);#endifreturn 0;}
判断完全 AVL 树
把判断的过程放到层序遍历当中去
#include <time.h>#include <iostream>using namespace std;const int N = 30;int l[N], r[N], h[N], w[N], idx;int pos[N * 2];int q[N], hh, tt = -1;int 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 L(int &u) {int p = r[u];r[u] = l[p], l[p] = u;update(u), update(p);u = p;}void R(int &u) {int p = l[u];l[u] = r[p], r[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);}bool bfs(int root) {q[++tt] = root;pos[root] = 1;bool flag = true;while (hh <= tt) {int t = q[hh++];int p = pos[t];if (p > n) flag = false;if (l[t]) {pos[l[t]] = p * 2;q[++tt] = l[t];}if (r[t]) {pos[r[t]] = p * 2 + 1;q[++tt] = r[t];}}return flag;}int main() {#ifdef SUBMITfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);long _begin_time = clock();#endifcin >> n;int root = 0;for (int i = 0; i < n; i++) {int v;cin >> v;insert(root, v);}bool ans = bfs(root);cout << w[q[0]];for (int i = 1; i < n; i++) cout << ' ' << w[q[i]];cout << endl;if (ans) puts("YES");else puts("NO");#ifdef SUBMITlong _end_time = clock();printf("\n\ntime = %ld ms", _end_time - _begin_time);#endifreturn 0;}
判断红黑树
和红黑树关系不大,在建树的过程中递归完成判断即可
注意有可能无法构建出二叉搜索树
权值因为红色结点会变成负数,因此排序的时候要用正数来排
#include <time.h>#include <iostream>#include <unordered_map>#include <algorithm>using namespace std;const int N = 40;int in[N], pre[N];unordered_map<int, int> pos;bool ans;int build(int il, int ir, int pl, int pr, int &sum) {int root = pre[pl];int k = pos[abs(root)]; // 用正数判断if (k < il || k > ir) { // 可能不存在ans = false;return 0;}int left = 0, right = 0, ls = 0, rs = 0;if (il < k) {left = build(il, k - 1, pl + 1, pl + k - il, ls);}if (k < ir) {right = build(k + 1, ir, pl + k - il + 1, pr, rs);}if (ls != rs) ans = false;sum = ls;if (root < 0) {if (left < 0 || right < 0) {ans = false;}}else sum++;return root;}int main() {#ifdef SUBMITfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);long _begin_time = clock();#endifint k;cin >> k;while (k--) {int n;cin >> n;for (int i = 0; i < n; i++) {cin >> pre[i];in[i] = abs(pre[i]);}sort(in, in + n);pos.clear();for (int i = 0; i < n; i++) pos[in[i]] = i;ans = true;int sum = 0;int root = build(0, n - 1, 0, n - 1, sum);if (root < 0) ans = false;if (ans) puts("Yes");else puts("No");}#ifdef SUBMITlong _end_time = clock();printf("\n\ntime = %ld ms", _end_time - _begin_time);#endifreturn 0;}
中缀表达式
如果结点不是叶子,那么需要加上左右括号
#define SUBMIT#include <time.h>#include <iostream>#include <cstring>using namespace std;const int N = 30;int l[N], r[N], has_fa[N];bool leaf[N];string v[N];string dfs(int u) {string ls, rs;if (l[u] != -1) {ls = dfs(l[u]);if (!leaf[l[u]]) ls = '(' + ls + ')';}if (r[u] != -1) {rs = dfs(r[u]);if (!leaf[r[u]]) rs = '(' + rs + ')';}return ls + v[u] + rs;}int main() {#ifdef SUBMITfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);long _begin_time = clock();#endifmemset(l, -1, sizeof l);memset(r, -1, sizeof r);int n;cin >> n;for (int i = 1; i <= n; i++) {cin >> v[i] >> l[i] >> r[i];if (l[i] == -1 && r[i] == -1) leaf[i] = true;if (l[i] != -1) has_fa[l[i]] = true;if (r[i] != -1) has_fa[r[i]] = true;}int root = 0;while (has_fa[++root]);cout << dfs(root);#ifdef SUBMITlong _end_time = clock();printf("\n\ntime = %ld ms", _end_time - _begin_time);#endifreturn 0;}
