包含检测是否有环,前序、后序等操作
import org.apache.commons.collections4.CollectionUtils; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.linkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Set; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DAG{ private static final Logger logger = LoggerFactory.getLogger(DAG.class); private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final Map nodesMap; private final Map > edgesMap; private final Map > reverseEdgesMap; public DAG() { nodesMap = new HashMap<>(); edgesMap = new HashMap<>(); reverseEdgesMap = new HashMap<>(); } public void addNode(Node node, NodeInfo nodeInfo) { lock.writeLock().lock(); try { nodesMap.put(node, nodeInfo); } finally { lock.writeLock().unlock(); } } public boolean addEdge(Node fromNode, Node toNode) { return addEdge(fromNode, toNode, false); } private boolean addEdge(Node fromNode, Node toNode, boolean createNode) { return addEdge(fromNode, toNode, null, createNode); } public boolean addEdge(Node fromNode, Node toNode, EdgeInfo edge, boolean createNode) { lock.writeLock().lock(); try { // Whether an edge can be successfully added(fromNode -> toNode) if (!isLegalAddEdge(fromNode, toNode, createNode)) { logger.error("serious error: add edge({} -> {}) is invalid, cause cycle!", fromNode, toNode); return false; } addNodeIfAbsent(fromNode, null); addNodeIfAbsent(toNode, null); addEdge(fromNode, toNode, edge, edgesMap); addEdge(toNode, fromNode, edge, reverseEdgesMap); return true; } finally { lock.writeLock().unlock(); } } public boolean containsNode(Node node) { lock.readLock().lock(); try { return nodesMap.containsKey(node); } finally { lock.readLock().unlock(); } } public boolean containsEdge(Node fromNode, Node toNode) { lock.readLock().lock(); try { Map endEdges = edgesMap.get(fromNode); if (endEdges == null) { return false; } return endEdges.containsKey(toNode); } finally { lock.readLock().unlock(); } } public NodeInfo getNode(Node node) { lock.readLock().lock(); try { return nodesMap.get(node); } finally { lock.readLock().unlock(); } } public int getNodesCount() { lock.readLock().lock(); try { return nodesMap.size(); } finally { lock.readLock().unlock(); } } public int getEdgesCount() { lock.readLock().lock(); try { int count = 0; for (Map.Entry > entry : edgesMap.entrySet()) { count += entry.getValue().size(); } return count; } finally { lock.readLock().unlock(); } } public Collection getBeginNode() { lock.readLock().lock(); try { return CollectionUtils.subtract(nodesMap.keySet(), reverseEdgesMap.keySet()); } finally { lock.readLock().unlock(); } } public Collection getEndNode() { lock.readLock().lock(); try { return CollectionUtils.subtract(nodesMap.keySet(), edgesMap.keySet()); } finally { lock.readLock().unlock(); } } public Set getPreviousNodes(Node node) { lock.readLock().lock(); try { return getNeighborNodes(node, reverseEdgesMap); } finally { lock.readLock().unlock(); } } public Set getSubsequentNodes(Node node) { lock.readLock().lock(); try { return getNeighborNodes(node, edgesMap); } finally { lock.readLock().unlock(); } } public int getIndegree(Node node) { lock.readLock().lock(); try { return getPreviousNodes(node).size(); } finally { lock.readLock().unlock(); } } public boolean hasCycle() { lock.readLock().lock(); try { return !topologicalSortImpl().getKey(); } finally { lock.readLock().unlock(); } } public List topologicalSort() throws Exception { lock.readLock().lock(); try { Map.Entry > entry = topologicalSortImpl(); if (entry.getKey()) { return entry.getValue(); } throw new Exception("serious error: graph has cycle ! "); } finally { lock.readLock().unlock(); } } private void addNodeIfAbsent(Node node, NodeInfo nodeInfo) { if (!containsNode(node)) { addNode(node, nodeInfo); } } private void addEdge(Node fromNode, Node toNode, EdgeInfo edge, Map > edges) { edges.putIfAbsent(fromNode, new HashMap<>()); Map tonodeEdges = edges.get(fromNode); toNodeEdges.put(toNode, edge); } private boolean isLegalAddEdge(Node fromNode, Node toNode, boolean createNode) { if (fromNode.equals(toNode)) { logger.error("edge fromNode({}) can't equals tonode({})", fromNode, toNode); return false; } if (!createNode) { if (!containsNode(fromNode) || !containsNode(toNode)) { logger.error("edge fromNode({}) or tonode({}) is not in vertices map", fromNode, toNode); return false; } } // Whether an edge can be successfully added(fromNode -> toNode),need to determine whether the DAG has cycle! int verticesCount = getNodesCount(); Queue queue = new linkedList<>(); queue.add(toNode); // if DAG doesn't find fromNode, it's not has cycle! while (!queue.isEmpty() && (--verticesCount > 0)) { Node key = queue.poll(); for (Node subsequentNode : getSubsequentNodes(key)) { if (subsequentNode.equals(fromNode)) { return false; } queue.add(subsequentNode); } } return true; } private Set getNeighborNodes(Node node, final Map > edges) { final Map neighborEdges = edges.get(node); if (neighborEdges == null) { return Collections.emptySet(); } return neighborEdges.keySet(); } private Map.Entry > topologicalSortImpl() { // node queue with degree of entry 0 Queue zeroIndegreeNodeQueue = new linkedList<>(); // save result List topoResultList = new ArrayList<>(); // save the node whose degree is not 0 Map notZeroIndegreeNodeMap = new HashMap<>(); // Scan all the vertices and push vertexs with an entry degree of 0 to queue for (Map.Entry vertices : nodesMap.entrySet()) { Node node = vertices.getKey(); int inDegree = getIndegree(node); if (inDegree == 0) { zeroIndegreeNodeQueue.add(node); topoResultList.add(node); } else { notZeroIndegreeNodeMap.put(node, inDegree); } } if (zeroIndegreeNodeQueue.isEmpty()) { return new AbstractMap.SimpleEntry<>(false, topoResultList); } // The topology algorithm is used to delete nodes with 0 degree of entry and its associated edges while (!zeroIndegreeNodeQueue.isEmpty()) { Node v = zeroIndegreeNodeQueue.poll(); // Get the neighbor node Set subsequentNodes = getSubsequentNodes(v); for (Node subsequentNode : subsequentNodes) { Integer degree = notZeroIndegreeNodeMap.get(subsequentNode); if (--degree == 0) { topoResultList.add(subsequentNode); zeroIndegreeNodeQueue.add(subsequentNode); notZeroIndegreeNodeMap.remove(subsequentNode); } else { notZeroIndegreeNodeMap.put(subsequentNode, degree); } } } // if notZeroIndegreeNodeMap is empty,there is no ring! return new AbstractMap.SimpleEntry<>(notZeroIndegreeNodeMap.size() == 0, topoResultList); } }



