/******************************************************************************* * ALMA - Atacama Large Millimeter Array * Copyright (c) ESO - European Southern Observatory, 2011 * (in the framework of the ALMA collaboration). * All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *******************************************************************************/ package alma.acs.algorithms; import java.util.*; /** * Topological sort algorithm, following Cormen et al, "Introduction to Algorithms". * <p> * To be used to sort a list of interdependent nodes, or to find out that this * is impossible because the dependencies are cyclic. * Applications need to wrap each of their nodes with a {@link Vertex} object, * set up the directed dependencies between any two such vertex objects * (see {@link Vertex#addAdjacentVertex(Vertex vertex) addAdjacentVertex}), run the sort algorithm, * and then extract the original nodes from the sorted vertices. * <p> * Expected use is to detangle component dependencies etc. */ public class TopologicalSort { // the graph as a List of Vertex objects private Collection<Vertex> m_vertices; // the sorted graph as a List of Vertex objects private LinkedList<Vertex> m_sortedVertices; // Map [key = Vertex, Value = List of Vertex objects that form a back edge in the graph] private Map<Vertex, List<Vertex>> m_cyclicVertices; /** * Constructor that takes the nodes, which we hope form a directed acyclic graph * @param vertices */ public TopologicalSort(Collection<Vertex> vertices) { m_vertices = vertices; m_sortedVertices = new LinkedList<Vertex>(); } /** * Tries to sort the vertices and to return them in a list. * The list will be incomplete if the graph could not be sorted, * see {@link #hasCycles()}. */ public List sort() { // init for (Iterator<Vertex> iter = m_vertices.iterator(); iter.hasNext(); ) { iter.next().setColor(Vertex.WHITE); } // depth-first-search for (Iterator<Vertex> iter = m_vertices.iterator(); iter.hasNext(); ) { Vertex vertex = iter.next(); if (vertex.getColor() == Vertex.WHITE) { dfsVisit(vertex); } } return m_sortedVertices; } private void dfsVisit(Vertex vertex) { vertex.setColor(Vertex.GRAY); for (Iterator<Vertex> iter = vertex.getAdjacencyList().iterator(); iter.hasNext(); ) { Vertex descendant = iter.next(); if (descendant.getColor() == Vertex.WHITE) { ///descendant.setPredecessor(vertex); dfsVisit(descendant); } else if (descendant.getColor() == Vertex.GRAY) { // we found a "back edge", which means a cycle in the graph if (m_cyclicVertices == null) { m_cyclicVertices = new HashMap<Vertex, List<Vertex>>(); } if (m_cyclicVertices.get(vertex) == null) { m_cyclicVertices.put(vertex, new ArrayList<Vertex>()); } m_cyclicVertices.get(vertex).add(descendant); } } // vertex is finished vertex.setColor(Vertex.BLACK); m_sortedVertices.addFirst(vertex); } /** * States whether the graph contains cyclic dependencies, * which implies that it could not be sorted. * To be called after {@link #sort()} was called. * @return true if cycles were found */ public boolean hasCycles() { return ( m_cyclicVertices != null ); } /** * Returns the vertices (graph nodes) that form a back edge in the graph. * * @return Map [key = Vertex, Value = List of adjacent Vertex objects that form the cycle] */ public Map getCyclicVertices() { return m_cyclicVertices; } }