参考:
lzh的笔记
树与图的存储结构:邻接表
由于树是特殊的图,所以我们存储树也是用的邻接表或邻接图。
邻接表是指对于图上的每个结点来说,用一个单链表存储它的临接点。
我们需要h[],e[],ne[],idx来表示这个邻接点:
- h[v] : 指向节点v形成的链表的头节点注意初始化为-1
- e[i] : 第i个点的值v(表示下标和值的映射)
- ne[i] : 表示邻接表链表中 i 节点的下一个节点
- idx : 当前遍历到的节点的索引,等边都插入完idx就是节点个数
在节点a的后面插入b:
idx为当前遍历到的节点即b,因此e[idx] = b
我们用头插法插入到a的那条邻接表,h[a]为a的邻接表的头节点,所以ne[idx] = h[a]
头节点变成我们新插入的节点b
private static void add(int a, int b){
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
有权重
有权重还需要一个w[i]来存储当前边的权重
- w[i]: 指i的权重
构建邻接表:
其中a->b, c为a到b的权重
void add(int a, int b, int c) {
//构建邻接表
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx++; //h[a] 一直指向a邻接表头插法起点,其实是最后一个,指针保留的方式也是向前
}
1. idx一直向前,如果a是第一次出现,则h[a]的值对应ne中位置即是起点。
2. 插入的方式是类似头插法,每次邻接表中的新元素出现,则插入邻接链表的第一个。也可以这样理解,是每次插到最后,让h[a]指向最后一个元素,遍历的时候倒着向前遍历。
3. 如果指向下一个为空时,指针值为-1.
DFS
DFS模板
用邻接表存储:h[],e[],ne[],idx
既然用邻接表存了,要注意如何判断和一个节点u相邻的另一个节点j ,哪一个是父节点。我们用st数组表示一个节点有没有被遍历过,由于从根节点开始遍历,所以父节点一定先于子节点访问,所以只有判断visted记录j有没有访问过就可以了。一定有且只有一个访问过的j,它是u的父节点。
private static int dfs(int u){
// 记录u被遍历过了
visited[u] = true;
for(int i=h[u];i!=-1;i=ne[i]){
int j = e[i];
// 跳过父节点
if(!visited[j]){
dfs(j);
}
}
模板题:AcWing 846. 树的重心
【题目描述】
重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。
【输入】
9
1 2
1 7
1 4
2 8
2 5
4 3
3 9
4 6
【输出】
4
【思路】
首先明白题目说的意思,把这个点删除后 剩余各个连通块中点数的最大值最小;也就是剩余联通块的节点数最多的值
如下图 把中间的蓝色节点删除后,剩下的连通块
- 根节点的连通块,节点数为3
- 剩下的三个子节点,节点数分别为1
因此最大值是3
我们要找的是遍历把每个节点都删除,找到剩下的联通块的最大值,然后再min
要求删除某个节点后 剩余节点的连通块个数: - dfs每个节点
- 求其每个子节点的连通块大小,找到最大值
- 用总节点个数减去当前节点的联通块大小 得到根节点的连通块
- 将1和2取最大值,得到当前节点各个连通块中点数的最大值
- min(ans,上一步求的值)
import java.util