复习并查集思想_二叉树最大深度问题_几种做法_并查集/递归/递推/最长路径_多种办法实现
Posted kid-yln
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了复习并查集思想_二叉树最大深度问题_几种做法_并查集/递归/递推/最长路径_多种办法实现相关的知识,希望对你有一定的参考价值。
A: 二叉树的最大深度
题目描述
二叉树是指每个结点最多有两个子树的树结构,这两个子树通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。
二叉树的结点层定义为:根结点的层定义为 1 ,根的孩子为第二层结点,依此类推;
二叉树的深度定义为:树中最大的结点层。
给定一颗以1号结点为根的二叉树,求它的最大深度。
输入描述
每组数据包含一颗二叉树,以如下形式给出:
第一行为一个整数 n ( 1 ≤ n ≤ 20 ) ,表示二叉树的结点个数,结点编号为 1~n 。
接下来直到输入数据末尾,每行为两个整数 x 和 y ,表示 x 和 y 之间有一条边,且 y 是 x 的儿子。
输入保证是一颗合法的二叉树。
输出描述
输出一行,表示给定的二叉树的最大深度。
样例输入
3
1 2
2 3
样例输出
3
思路:
这个题一开始想着用模拟来解决,但是后来发现,这样做非常麻烦,因为你不知道怎么表示,链表不是顺序储存结构,访问是不能通过序列编号直接访问的,
而且给定的编号并不一定是规则的从上到下恰好从小到大(之前想用排序做但是WA了),除了第一个节点一定是1号之外 ,没有其他的限制
但是,这题给的数据量是20,也就是说如果我每个结点都往上找到底不管怎样绝对不会超时或者爆炸,最大也就20*20=400,可以接受深搜,可以最长路径解决
除了求图路径的办法,dp递推式,它的输入方法很神奇,x与y,很像并查集,
复习一下,一般并查集:
——并查集——是利用数组下标 / 数组值嵌套—形成递归结构,从而实现
- 检查图中节点是否连通,连通度,求块数 —— 求极大连通子图数—染色
- 合并连通子块,或连通路径长度的,压缩路径——压缩存储空间
- 问询新边加入(根是否一样)是否存在环——是否存在矛盾悖论,判断语句矛盾的问题
- 甚至是求最小/大生成树的问题/特殊的最短路问题—固定起点(根)终点为分支,求路径
- 甚至 前缀和 / 字典树,也利用了类似的思想(这个还没想明白)
比较全的想法整理:https://blog.csdn.net/yjr3426619/article/details/82315133,题集:https://www.cnblogs.com/cyanigence-oi/p/11774190.html
init() find()union()为基础操作;模板如下:
1 int fa[MAXN]; 2 inline void init(int n) { 3 for (int i = 1; i <= n; ++i) 4 fa[i] = i; 5 } 6 7 int find(int x) { 8 if(fa[x] == x) 9 return x; 10 else 11 return find(fa[x]); 12 } 13 14 inline void merge(int i, int j) { 15 fa[find(i)] = find(j); 16 }
如果有权值,则多设一个权值变量数组,合并操作
不过,这题更简单,和一般并查集不一样的地方在于,我们知道这些节点一定是在同1块连通区域内的,而且本题对求路径长度的时间没有太多要求,
因此,并查集中 init() find()union()的操作中,union可以被省略掉,
那么简单利用数组下标和实际值嵌套,(更新路径长的并查集)实现递归,就可以解决了(以下为简易版本)
1 #include <iostream> 2 #include <cstring> 3 #include <queue> 4 #include <cstdio> 5 #include <cmath> 6 #include <map> 7 #include <algorithm> 8 typedef long long ll; 9 using namespace std; 10 int n; 11 int len=0; 12 #define o NULL 13 //最深不会超过n, 14 15 16 int a[25]; 17 int m=0; 18 void f(int i) { 19 if(a[i]==i) { 20 len++; 21 if(len>m)m=len; 22 return; 23 } 24 if(a[i]!=i) { 25 len++; 26 f(a[i]); 27 } 28 29 } 30 31 int main() { 32 cin>>n; 33 int x,y; 34 35 for(int i=1; i<=n; i++) { 36 a[i]=i; 37 } 38 39 while(cin>>x>>y) { 40 a[y]=x; 41 len=0; 42 f(y); 43 //不用a[x]=y; 44 //是因为父亲可以有多个儿子,但是儿子只有一个父亲,需要利用唯一关系 45 } 46 printf("%d ",m); 47 }
复杂版本——带权并查集
1 int find(int x) { 2 if (x == pre[x]) return x; 3 else { 4 int root = find(pre[x]); // 找到根节点 5 sum[x] += sum[pre[x]]; // 权值合并,更新 6 return pre[x] = root; // 压缩路径 7 } 8 } 9 //初始权值都为1,一个节点表示深度=1
这是升级版的———利用并查集求解路径长/ 深度的问题
https://blog.csdn.net/qq_33204081/article/details/77189674
进一步考虑到深度可以迭代实现递推的关系 len【儿子】=len【父亲】+1,更简单的做法:
1 #include <iostream> 2 #include <cstring> 3 #include <queue> 4 #include <cstdio> 5 #include <cmath> 6 #include <map> 7 #include <algorithm> 8 typedef long long ll; 9 using namespace std; 10 int n; 11 int len=0; 12 #define o NULL 13 //最深不会超过n, 14 15 int m=0; 16 int l[25]; 17 18 int main() { 19 cin>>n; 20 int x,y; 21 for(int i=1; i<=n; i++) { 22 l[i]=1; 23 } 24 while(cin>>x>>y) { 25 l[y]=l[x]+1; 26 if(m<l[y])m=l[y]; 27 //不用a[x]=y; 28 //是因为父亲可以有多个儿子,但是儿子只有一个父亲,需要利用唯一关系 29 } 30 printf("%d ",m); 31 }
也可以化成纯图论问题——求最长路径(待续更新)
以上是关于复习并查集思想_二叉树最大深度问题_几种做法_并查集/递归/递推/最长路径_多种办法实现的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ_1821_[JSOI2010]_部落划分_(贪心,并查集)