Java 集合扩展系列 | 图框架

1、前言

一直很好奇为什么JDK没有实现图这种数据结构, 而每次当我们需要去使用的图的时候都要重新写一遍领接矩阵、领接表之类 然后再去写算法实现, 实在异常麻烦。

于是便简单对图这种数据结构进行一些封装实现(仅供学习)。具体代码见 github

2、图是如何存储

图的存储主要有两种实现,分别是领接矩阵领接表

2.1、领接矩阵

实际就是用一个二维数组去存储图中节点与节点的关系。 行下标和列下标分别代表一个节点序号,数组的值表示一条边。 比如有下图二维数组 int[][] graph, 则 graph[3][5] 表示 节点3节点5之间存在一条边。 通过这样的方式我们就可以很方便的存储图中的所有边和节点。 在这里插入图片描述

2.2、领接表

实际就是用一个链表数组 或者哈希表的方式去存储。 比如用链表数组 LinkedList<Integer>[] graph 存储, 则 graph[0] 表示与节点0相邻的所有节点. 比如用哈希表, Map<Integer, LinkedList<Integer>> graph , 则的此时的 graph.get(0) 表示与节点0相邻的所有节点.

在这里插入图片描述

3、图库体系

主要实现了4种图的结构, 分别是

  • 领接矩阵-无权图: AdjacencyMatrixGraph
  • 领接矩阵-有权图: AdjacencyMatrixWeightGraph
  • 领接表-无权图: AdjacencyListsGraph
  • 领接表-有权图: AdjacencyListsWeightGraph

类图关系如下 在这里插入图片描述

4、功能

并且在这四种数据结构之上实现了以下基础图论算法:

  • 图的深度优先遍历
  • 图的广度优先遍历
  • 计算图连通分量个数
  • 最小生成树算法
    • Prim算法
    • Kruskal算法
  • 路径算法
    • 有权图单源最短路径算法Dijkstra
    • 无权图单源最短路径算法
    • 寻找从 开始节点 到 结束节点的一条路径
    • 寻找从 开始节点 到 结束节点的所有路径

5、快速开始

5.1、创建图


    /** 有以下图
     *             0
     *           /   \
     *          2    3
     *         /    /  \
     *        v    |    5  ->  1
     *        6    v   ^
     *            4   /
     *
     *           8--> 9
     *           12
     */
    // 创建有向无权-领结表图
    static Graph<String> noWeightDirectListedGraph = new AdjacencyListsGraph<>(true);
    // 创建无向无权-邻接矩阵图
    static Graph<String> noWeightMatrixGraph = new AdjacencyMatrixGraph<>(50,false);

    static {
        noWeightDirectListedGraph.addEdge("x0","x2");
        noWeightDirectListedGraph.addEdge("x2","x6");
        noWeightDirectListedGraph.addEdge("x0","x3");
        noWeightDirectListedGraph.addEdge("x3","x4");
        noWeightDirectListedGraph.addEdge("x3","x5");
        noWeightDirectListedGraph.addEdge("x4","x5");
        noWeightDirectListedGraph.addEdge("x5","x1");
        noWeightDirectListedGraph.addEdge("x8","x9");
        noWeightDirectListedGraph.addEdge("x12","x12");

        noWeightMatrixGraph.addEdge("x0","x2");
        noWeightMatrixGraph.addEdge("x2","x6");
        noWeightMatrixGraph.addEdge("x0","x3");
        noWeightMatrixGraph.addEdge("x3","x4");
        noWeightMatrixGraph.addEdge("x3","x5");
        noWeightMatrixGraph.addEdge("x4","x5");
        noWeightMatrixGraph.addEdge("x8","x9");
        noWeightMatrixGraph.addEdge("x12","x12");

    }
    
	// 创建无向有权-领接矩阵图
    static WeightedGraph<String, Integer> weightListedGraph = new AdjacencyMatrixWeightGraph<>(20,false);
    /*
         0 - 2
          \
           3 -  4 - 6                                       a2-a0(10)
             \ / \                                          a3-a5(10)
          1-  5   7                                         a3-a4(20)
                                                            a0-a3(30)
                                                            a1-a5(40)
                                                            a6-a4(98)
                                                            a7-a4(100)
     */
    static {
        weightListedGraph.addEdge("a0","a2",10);
        weightListedGraph.addEdge("a0","a3",10);
        weightListedGraph.addEdge("a3","a4",20);
        weightListedGraph.addEdge("a3","a5",10);
        weightListedGraph.addEdge("a5","a1",40);
        weightListedGraph.addEdge("a4","a5",50);
        weightListedGraph.addEdge("a4","a5",60);
        weightListedGraph.addEdge("a4","a5",36);
        weightListedGraph.addEdge("a6","a4",98);
        weightListedGraph.addEdge("a4","a7",100);
    }
复制代码

5.2、图深度、广度遍历

    @Test
    public void testTraverse(){
        // 深度遍历 dfs
        noWeightDirectListedGraph.dfsTraverse(e -> System.out.print(e + "->"));
        System.out.println("\n-------");
        // 广度遍历 bfs
        noWeightMatrixGraph.bfsTraverse(e -> System.out.print(e + "->"));
    }
复制代码

5.3、计算联通分量

    int count = noWeightDirectListedGraph.componentCount();
    int count1 = noWeightMatrixGraph.componentCount();
    System.out.println(count);
    System.out.println(count1);
复制代码

5.4、最小生成树算法

    @Test
    public void testGetMinimumSpanningTree(){
        MstTree<String, Integer> minimumSpanningTree = weightListedGraph.getMinimumSpanningTree();
        // Prim算法
        MstTree<String, Integer> minimumSpanningTreePrim = weightListedGraph.getMinimumSpanningTreePrim();
        // Kruskal算法
        MstTree<String, Integer> minimumSpanningTreeKruskal = weightListedGraph.getMinimumSpanningTreeKruskal();
        List<Edge<String,Integer>> edgeList = minimumSpanningTree.getEdgeList();
        for (Edge<String,Integer> e : edgeList){
            System.out.println(e.toString());
        }
    }
复制代码

5.5、无权图求单源最短路径算法

        // 找到所有从开始节点 到 其他节点的最短路径
        String startNoe = "x0";
        Map<String, List<String>> tmp = noWeightDirectListedGraph.findShortPathFromStartNode(startNoe);
        for (Map.Entry<String, List<String>> entry : tmp.entrySet()) {
            String path = entry.getValue().stream().collect(Collectors.joining("->"));
            System.out.println("从" + startNoe + "到节点" + entry.getKey() + "的最短路径为: " + path);
        }
复制代码

5.6、路径算法

  System.out.println("=================2、找到所有从开始节点 到 结束节点的一条路径 ====");
        //  寻找从 x0 到x1 的一条路径。
        List<String> onePath = noWeightDirectListedGraph.findOnePath("x0", "x1");
        System.out.println("寻找从x0到x1 的一条路径为: " + onePath.stream().collect(Collectors.joining("->")));


        System.out.println("=================3、找到所有从开始节点 到 结束节点的所有路径 ====");

        List<List<String>> allPath = noWeightDirectListedGraph.findAllPath("x0", "x1");
        for (List<String> list : allPath) {
            System.out.println("从x0 到x1的路径为: " + list.stream().collect(Collectors.joining("->")));
        }
复制代码

5.7、有权图单源最短路径算法Dijkstra

    @Test
    public void testShortPathTree(){
        ShortPathTree<String, Integer> tmp = weightListedGraph.getShortPathTree("a0", Integer.class);
        for (Map.Entry<String, List<Edge<String, Integer>>> entry : tmp.getFormSourceToEndPathEdgeMap().entrySet()) {
            String path = entry.getValue().stream().map(Edge::toString).collect(Collectors.joining("->"));
            System.out.println("从a0到节点" + entry.getKey() + "的最短路径为: " + path);
        }

        System.out.println();
    }
复制代码

Guess you like

Origin juejin.im/post/7076109638038454303