概念:拓扑排序概念
原文:简短介绍及原始代码
本文对比原文,对其代码增加了更多注释。
package com.qf.greaph;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
public class TopoSortB {
/**
* @author Administrator
* 拓扑排序的节点类
*/
public static class Node{
public Object val;
public int PaIn = 0; //由于是拓扑排序, 所以是入度数量
public Node(Object val) {
this.val = val;
}
}
/**
* @author Administrator
* 怎么表示一个图, 这里给出了另外一个解法。
* 1.用set 容器 来 记录 顶顶点集
* 2.边的记录用 Map + set 来,Map 的 key 用来指向 顶点, value 是一个
* set 集合 , 这样 即可实现 单点对多点, 图的构建就此完成。(领接表的思想)
*/
private static class Graph{
//图中节点的集合
public Set<Node> vertexSet = new HashSet<Node>();
// 相邻的节点, 记录边
public Map<Node, Set<Node>> adjaNode = new HashMap<Node,Set<Node>>();
// 将节点加入图中
/**
* @param start
* @param end
* 1.前两个if 是 先判断顶点集里是否有 这两个节点
* 2. 第三个if判断该节点出度里面是否已经包含了这个点, 即是否存在这个要加入的边
* 3. 通过2判断该边是一条新边, 如果 边集已包含start顶点,则只需加入end。
* 4. 不存在的话, 直接加入这两位
* @return
*/
public boolean addNode(Node start, Node end) {
if (!vertexSet.contains(start)) {
vertexSet.add(start);
}
if (!vertexSet.contains(end)) {
vertexSet.add(end);
}
if (adjaNode.containsKey(start) &&
adjaNode.get(start).contains(end)) {
return false;
}
if (adjaNode.containsKey(start)) {
adjaNode.get(start).add(end);
}else {
Set<Node> temp = new HashSet<>();
temp.add(end);
adjaNode.put(start, temp);
}
end.PaIn ++;
return true;
}
}
// Kahn算法
/**
* @author Administrator
* 1.采用的是BFS算法进行拓扑排序的(需要先学习图的BFS搜索)
* 2. 先通过构造函数找到入度为0的顶点
* 3. 通过process进行拓扑操作, 用的是BFS算法。
*/
private static class KahnTopo{
private List<Node> result; // 用来储存结果集
private Queue<Node> setOfZeroIndegree; // 用来存储入度为0 的顶点
private Graph graph;
// 构造函数, 初始化
public KahnTopo(Graph di) {
this.graph = di;
this.result = new ArrayList<Node>();
this.setOfZeroIndegree = new LinkedList<Node>();
// 对入度为0的集合进行初始化
for (Node iterator : this.graph.vertexSet) {
if (iterator.PaIn == 0) {
this.setOfZeroIndegree.add(iterator);
}
}
}
// 拓扑排序处理过程
private void process() {
while (! setOfZeroIndegree.isEmpty()) {
Node v = setOfZeroIndegree.poll();
// 将当前顶点添加到集合集中
result.add(v);
// 没有顶点了, 直接返回
if (this.graph.adjaNode.keySet().isEmpty()) {
return ;
}
// 遍历 v 引出的所有边
for (Node w : this.graph.adjaNode.get(v)) {
// 将该边从图中移除, 通过减少边的数量来表示
w.PaIn -- ;
if (0 == w.PaIn) {// 如果入度为0, 那么加入入度为0的集合
setOfZeroIndegree.add(w);
}
}
this.graph.vertexSet.remove(v);
this.graph.adjaNode.remove(v);
}
// 如果此时图中还存在边, 那么说明图中含有环路
if(!this.graph.vertexSet.isEmpty()) {
System.out.println("存在回环");
return ;
}
}
//结果集
public Iterable<Node> getResult() {
return result;
}
}
//测试
public static void main(String[] args) {
Node A = new Node("A");
Node B = new Node("B");
Node C = new Node("C");
Node D = new Node("D");
Node E = new Node("E");
Node F = new Node("F");
Graph graph = new Graph();
graph.addNode(A, B);
graph.addNode(B, C);
graph.addNode(B, D);
graph.addNode(D, C);
graph.addNode(E, C);
graph.addNode(C, F);
KahnTopo topo = new KahnTopo(graph);
topo.process();
for(Node temp : topo.getResult()){
System.out.print(temp.val.toString() + "-->");
}
}
}