java实现求有向图的强连通分量

求解方法:

  1. 求出该图的转置(所有边反向)
  2. 求出转置图的拓扑排序(如何求拓扑排序看出可以看我上一篇博文。文章链接)
  3. 根据拓扑排序的顶点顺序使用深度优先算法进行图搜索,一次搜索到的新的顶点的集合(上次遍历过的顶点不算)为一个强连通分量。

代码:

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Scanner;

/*
求解强连通分量
 */
public class StronglyConnected {
    static int time=0;
    static Node[] nodes;    //原图
    static Node[] nodes_rev; //转置图
    static ArrayList<Integer> topology; // 保存转置图的拓扑顺序的倒序
    public static void main(String[] args){
        /**
         * 输入方式:
         * 第一行输入节点的个数n
         * 后面n行输入第n个节点(从0开始数)链接的子节点,没有子节点则直接换行
         */
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();
        nodes = new Node[n];
        nodes_rev = new Node[n];
        topology = new ArrayList<>();
        for(int i = 0; i < n; ++i){
            nodes[i] = new Node();
        }

        input.nextLine();

        for(int i = 0; i < n; ++i){
            String line = input.nextLine();
            if(!line.equals("")){
                String[] tempIntStr = line.split(" ");
                for(int j = 0; j < tempIntStr.length; ++j){
                    nodes[i].linkNodes.add(Integer.parseInt(tempIntStr[j]));
                }
            }
        }


        // 得到转置nodes_rev
        revesal();

        // 得到nodes_rev的拓扑排序
        for(int i=0;i<nodes_rev.length;i++){
            dfs_topoloy(nodes_rev[i], i);
        }

        // 根据得到的拓扑顺序进行dfs,每次dfs访问的新节点就是最大连通分量
        HashSet<Integer> set = new HashSet<>(); // 保存已经得到的连通分量的顶点
        int count = 1;
        for(int i=topology.size()-1;i>=0;i--){ // 按拓扑结构的正序遍历
            dfs(nodes[topology.get(i)]);
            String str = "";
            for(int j=0;j<nodes.length;j++){
                if(nodes[j].visited == 1 && !set.contains(j)){
                    set.add(j);
                    str += j+" ";
                }
            }
            if(str.length()!=0){
                System.out.println("强连通分量"+count+": ");
                System.out.print(str);
                count ++;
                System.out.println();
            }

        }

    }
    // 转置有向无环图
    static void revesal(){
        for(int i=0;i<nodes.length;i++){
            for(int j : nodes[i].linkNodes){
                if(nodes_rev[j] == null)
                    nodes_rev[j] = new Node();
                nodes_rev[j].linkNodes.add(i);
            }
        }
    }

    // 求拓扑排序
    static void dfs_topoloy(Node node,int index){
        if(node.visited==1 || node==null)
            return;
        time ++;
        node.d = time;
        node.visited = 1;
        for(int i : node.linkNodes){
            Node current = nodes_rev[i];
            if(current.visited == 0)
                dfs_topoloy(current, i);
        }
        topology.add(index);
        time ++;
        node.f = time;
    }

    // 第二次dfs求连通分量
    static void dfs(Node node){
        if(node.visited==1 || node==null)
            return;
        node.visited = 1;
        for(int i : node.linkNodes){
            Node current = nodes[i];
            if(current.visited == 0)
                dfs(current);
        }
    }

}

结果:
以算法导论22.5节强连通分量的图(a)为例:
java实现求有向图的强连通分量_第1张图片
输入:
java实现求有向图的强连通分量_第2张图片
输出:
java实现求有向图的强连通分量_第3张图片

你可能感兴趣的:(dfs,算法,java,图搜索算法)