文章目录
PAT1004叶子结点
树的问题深搜和广搜其实一般都可以,一般深搜会更好写一点,不用开队列
题目输出每层叶子结点数,多以开一个
c
n
t
cnt
cnt数组记录一下每层叶子结点数,邻接表存图提高效率
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110, M = N * 2;
int h[N], e[M], ne[M], idx;//h表示表头 e表示边的指向 ne下一个结点 idx使用的结点编号
int n, m;
int cnt[N], max_depth;
void add(int a, int b) // 添加一条边a->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; i = ne[i])
{
dfs(e[i], depth + 1);
}
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
for (int i = 0; i < m; i ++ )
{
int id, k;
cin >> id >> k;
while (k -- )
{
int x;
cin >> x;
add(id, x);
}
}
dfs(1, 0);
cout << cnt[0];
for (int i = 1; i <= max_depth; i ++ )
{
cout << ' ' << cnt[i];
}
cout << endl;
return 0;
}
PAT1020树的遍历
后序遍历的最后一个是根节点,从中序遍历中找到根节点的下标 k k k,递归根节点左右两个区间建树,最后 B F S BFS BFS从根节点开始遍历输出
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
using namespace std;
const int N = 40;
int n;
int postorder[N], inorder[N];
unordered_map<int, int> l, r, pos;
int q[N];
int dfs(int il, int ir, int pl, int pr)
{
int root = postorder[pr];
int k = pos[root];
if(il < k) l[root] = dfs(il, k - 1, pl, pl - il + k - 1);
if(ir > k) r[root] = dfs(k + 1, ir, pl - il + k, pr - 1);
return root;
}
void bfs(int root)
{
int hh = 0, tt = -1;
q[ ++ tt] = root;
while (hh <= tt)
{
auto t = q[hh ++ ];
if(l.count(t)) q[ ++ tt] = l[t];
if(r.count(t)) q[ ++ tt] = r[t];
}
cout << q[0];
for (int i = 1; i < n; i ++ ) printf(" %d", q[i]);
cout << endl;
}
int main()
{
scanf("%d", &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 = dfs(0, n - 1, 0, n - 1);
bfs(root);
return 0;
}
PAT1021最深的根
以每个点为根,获得深度的最大值
时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)遍历所有结点
O
(
n
)
O(n)
O(n)*求深度
(
O
)
(O)
(O)
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 10010, M = N * 2;
int h[N], e[M], ne[M], idx;
int p[N];
int n;
void add(int a, int b) // 添加一条边a->b
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
int find(int x) // 并查集
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int dfs(int u, int father) // 从u出发最大深度
{
int depth = 0;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if(j == father) continue;
depth = max(depth, dfs(j, u) + 1);
}
return depth;
}
int main()
{
cin >> n;
memset(h, -1, sizeof h);
int k = n;
for (int i = 1; i <= n; i ++ ) p[i] = i;
for (int i = 0; i < n - 1; i ++ )
{
int a, b;
cin >> a >> b;
if(find(a) != find(b))
{
p[find(a)] = find(b);
k --;
}
add(a, b);
add(b, a);
}
if(k > 1) printf("Error: %d components\n", k);
else
{
int max_depth = -1;
vector<int> res;
for (int i = 1; i <= n; i ++ )
{
int depth = dfs(i, -1);
if(max_depth < depth)
{
res.clear();
res.push_back(i);
max_depth = depth;
}
else if(max_depth == depth)
{
res.push_back(i);
}
}
for (auto v : res) cout << v << endl;
}
return 0;
}
PAT1043判断二叉搜索树
树的遍历,通过前序遍历和中序遍历还原后序遍历(先递归左子树,再递归右子树,保存根结点顺序得到后序遍历的值)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int n, cnt;
int inorder[N], preorder[N], postorder[N];
bool dfs(int il, int ir, int pl, int pr, int type)
{
if(il > ir) return true;
int root = preorder[pl];
int k; // 找到中序遍历中root的下标
if(type)
{
for (k = ir; k >= il; k -- )
if(inorder[k] == root)
break;
if(k < il) return false;
}
else
{
for (k = il; k <= ir; k ++ )
if(inorder[k] == root)
break;
if(k > ir) return false;
}
bool flag = true;
if(!(dfs(il, k - 1, pl + 1, pl + 1 + (k - 1 - il), type))) flag = false; // 递归左子树
if(!(dfs(k + 1, ir, pl + k - il + 1, pr, type))) flag = false; // 递归右子树
postorder[cnt ++ ] = root; // 递归左子树和右子树后保存根节点(符合后序遍历的过程)
return flag;
}
int main()
{
cin >> n;
for (int i = 0; i < n; i ++ )
{
cin >> preorder[i];
inorder[i] = preorder[i];
}
sort(inorder, inorder + n);
if(dfs(0, n - 1, 0, n - 1, 0))
{
puts("YES");
cout << postorder[0];
for (int i = 1; i < n; i ++ ) printf(" %d", postorder[i]);
cout << endl;
}
else
{
reverse(inorder, inorder + n);
cnt = 0; // 按照第一种方式改变了cnt的值
if(dfs(0, n - 1, 0, n - 1, 1))
{
puts("YES");
cout << postorder[0];
for (int i = 1; i < n; i ++ ) printf(" %d", postorder[i]);
cout << endl;
}
else puts("NO");
}
return 0;
}
PAT1064完全二叉搜索树
完全二叉树(或者满二叉树)都可以用数组的方式存,BST树中序遍历就是递增的,中序遍历一遍,边遍历边填,就可以构造一个BST树
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1010;
int n;
int f[N], res[N];
void dfs(int u, int &k)
{
if(u * 2 <= n) dfs(u * 2, k);
res[u] = f[k ++ ];
if(u * 2 + 1 <= n) dfs(u * 2 + 1, k);
}
int main()
{
cin >> n;
for (int i = 0; i < n; i ++ )
{
scanf("%d", &f[i]);
}
sort(f, f + n);
int k = 0;
dfs(1, k);
cout << res[1];
for (int i = 2; i <= n; i ++ ) printf(" %d", res[i]);
return 0;
}
PAT1086二叉树的再遍历
找规律,发现二叉树的非递归遍历左子树一路到左(上一步压栈),右子树上一步是弹栈
#include <iostream>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
const int N = 40;
int n;
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()
{
cin >> n;
int root = 0, last, type;
stack<int> s;
for (int i = 0; i < n * 2; i ++ )
{
string op;
cin >> op;
if(op == "Push")
{
int x;
cin >> x;
if(!root) root = x;
else
{
if(!type) l[last] = x;
else r[last] = x;
}
s.push(x);
last = x; // 上一步操作是push
type = 0;
}
else
{
last = s.top();
s.pop();
type = 1;
}
}
dfs(root, root);
return 0;
}
PAT1099构建二叉搜索树
先dfs建BST,再bfs输出
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110;
int w[N], l[N], r[N];
int a[N], q[N] , n;
void bfs()
{
int hh = 0, tt = 0;
q[0] = 0;
while(hh <= tt)
{
int t = q[hh ++ ];
if(l[t] != -1) q[++ tt] = l[t];
if(r[t] != -1) q[++ tt] = r[t];
}
cout << w[q[0]];
for (int i = 1; i < n; i ++ ) printf(" %d", w[q[i]]);
cout << endl;
}
void dfs(int u, int& k)
{
if(u == -1) return;
dfs(l[u], k);
w[u] = a[k ++];
dfs(r[u], k);
}
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i ++ ) scanf("%d %d", &l[i], &r[i]);
for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
sort(a, a + n);
int k = 0;
dfs(0, k);
bfs();
return 0;
}
PAT翻转二叉树
方法一:不翻转
先遍历右子树,再遍历左子树
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 15;
int l[N], r[N];
int q[N], n;
bool is_root[N];
void bfs(int root)
{
int hh = 0, tt = -1;
q[ ++ tt] = root;
while (hh <= tt)
{
auto t = q[hh ++ ];
if(r[t] != -1) q[ ++ tt] = r[t];
if(l[t] != -1) q[ ++ tt] = l[t];
}
cout << q[0];
for (int i = 1; i < n; i ++ ) printf(" %d", q[i]);
cout << endl;
}
void dfs(int u, int& k)
{
if(u == -1) return ;
dfs(r[u], k);
cout << u;
if(++k != n) cout << ' ';
dfs(l[u], k);
}
int main()
{
cin >> n;
for (int i = 0; i < n; i ++ )
{
char lc, rc;
cin >> lc >> rc;
if(lc != '-')
{
l[i] = lc - '0';
is_root[l[i]] = true;
}
else l[i] = -1;
if(rc != '-')
{
r[i] = rc - '0';
is_root[r[i]] = true;
}
else r[i] = -1;
}
int root = 0;
while (is_root[root]) root ++;
bfs(root);
int k = 0;
dfs(root, k);
puts("");
return 0;
}
方法二:先翻转,再正常遍历
先 d f s dfs dfs翻转,再正常遍历
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 15;
int n;
int l[N], r[N];
int q[N];
bool has_father[N];
void dfs_reverse(int u)
{
if (u == -1) return;
dfs_reverse(l[u]);
dfs_reverse(r[u]);
swap(l[u], r[u]);
}
void bfs(int root)
{
int hh = 0, tt = 0;
q[0] = root;
while (hh <= tt)
{
int t = q[hh ++ ];
if (l[t] != -1) q[ ++ tt] = l[t];
if (r[t] != -1) q[ ++ tt] = r[t];
}
cout << q[0];
for (int i = 1; i < n; i ++ ) cout << ' ' << q[i];
cout << endl;
}
void dfs(int u, int& k)
{
if (u == -1) return;
dfs(l[u], k);
cout << u;
if ( ++ k != n) cout << ' ';
dfs(r[u], k);
}
int main()
{
cin >> n;
memset(l, -1, sizeof l);
memset(r, -1, sizeof r);
for (int i = 0; i < n; i ++ )
{
char lc, rc;
cin >> lc >> rc;
if (lc != '-') l[i] = lc - '0', has_father[l[i]] = true;
if (rc != '-') r[i] = rc - '0', has_father[r[i]] = true;
}
int root = 0;
while (has_father[root]) root ++ ;
dfs_reverse(root);
bfs(root);
int k = 0;
dfs(root, k);
return 0;
}