2023年2月2日17:17:53
算法不难理解,实现比较麻烦,sum与res的使用很巧妙,是一个工具写法,可以背下来。
这里的e存储的是边,一个e对应着一个有向边。j = e[i],代表着i这个点到j有条边。由于是树,肯定存在i = e[j];(无相边)
由于是双向的,所以e得开2N,ne也是如此。
在树中:有n个结点,则有n-1条边,由于用图的方式表示树,也要在意每条边的方向,由于每条边两个方向,因此一共最多2*(n-1)条边,方便起见开2N条边。
树∈图,树是特殊的图,无向连通图
图有两种,一种是有向的,一种是无向的,有向图的表示方式可以用来表示无向图,因此用有向图的数据结构也可以表示树。
只要在构建图的时候,a-b和b-a这两个都添加一遍就行了,如果只添加了a-b,会漏掉b-a这种情况,这样算法跑起来有时候会有限制,就如同之前用对象表示树结构一样。
学习静态链表写法对于找工作有什么意义呢?好像没有。。emm
import java.util.*;
/**
* @desc 使用acwing(oj)用的模板
*/
public class Main {
//定义容器、常数、读入
int N = 100010;
Scanner jin = new Scanner(System.in);
int[] e = new int[2*N], ne = new int[2*N], h = new int[N];
int idx = 0;
boolean[] st = new boolean[N];
int n;
int ans;
//oj要用的main方法
public static void main(String[] args) {new Main().run();}//在oj中调用题解方法
void run() {
//读入题目数据
n = jin.nextInt();
ans = N;
for (int i = 0; i < n+1; i++) {
h[i] = -1;
}
// Arrays.fill(h,1,n+1,-1);
for (int i = 0; i < n-1; i++) {//n-1是边的数量,n是结点的数量
int a = jin.nextInt();
int b = jin.nextInt();
head_insert(a,b);
head_insert(b,a);//不能忘记
}
//调用解题方法
dfs(1);
//输出题解答案
System.out.print(ans);
}
int dfs(int u){//返回值是以u为根时,其子节点的最大结点数量
st[u] = true;
//遍历链表
int sum = 1; int res = 0;/*寻找最大,sum是为了求u的总和,res是为了求最大的子树数量*/
for(int i = h[u]; i != -1; i = ne[i]){
int j = e[i];
if(!st[j]){
int s = dfs(j);
res = Math.max(res,s);
sum += s;
}
}
res = Math.max(res, n - sum);//外部比较
ans = Math.min(ans, res);//全局比较
return sum;
}
void head_insert(int a, int b){
e[idx] = b;//创建新结点
ne[idx] = h[a];//新结点指向原来的头结点,成为新的头结点
h[a] = idx;
idx++;
}
}