强连通分治算法-Kosaraju算法

该算法分为三步:
    1. DFS算法计算完成时间。DFS算法可以计算出每个节点的发现时间与完成时间。
    2. 计算逆图,所谓逆图,就是将边进行反向后产生的图。
    3. 在逆图上继续DFS,因为前面正向了,如果反向还能访问,那无疑是一个强连接组件。
       DFS以最大访问时间的节点开始,如此DFS找出所有的强连接组件。
 
package com.lpl.graph.korasaju;

import java.util.*;

/**
 * @author lpl
 * @date 2022-10-17 14:31
 */

/* 该算法分为三步:
    1. DFS算法计算完成时间。DFS算法可以计算出每个节点的发现时间与完成时间。
    2. 计算逆图,所谓逆图,就是将边进行反向后产生的图。
    3. 在逆图上继续DFS,因为前面正向了,如果反向还能访问,那无疑是一个强连接组件。
       DFS以最大访问时间的节点开始,如此DFS找出所有的强连接组件。
    */
public class KorasajuTest {
    public static void main(String[] args) {
        HashMap<String, LinkedList<String>> map = new HashMap<>();
        map.put("A", setAdjacencyList("F"));
        map.put("B", setAdjacencyList("A", "C", "G"));
        map.put("C", setAdjacencyList("D", "G"));
        map.put("D", setAdjacencyList("I"));
        map.put("E", setAdjacencyList("D", "J"));
        map.put("F", setAdjacencyList("B"));
        map.put("G", setAdjacencyList("H", "K", "L"));
        map.put("H", setAdjacencyList("D", "I", "L"));
        map.put("I", setAdjacencyList("C", "M"));
        map.put("J", setAdjacencyList("E", "I"));
        map.put("K", setAdjacencyList("L"));
        map.put("L", setAdjacencyList("M"));
        map.put("M", setAdjacencyList("L"));

//        System.out.println(map.size());
        Graph graph = new Graph();
        graph.setMap(map);

        System.out.println("原图的邻接链表形式:");
        print(graph.getMap());

        System.out.print("dfs结果: ");
        graph.dfs();

        System.out.println("\n各顶点的搜索时间和完成时间: ");
        graph.showDiscoveredTimeAndCompletedTime();


        HashMap<String, LinkedList<String>> inverseGraph = getInverseGraph(map);
        graph.setMap(inverseGraph);
        System.out.println("\n逆图的邻接链表形式:");
        print(graph.getMap());

        //按完成时间从大到小的顺序, 完成逆图的dfs
        ArrayList<String> sortVertex = graph.sortVertex();
        graph.dfs(sortVertex);

    }

    //获得逆图
    public static HashMap<String, LinkedList<String>> getInverseGraph(HashMap<String, LinkedList<String>> map) {
        HashMap<String, LinkedList<String>> reversedMap = new HashMap();
        Set<String> keySet = map.keySet();
        for (String ver : keySet) {
            LinkedList<String> list = map.get(ver); //ver 点的邻接链表
            for (String x : list) {
                if (reversedMap.containsKey(x)) {
                    reversedMap.get(x).addLast(ver);
                    reversedMap.put(x, reversedMap.get(x));
                } else {
                    reversedMap.put(x, setAdjacencyList(ver));
                }
            }
        }
        return reversedMap;
    }

    //设置某个顶点的邻接链表
    public static LinkedList<String> setAdjacencyList(String... vertexes) {
        LinkedList<String> list = new LinkedList<>();
        for (int i = 0; i < vertexes.length; i++) {
            list.add(vertexes[i]);
        }
        return list;
    }

    //打印
    public static void print(HashMap map) {
        for (Object obj : map.keySet()) {
            String ver = (String)obj;
            System.out.println(ver + ": " + map.get(ver));
        }
    }
}

class Graph {
    private HashMap<String, LinkedList<String>> map;
    private HashSet<String> isVisited; //记录是否访问
    private HashMap<String, Integer> discoveredTime; //顶点被搜索的时间
    private HashMap<String, Integer> completedTime; //顶点已完成搜索的时间
    private int count; //用于辅助记录上面两个时间

    public void setMap(HashMap<String, LinkedList<String>> map) {
        this.map = map;
    }

    public Graph() {
        this.isVisited = new HashSet<>();
        this.discoveredTime = new HashMap<>();
        this.completedTime = new HashMap<>();
        this.count = 0;
    }

    //处理非全连通情况, 按完成时间从大到小的顺序, 完成逆图的dfs
    public void dfs(ArrayList<String> list) {
        isVisited.clear();
        for (String ver: list) {
            if (!isVisited.contains(ver)) {
                System.out.print("\n**强连通分量 ");
//            dfsBysatck(ver);
                dfsByRecursion(ver);
                System.out.print(" 连通分量**");
            }
        }
    }

    //按完成时间从大到小的顺序, 完成逆图的dfs
    public ArrayList<String> sortVertex() {
        //存储各顶点的存储时间, 后面降序整理
        ArrayList<Integer> vals = new ArrayList<>();
        //集合 completedTime 的 key
        Set<String> keySet = completedTime.keySet();
        //填充 vals
        for (String ver: keySet) {
            vals.add(completedTime.get(ver));
        }
        //降序排列 vals
        vals.sort(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        /* 转换键值对, 用 tmpMap 来接收*/
        HashMap<Integer, String> tmpMap = new HashMap<>();
        for (String ver: keySet) {
            tmpMap.put(completedTime.get(ver), ver);
        }
        //res 用于返回
        ArrayList<String> res = new ArrayList<>();
        //根据排序好的 vals 来填充 res
        for (Integer val: vals) {
            res.add(tmpMap.get(val));
        }

        return res;
    }

    //处理非全连通情况
    public void dfs() {
        isVisited.clear();
        for (String ver: map.keySet()) {
            if (!isVisited.contains(ver)) {
                System.out.print("\n**连通开始 ");
//            dfsBysatck(ver);
                dfsByRecursion(ver);
                System.out.print(" 连通结束**");
            }
        }
    }

    //dfs: 递归
    public void dfsByRecursion(String ver) {
        System.out.print(ver + "->");
        //记录搜索时间
        discoveredTime.put(ver, ++count );
        //标记
        isVisited.add(ver);
        //拿到 ver 的邻接结点集合
        LinkedList<String> list = map.get(ver);
        for (String w : list) {
            if (!isVisited.contains(w)) {
                dfsByRecursion(w);
            }
        }
        //记录完成时间
        completedTime.put(ver, ++count);
    }

    public HashMap<String, LinkedList<String>> getMap() {
        return map;
    }

    public HashSet<String> getIsVisited() {
        return isVisited;
    }

    public void showDiscoveredTimeAndCompletedTime() {
        for (String ver: discoveredTime.keySet()) {
            System.out.println(ver + ": (" + discoveredTime.get(ver) + ", " + completedTime.get(ver) + ")");
        }
    }
}

测试用例: 

 

运行结果: 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值