1.树和图的深度优先搜索
树的重心
给定一棵树,如果删除树中某个结点,剩余各个连通块分量有几种情况呢
1.以它的孩子结点为根所在树的结点个数
2.以它的父亲结点为根所在树的结点个数-以它为根所在树的结点个数
定义两个变量sum用于存储每棵树的结点总数 res用来存储删除该节点后 连通图的最大点数
利用ans来存储所有结点被删除情况下各个连通图最大点数的最小值
import java.util.*;
public class Main{
static int N=100010;
static int M=N*2;
static int[] h=new int[N];
static int[] e=new int[M];
static int[] ne=new int[M];
static boolean[] st=new boolean[N];
static int n,m,idx;
static int ans=N;
public static void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
//深度优先搜索 返回以u为根的结点总数
public static int dfs(int u){
int res=0;//存储删掉某个节点之后,最大的连通子图节点数
int sum=1;//以u为根所在树的结点数量
st[u]=true;
for(int i=h[u];i!=-1;i=ne[i]){
//j是u的孩子结点
int j=e[i];
//如果j未被访问过
if(!st[j]){
int s=dfs(j);
res=Math.max(res,s);//删除j结点后剩余连通图的最大点数
sum+=s;//以j为根的节点总数
}
}
res=Math.max(res,n-sum);//和n-size对比 取较大值
ans=Math.min(ans,res);
return sum;
}
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
for(int i=0;i<N;i++) h[i]=-1;
for(int i=0;i<n-1;i++){
int a=sc.nextInt();
int b=sc.nextInt();
//无边图 各需记录一条边
add(a,b);
add(b,a);
}
dfs(1);
System.out.println(ans);
}
}
2.树和图的宽度优先搜索
开辟一个数组d用来记录遍历过程中各个结点到初始点的距离 最后返回d[n]
数组d还可以用来判别该结点是否求过距离
宽度优先搜索的标志:“最短距离”
题解
import java.util.*;
public class Main{
static int N=100010;
static int n,m,idx,hh,tt;
static int[] q=new int[N];//数组模拟队列
static int[] h=new int[N];
static int[] e=new int[N];
static int[] ne=new int[N];
static int[] d=new int[N];//存储每个结点到起始结点的距离
public static void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
public static int bfs(){
//队列初始化
hh=0;
tt=-1;
d[1]=0;//第1个结点到第1个结点的距离为0
q[++tt]=1;//将结点1入队
while(hh<=tt){
int t=q[hh++];//取出队头元素
for(int i=h[t];i!=-1;i=ne[i]){遍历队头元素所在链表
int j=e[i];
//如果j没被访问过 更新数组d在j的位置的值 将j入队
if(d[j]==-1){
d[j]=d[t]+1;
q[++tt]=j;
}
}
}
return d[n];
}
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
m=sc.nextInt();
//数组初始化
for(int i=1;i<N;i++){
d[i]=-1;
h[i]=-1;
}
while(m-->0){
int a=sc.nextInt();
int b=sc.nextInt();
add(a,b);
}
System.out.println(bfs());
}
}
3.拓扑序列
有向无环图必有拓扑序列
拓扑序列可以用来检测有向图中是否有环
定义一个数组d用来存储所有结点的入度
深度优先搜索用来判断是否是拓扑序列
import java.util.*;
public class Main{
static int N=100010;
static int n,m,idx,hh,tt;
static int[] q=new int[N];
static int[] h=new int[N];
static int[] e=new int[N];
static int[] ne=new int[N];
static int[] d=new int[N];//保存各个点的入度
public static void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
public static boolean bfs(){
//队列初始化
hh=0;
tt=-1;
//遍历数组d 将所有入度为0的点加入队列(拓扑序列)
//如果存在拓扑序列 那么拓扑序列中必有入度为0的结点
for(int i=1;i<=n;i++){
if(d[i]==0){
q[++tt]=i;
}
}
while(hh<=tt){
//取出队头结点开始遍历
int u=q[hh++];
for(int i=h[u];i!=-1;i=ne[i]){
//取出边s 对应的入度减1 如果此时入度为0 则说明有且只有一条边指向该节点 加入队列
int j=e[i];
d[j]--;
if(d[j]==0)
q[++tt]=j;
}
}
return tt==n-1;//如果tt==n-1 则表示存在拓扑序列 否则有环
}
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
m=sc.nextInt();
//初始化
for(int i=0;i<N;i++) h[i]=-1;
while(m-->0){
int a=sc.nextInt();
int b=sc.nextInt();
add(a,b);
//a结点指向b结点 b结点的入度加1
d[b]++;
}
if(bfs()){
for(int i=0;i<n;i++){
System.out.print(q[i]+" ");
}
}else{
System.out.print("-1");
}
}
}