目录
树和图的存储
图分为有向图和无向图
无向图可以看作特殊的有向图,及无向图中一条边可以看作方向相反的两条有向边
树是特殊的图(这些都是离散数学里面的知识)
1.邻接矩阵
作用:用于存储稠密图
缺点:不能存储重边,空间浪费较大
如何存储:就是一个二维数组
如果没有权重 g[a][b] 就是bool类型,结果表示,a和b中间是否存在边
有权重的话,g[a][b]的 值就是权重
2.邻接表
作用:用于存储稀疏图
如何存储:用数组模拟链表来存储
有几个点就开几个链表,每个链表中存储头节点可以到达的点
const int N=10010;
int h[N],e[N],ne[N],idx;
h[] :头节点数组,h[1]是一个链表 ,h[2]是一个链表........
e[]: 存储值,也就其是下标所能到达的点
ne[]:存储其下一个节点
idx:当前指针
画个图
邻接表存数据
const int N=1002;
int h[N],e[N],ne[N],idx;
void add(int a,int b)
{
memset(h,-1,sizeof h);\\头节点初始化为-1
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
题目:树的重心
给定一颗树,树中包含 nn 个结点(编号 1∼n1∼n)和 n−1n−1 条无向边。
请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。
重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。
输入格式
第一行包含整数 nn,表示树的结点数。
接下来 n−1n−1 行,每行包含两个整数 aa 和 bb,表示点 aa 和点 bb 之间存在一条边。
输出格式
输出一个整数 mm,表示将重心删除后,剩余各个连通块中点数的最大值。
数据范围
1≤n≤1051≤n≤105
输入样例
9
1 2
1 7
1 4
2 8
2 5
4 3
3 9
4 6
输出样例:
4
思路:依次求出删除每个节点时最大连通块中点的数量, 求出最小的最大连通块。
问题于是变成了如何求出删除某个节点时的最大连通块
由于树的性质:对于这个删除的点求其子树大小,dfs可以求出。如果其不是根节点那么,包含根节点的连通块的大小等于总的点的数量减去这个点的所有子树大小之和,再减去1(删去的这个点)
下面是普通的深搜代码
u表示以这个节点为根节点进行遍历(由于树的性质,它只会向下走)
int dfs(int u){
st[u]=true;
for(int i=h[u];i!=-1;i=ne[i])
{
j=e[i];
if(!st[j]) dfs(j);
}
}
在深搜的过程中获得其连通块的大小
int dfs(int u){
st[u]=true;
sum=1;res=0;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(!st[j]) {
int s=dfs(j);
res=max(s,res);// 求子节点最大连通块
sum+=s; //以u为节点的连通块之和
}
}
res=max(res,n-sum);
ans=min(res,ans);
return sum;
}
代码1
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
const int M=2*N;
int h[N],e[M],ne[M],idx,n;
bool st[N];
int sum, res,ans=N;
void add(int a,int b){//存表
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
int dfs(int u){
st[u]=true;
sum=1;res=0;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(!st[j]) {
int s=dfs(j);
res=max(s,res);// 求子节点最大连通块
sum+=s; //以u为节点的连通块之和
}
}
res=max(res,n-sum);
ans=min(res,ans);
return sum;
}
int main()
{
memset(h,-1,sizeof h);
cin>>n;
for(int i=0;i<n-1;i++){
int a,b;
cin>>a>>b;
add(a,b),add(b,a);
}
dfs(1);
cout<<ans;
return 0;
}
这里有两个小bug,就是sum 和res 不能在最外面定义,需要在dfs函数里面赋值的那里定义才不会出错。我靠,我现在都不知道为啥
求大佬讲解以下
代码2
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
public class Main {
static int ans; //最终答案
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
ans = n + 1;
Object[] tree = new Object[n + 1];
boolean[] state = new boolean[n + 1];
//构造树
for (int i = 0; i < n - 1; i++) {
int a = scanner.nextInt(), b = scanner.nextInt();
if (tree[a] == null) {
List<Integer> list = new LinkedList<>();
list.add(b);
tree[a] = list;
} else {
List<Integer> list = (List<Integer>) tree[a];
list.add(b);
}
//边是无向的,所以需要添加两条边
if (tree[b] == null) {
List<Integer> list = new LinkedList<>();
list.add(a);
tree[b] = list;
} else {
List<Integer> list = (List<Integer>) tree[b];
list.add(a);
}
}
dfs(1, n, tree, state);
System.out.println(ans);
scanner.close();
}
// 返回以u为根的子树的大小
private static int dfs(int u, int n, Object[] tree, boolean[] state) {
state[u] = true;
List<Integer> list = (List<Integer>) tree[u];
if (list == null)
return 1;
int cont = 1; // 统计当前节点子树所有的节点
int res = 0;// 统计最大连通块大小
for (int i : list) {
if (!state[i]) {
int child = dfs(i, n, tree, state); //一颗子树的结点数目
res = Math.max(res, child); //保证res中存的是最大的那颗子树结点数目
cont += child;
}
}
res = Math.max(res, n - cont);
ans = Math.min(ans, res);
return cont;
}
}