在计算机科学中,一个图就是一些顶点的集合,这些顶点通过一系列边结对(连接)。顶点用圆圈表示,边就是这些圆圈之间的连线。顶点之间通过边连接。关于图数据结构的介绍可以参照 数据结构-图
Guava-Graph
特性:a)顶点唯一; b)支持有向边和无向边; c)边只能通过两个顶点隐式定义; d)不支持并行边。
示例图如下:
使用对应构建类GraphBuilder来构建Graph实例
MutableGraph<Integer> graph1 = GraphBuilder.directed() //指定为有向图
.nodeOrder(ElementOrder.<Integer>insertion()) //节点按插入顺序输出
//(还可以取值无序unordered()、节点类型的自然顺序natural())
.expectedNodeCount(20) //预期节点数
.allowsSelfLoops(true) //允许自环
.build();
在Builder中并没有包含复杂的构造逻辑,而只是简单设置了几个全局属性而已(如输出所示:是否允许自环、是否有向等);build()接口为最终的构建接口,返回一个MutableGraph接口类型的返回值,此处返回的是其实现子类ConfigurableMutableGraph,内部通过一个ConfigurableMutableValueGraph实例来实现(所有的方法都调用该实例的方法实现)的,因为ValueGraph包含了Graph的全部功能,可以猜测到设计者也因此复用了同一套实现方案(ConfigurableMutableValueGraph)。
增加节点以及连接边
Integer N1 = 1;
Integer N2 = 2;
Integer N3 = 3;
Integer N4 = 4;
//插入边(默认会将节点加入graph中)
graph1.putEdge(N2, N3);
graph1.putEdge(N1, N3);
graph1.putEdge(N1, N2);
graph1.putEdge(N2, N2);
graph1.addNode(N4);
//返回图中所有的节点(顺序依赖nodeOrder)
Set<Integer> nodes = graph1.nodes();
System.out.println("graph1 nodes count:" + nodes.size() + ", nodes value:"
+ nodes);
//返回图中所有的边集合
Set<EndpointPair<Integer>> edges = graph1.edges();
System.out.println("graph1 edge count:" + edges.size() + ", edges value:"
+ edges);
返回结果:
graph1 nodes count:4, nodes value:[2, 3, 1, 4]
graph1 edge count:4, edges value:[<2 -> 2>, <2 -> 3>, <1 -> 2>, <1 -> 3>]
获取节点的前趋列表
Set<Integer> predecessors = graph1.predecessors(N2); //获取N2的前趋
System.out.println("graph1 node:" + N2 + " predecessors:" + predecessors);
输出:
graph1 node:2 predecessors:{1,2}
注:对于允许自环的图allowsSelfLoops(true)中,一条自环边在有向图中既是前趋也是后继,既是入度也是出度。
获取节点的后继列表
graph1.putEdge(N2, N4); //图上面示例图中红色边所示,动态增加了一条边
Set<Integer> successors = graph1.successors(N2); //获取N2的后继
System.out.println("add edge of (" + N2 + "->" + N4 + ") after graph1 node:"
+ N2 + " successors:" + successors);
输出:
add edge of (2->4) after graph1 node:2 successors:[4, 2, 3]
获取节点的邻接点列表(包括前趋和后继)
Set<Integer> adjacents = graph1.adjacentNodes(N2); //获取N2的邻接点
System.out.println("graph1 node: " + N2 + ", adjacents: " + adjacents);
输出:
graph1 node: 2, adjacents: {4,1,2,3}
获取节点的度(入度和出度)
System.out.println("graph1 node: " + N2 + ", degree: " + graph1.degree(N2)
+ ", indegree: " + graph1.inDegree(N2)
+ ", outdegree: " + graph1.outDegree(N2)); //N2的度、入度、出度
输出:
graph1 node: 2, degree: 5, indegree: 2, outdegree: 3 //自环既是入度也是出度
判断顶点连通性(是否有直连边)
final boolean connecting23 = graph1.hasEdgeConnecting(N2, N3); //N2&N3是否连通
final boolean connecting14 = graph1.hasEdgeConnecting(N1, N4); //N1&N4是否连通
System.out.println("graph1 node " + N2 + " & " + N3 + " connecting: " + connecting23
+ ", node " + N1 + " & " + N4 + " connecting: " + connecting14);
输出:
graph1 node 2 & 3 connecting: true, node 1 & 4 connecting: false
转换成不可变graph(Immutable类型)**
ImmutableGraph<Integer> immutableGraph = ImmutableGraph.copyOf(graph1);
nodes = immutableGraph.nodes(); //返回图中所有的节点(顺序依赖nodeOrder)
System.out.println("immutable graph nodes count:" + nodes.size()
+ ", nodes value:" + nodes);
输出:
immutable graph nodes count:4, nodes value:{2,3,1,4} //同被拷贝图顺序
判断是否存在环(第一个顶点和最后一个顶点相同的路径称为环)
final boolean cycle = Graphs.hasCycle(graph1);
System.out.println("graph1 has cycle: " + cycle);
输出:
graph1 has cycle: true //因为N2节点存在一条自环,如果去掉则不存在环
获取节点的可到达列表(获取能访问到的节点结合,不单指直连边)
Set<Integer> reachNodes = Graphs.reachableNodes(graph1, N2); //N2的可达列表
Log.d(TAG, "graph1 node: " + N2 + ", reachNodes: " + format(reachNodes));
输出:
graph1 node: 2, reachNodes: {2,4,3} //N2不存在能访问到N1的边