包含检测是否有环,前序、后序等操作
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<Node, NodeInfo, EdgeInfo> {
private static final Logger logger = LoggerFactory.getLogger(DAG.class);
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Map<Node, NodeInfo> nodesMap;
private final Map<Node, Map<Node, EdgeInfo>> edgesMap;
private final Map<Node, Map<Node, EdgeInfo>> 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 {
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<Node, EdgeInfo> 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<Node, Map<Node, EdgeInfo>> entry : edgesMap.entrySet()) {
count += entry.getValue().size();
}
return count;
} finally {
lock.readLock().unlock();
}
}
public Collection<Node> getBeginNode() {
lock.readLock().lock();
try {
return CollectionUtils.subtract(nodesMap.keySet(), reverseEdgesMap.keySet());
} finally {
lock.readLock().unlock();
}
}
public Collection<Node> getEndNode() {
lock.readLock().lock();
try {
return CollectionUtils.subtract(nodesMap.keySet(), edgesMap.keySet());
} finally {
lock.readLock().unlock();
}
}
public Set<Node> getPreviousNodes(Node node) {
lock.readLock().lock();
try {
return getNeighborNodes(node, reverseEdgesMap);
} finally {
lock.readLock().unlock();
}
}
public Set<Node> 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<Node> topologicalSort() throws Exception {
lock.readLock().lock();
try {
Map.Entry<Boolean, List<Node>> 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<Node, Map<Node, EdgeInfo>> edges) {
edges.putIfAbsent(fromNode, new HashMap<>());
Map<Node, EdgeInfo> 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;
}
}
int verticesCount = getNodesCount();
Queue<Node> queue = new LinkedList<>();
queue.add(toNode);
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<Node> getNeighborNodes(Node node, final Map<Node, Map<Node, EdgeInfo>> edges) {
final Map<Node, EdgeInfo> neighborEdges = edges.get(node);
if (neighborEdges == null) {
return Collections.emptySet();
}
return neighborEdges.keySet();
}
private Map.Entry<Boolean, List<Node>> topologicalSortImpl() {
Queue<Node> zeroIndegreeNodeQueue = new LinkedList<>();
List<Node> topoResultList = new ArrayList<>();
Map<Node, Integer> notZeroIndegreeNodeMap = new HashMap<>();
for (Map.Entry<Node, NodeInfo> 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);
}
while (!zeroIndegreeNodeQueue.isEmpty()) {
Node v = zeroIndegreeNodeQueue.poll();
Set<Node> 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);
}
}
}
return new AbstractMap.SimpleEntry<>(notZeroIndegreeNodeMap.size() == 0, topoResultList);
}
}
|