算法学习记录 8.15 树和图的深度优先搜索 树和图的宽度优先搜索

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");
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值