/* * $Revision: 2594 $ * * last checkin: * $Author: gutwenger $ * $Date: 2012-07-15 15:35:29 +0200 (So, 15. Jul 2012) $ ***************************************************************/ /** \file * \brief Implementation of simple graph algorithms * * \author Carsten Gutwenger, Sebastian Leipert * * \par License: * This file is part of the Open Graph Drawing Framework (OGDF). * * \par * Copyright (C)
* See README.txt in the root directory of the OGDF installation for details. * * \par * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * Version 2 or 3 as published by the Free Software Foundation; * see the file LICENSE.txt included in the packaging of this file * for details. * * \par * This program 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 General Public License for more details. * * \par * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * * \see http://www.gnu.org/copyleft/gpl.html ***************************************************************/ #include "simple_graph_alg.h" #include "SList.h" #include "Stack.h" #include "GraphCopy.h" #include "tuples.h" #include "BoundedStack.h" namespace ogdf { //--------------------------------------------------------- // isLoopFree(), makeLoopFree() // testing for self-loops, removing self-loops //--------------------------------------------------------- bool isLoopFree(const Graph &G) { edge e; forall_edges(e,G) if(e->isSelfLoop()) return false; return true; } void makeLoopFree(Graph &G) { edge e, eNext; for (e = G.firstEdge(); e; e = eNext) { eNext = e->succ(); if (e->isSelfLoop()) G.delEdge(e); } } //--------------------------------------------------------- // isParallelFree(), makeParallelFree() // testing for multi-edges, removing multi-edges //--------------------------------------------------------- void parallelFreeSort(const Graph &G, SListPure &edges) { G.allEdges(edges); BucketSourceIndex bucketSrc; edges.bucketSort(0,G.maxNodeIndex(),bucketSrc); BucketTargetIndex bucketTgt; edges.bucketSort(0,G.maxNodeIndex(),bucketTgt); } bool isParallelFree(const Graph &G) { if (G.numberOfEdges() <= 1) return true; SListPure edges; parallelFreeSort(G,edges); SListConstIterator it = edges.begin(); edge ePrev = *it, e; for(it = ++it; it.valid(); ++it, ePrev = e) { e = *it; if (ePrev->source() == e->source() && ePrev->target() == e->target()) return false; } return true; } int numParallelEdges(const Graph &G) { if (G.numberOfEdges() <= 1) return 0; SListPure edges; parallelFreeSort(G,edges); int num = 0; SListConstIterator it = edges.begin(); edge ePrev = *it, e; for(it = ++it; it.valid(); ++it, ePrev = e) { e = *it; if (ePrev->source() == e->source() && ePrev->target() == e->target()) ++num; } return num; } //--------------------------------------------------------- // isParallelFreeUndirected(), makeParallelFreeUndirected() // testing for (undirected) multi-edges, removing (undirected) multi-edges //--------------------------------------------------------- void parallelFreeSortUndirected(const Graph &G, SListPure &edges, EdgeArray &minIndex, EdgeArray &maxIndex) { G.allEdges(edges); edge e; forall_edges(e,G) { int srcIndex = e->source()->index(), tgtIndex = e->target()->index(); if (srcIndex <= tgtIndex) { minIndex[e] = srcIndex; maxIndex[e] = tgtIndex; } else { minIndex[e] = tgtIndex; maxIndex[e] = srcIndex; } } BucketEdgeArray bucketMin(minIndex), bucketMax(maxIndex); edges.bucketSort(0,G.maxNodeIndex(),bucketMin); edges.bucketSort(0,G.maxNodeIndex(),bucketMax); } bool isParallelFreeUndirected(const Graph &G) { if (G.numberOfEdges() <= 1) return true; SListPure edges; EdgeArray minIndex(G), maxIndex(G); parallelFreeSortUndirected(G,edges,minIndex,maxIndex); SListConstIterator it = edges.begin(); edge ePrev = *it, e; for(it = ++it; it.valid(); ++it, ePrev = e) { e = *it; if (minIndex[ePrev] == minIndex[e] && maxIndex[ePrev] == maxIndex[e]) return false; } return true; } int numParallelEdgesUndirected(const Graph &G) { if (G.numberOfEdges() <= 1) return 0; SListPure edges; EdgeArray minIndex(G), maxIndex(G); parallelFreeSortUndirected(G,edges,minIndex,maxIndex); int num = 0; SListConstIterator it = edges.begin(); edge ePrev = *it, e; for(it = ++it; it.valid(); ++it, ePrev = e) { e = *it; if (minIndex[ePrev] == minIndex[e] && maxIndex[ePrev] == maxIndex[e]) ++num; } return num; } //--------------------------------------------------------- // isConnected(), makeConnected() // testing connectivity, establishing connectivity //--------------------------------------------------------- bool isConnected(const Graph &G) { node v = G.firstNode(); if (v == 0) return true; int count = 0; NodeArray visited(G,false); BoundedStack S(G.numberOfNodes()); S.push(v); visited[v] = true; while(!S.empty()) { v = S.pop(); ++count; adjEntry adj; forall_adj(adj,v) { node w = adj->twinNode(); if(!visited[w]) { visited[w] = true; S.push(w); } } } return (count == G.numberOfNodes()); } void makeConnected(Graph &G, List &added) { added.clear(); if (G.numberOfNodes() == 0) return; NodeArray visited(G,false); BoundedStack S(G.numberOfNodes()); node pred = 0, u; forall_nodes(u,G) { if (visited[u]) continue; node vMinDeg = u; int minDeg = u->degree(); S.push(u); visited[u] = true; while(!S.empty()) { node v = S.pop(); adjEntry adj; forall_adj(adj,v) { node w = adj->twinNode(); if(!visited[w]) { visited[w] = true; S.push(w); int wDeg = w->degree(); if (wDeg < minDeg) { vMinDeg = w; minDeg = wDeg; } } } } if (pred) added.pushBack(G.newEdge(pred,vMinDeg)); pred = vMinDeg; } } int connectedComponents(const Graph &G, NodeArray &component) { int nComponent = 0; component.fill(-1); StackPure S; node v; forall_nodes(v,G) { if (component[v] != -1) continue; S.push(v); component[v] = nComponent; while(!S.empty()) { node w = S.pop(); edge e; forall_adj_edges(e,w) { node x = e->opposite(w); if (component[x] == -1) { component[x] = nComponent; S.push(x); } } } ++nComponent; } return nComponent; } //return the isolated nodes too, is used in incremental layout int connectedIsolatedComponents(const Graph &G, List &isolated, NodeArray &component) { int nComponent = 0; component.fill(-1); StackPure S; node v; forall_nodes(v,G) { if (component[v] != -1) continue; S.push(v); component[v] = nComponent; while(!S.empty()) { node w = S.pop(); if (w->degree() == 0) isolated.pushBack(w); edge e; forall_adj_edges(e,w) { node x = e->opposite(w); if (component[x] == -1) { component[x] = nComponent; S.push(x); } } } ++nComponent; } return nComponent; }//connectedIsolated //--------------------------------------------------------- // isBiconnected(), makeBiconnected() // testing biconnectivity, establishing biconnectivity //--------------------------------------------------------- static node dfsIsBicon (const Graph &G, node v, node father, NodeArray &number, NodeArray &lowpt, int &numCount) { node first_son = 0; lowpt[v] = number[v] = ++numCount; edge e; forall_adj_edges(e,v) { node w = e->opposite(v); if (v == w) continue; // ignore self-loops if (number[w] == 0) { if (first_son == 0) first_son = w; node cutVertex = dfsIsBicon(G,w,v,number,lowpt,numCount); if (cutVertex) return cutVertex; // is v cut vertex ? if (lowpt[w] >= number[v] && (w != first_son || father != 0)) return v; if (lowpt[w] < lowpt[v]) lowpt[v] = lowpt[w]; } else { if (number[w] < lowpt[v]) lowpt[v] = number[w]; } } return 0; } bool isBiconnected(const Graph &G, node &cutVertex) { if (G.empty()) return true; NodeArray number(G,0); NodeArray lowpt(G); int numCount = 0; cutVertex = dfsIsBicon(G,G.firstNode(),0,number,lowpt,numCount); return (numCount == G.numberOfNodes() && cutVertex == 0); } static void dfsMakeBicon (Graph &G, node v, node father, NodeArray &number, NodeArray &lowpt, int &numCount, List &added) { node predSon = 0; lowpt[v] = number[v] = ++numCount; edge e; forall_adj_edges(e,v) { node w = e->opposite(v); if (v == w) continue; // ignore self-loops if (number[w] == 0) { dfsMakeBicon(G,w,v,number,lowpt,numCount,added); // is v cut vertex ? if (lowpt[w] >= number[v]) { if (predSon == 0 && father != 0) added .pushBack(G.newEdge(w,father)); else if (predSon != 0) added.pushBack(G.newEdge(w,predSon)); } if (lowpt[w] < lowpt[v]) lowpt[v] = lowpt[w]; predSon = w; } else { if (number[w] < lowpt[v]) lowpt[v] = number[w]; } } } void makeBiconnected(Graph &G, List &added) { if (G.empty()) return; makeConnected(G,added); NodeArray number(G,0); NodeArray lowpt(G); int numCount = 0; dfsMakeBicon(G,G.firstNode(),0,number,lowpt,numCount,added); } //--------------------------------------------------------- // biconnectedComponents() // computing biconnected components //--------------------------------------------------------- static void dfsBiconComp (const Graph &G, node v, node father, NodeArray &number, NodeArray &lowpt, StackPure &called, EdgeArray &component, int &nNumber, int &nComponent) { lowpt[v] = number[v] = ++nNumber; called.push(v); edge e; forall_adj_edges(e,v) { node w = e->opposite(v); if (v == w) continue; // ignore self-loops if (number[w] == 0) { dfsBiconComp(G,w,v,number,lowpt,called,component, nNumber,nComponent); if (lowpt[w] < lowpt[v]) lowpt[v] = lowpt[w]; } else { if (number[w] < lowpt[v]) lowpt[v] = number[w]; } } if (father && (lowpt[v] == number[father])) { node w; do { w = called.top(); called.pop(); forall_adj_edges(e,w) { if (number[w] > number[e->opposite(w)]) component[e] = nComponent; } } while (w != v); ++nComponent; } } int biconnectedComponents(const Graph &G, EdgeArray &component) { if (G.empty()) return 0; StackPure called; NodeArray number(G,0); NodeArray lowpt(G); int nNumber = 0, nComponent = 0, nIsolated = 0; node v; forall_nodes(v,G) { if (number[v] == 0) { bool isolated = true; edge e; forall_adj_edges(e,v) if (!e->isSelfLoop()) { isolated = false; break; } if (isolated) ++nIsolated; else dfsBiconComp(G,v,0,number,lowpt,called,component, nNumber,nComponent); } } return nComponent + nIsolated; } //--------------------------------------------------------- // isTriconnected() // testing triconnectivity //--------------------------------------------------------- bool isTriconnectedPrimitive(const Graph &G, node &s1, node &s2) { s1 = s2 = 0; if (isConnected(G) == false) return false; if (isBiconnected(G,s1) == false) return false; if (G.numberOfNodes() <= 3) return true; // make a copy of G GraphCopySimple GC(G); // for each node v in G, we test if G \ v is biconnected node v; forall_nodes(v,G) { node vC = GC.copy(v), wC; // store adjacent nodes SListPure adjacentNodes; edge eC; forall_adj_edges(eC,vC) { wC = eC->opposite(vC); // forget self-loops (vC would no longer be in GC!) if (wC != vC) adjacentNodes.pushBack(wC); } GC.delNode(vC); // test for biconnectivity if(isBiconnected(GC,wC) == false) { s1 = v; s2 = GC.original(wC); return false; } // restore deleted node with adjacent edges vC = GC.newNode(v); SListConstIterator it; for(it = adjacentNodes.begin(); it.valid(); ++it) GC.newEdge(vC,*it); } return true; } //-------------------------------------------------------------------------- // triangulate() //-------------------------------------------------------------------------- void triangulate(Graph &G) { OGDF_ASSERT(isSimple(G)); CombinatorialEmbedding E(G); OGDF_ASSERT(E.consistencyCheck()); node v; edge e; adjEntry adj, succ, succ2, succ3; NodeArray marked(E.getGraph(), 0); forall_nodes(v,E.getGraph()) { marked.init(E.getGraph(), 0); forall_adj(adj,v) { marked[adj->twinNode()] = 1; } // forall faces adj to v forall_adj(adj,v) { succ = adj->faceCycleSucc(); succ2 = succ->faceCycleSucc(); if (succ->twinNode() != v && adj->twinNode() != v) { while (succ2->twinNode() != v) { if (marked[succ2->theNode()] == 1) { // edge e=(x2,x4) succ3 = succ2->faceCycleSucc(); E.splitFace(succ, succ3); } else { // edge e=(v=x1,x3) e = E.splitFace(adj, succ2); marked[succ2->theNode()] = 1; // old adj is in wrong face adj = e->adjSource(); } succ = adj->faceCycleSucc(); succ2 = succ->faceCycleSucc(); } } } } } //-------------------------------------------------------------------------- // isAcyclic(), isAcyclicUndirected(), makeAcyclic(), makeAcyclicByReverse() // testing acyclicity, establishing acyclicity //-------------------------------------------------------------------------- void dfsIsAcyclic(const Graph &G, node v, NodeArray &number, NodeArray &completion, int &nNumber, int &nCompletion) { number[v] = ++nNumber; edge e; forall_adj_edges(e,v) { node w = e->target(); if (number[w] == 0) dfsIsAcyclic(G,w,number,completion,nNumber,nCompletion); } completion[v] = ++nCompletion; } void dfsIsAcyclicUndirected(const Graph &G, node v, NodeArray &number, int &nNumber, List &backedges) { number[v] = ++nNumber; adjEntry adj; node w; forall_adj(adj,v) { w = adj->twinNode(); if (number[w] == 0) { dfsIsAcyclicUndirected(G,w,number,nNumber,backedges); } else { if (number[w] > number[v]) { backedges.pushBack(adj->theEdge()); } } } } bool isAcyclic(const Graph &G, List &backedges) { backedges.clear(); NodeArray number(G,0), completion(G); int nNumber = 0, nCompletion = 0; node v; forall_nodes(v,G) if (number[v] == 0) dfsIsAcyclic(G,v,number,completion,nNumber,nCompletion); edge e; forall_edges(e,G) { node src = e->source(), tgt = e->target(); if (number[src] >= number[tgt] && completion[src] <= completion[tgt]) backedges.pushBack(e); } return backedges.empty(); } bool isAcyclicUndirected(const Graph &G, List &backedges) { backedges.clear(); int nNumber = 0; NodeArray number(G,0); node v; forall_nodes(v,G) { if (number[v] == 0) { dfsIsAcyclicUndirected(G,v,number,nNumber,backedges); } } return backedges.empty(); } void makeAcyclic(Graph &G) { List backedges; isAcyclic(G,backedges); ListIterator it; for(it = backedges.begin(); it.valid(); ++it) G.delEdge(*it); } void makeAcyclicByReverse(Graph &G) { List backedges; isAcyclic(G,backedges); ListIterator it; for(it = backedges.begin(); it.valid(); ++it) if (!(*it)->isSelfLoop()) G.reverseEdge(*it); } //--------------------------------------------------------- // hasSingleSource(), hasSingleSink() // testing for single source/sink //--------------------------------------------------------- bool hasSingleSource(const Graph& G, node &s) { node v; s = 0; forall_nodes(v,G) { if (v->indeg() == 0) { if (s != 0) { s = 0; return false; } else s = v; } } return (G.empty() || s != 0); } bool hasSingleSink(const Graph& G, node &t) { node v; t = 0; forall_nodes(v,G) { if (v->outdeg() == 0) { if (t != 0) { t = 0; return false; } else t = v; } } return (G.empty() || t != 0); } //--------------------------------------------------------- // isStGraph() // true <=> G is st-graph, i.e., is acyclic, contains exactly one source s // and one sink t, and the edge (s,t); returns single source s and single // sink t if contained (otherwise they are set to 0), and edge st if // contained (otherwise 0) //--------------------------------------------------------- bool isStGraph(const Graph &G, node &s, node &t, edge &st) { st = 0; hasSingleSource(G,s); hasSingleSink (G,t); if (s == 0 || t == 0 || isAcyclic(G) == false) { s = t = 0; return false; } edge e; forall_adj_edges(e,s) { if (e->target() == t) { st = e; break; } } return (st != 0); } //--------------------------------------------------------- // topologicalNumbering() // computes a topological numbering of an acyclic graph //--------------------------------------------------------- void topologicalNumbering(const Graph &G, NodeArray &num) { BoundedStack S(G.numberOfNodes()); NodeArray indeg(G); node v; forall_nodes(v,G) if((indeg[v] = v->indeg()) == 0) S.push(v); int count = 0; while(!S.empty()) { node v = S.pop(); num[v] = count++; edge e; forall_adj_edges(e,v) { node u = e->target(); if(u != v) { if(--indeg[u] == 0) S.push(u); } } } } //--------------------------------------------------------- // strongComponents() // computes the strongly connected components //--------------------------------------------------------- //! Computes the strongly connected components with the algorithm of Tarjan. /** * @param G is the input graph. * @param w is the current node. * @param S is the stack containing all vertices of the current * component during the algorithm * @param pre is the preorder number. * @param low is the lowest reachable preorder number (lowpoint). * @param cnt is the counter for the dfs-number. * @param scnt is the counter for the components. * @param component is assigned a mapping from nodes to component numbers. */ void dfsStrongComponents( const Graph& G, node w, BoundedStack& S, NodeArray& pre, NodeArray& low, int& cnt, int& scnt, NodeArray& component) { S.push(w); int min = cnt; low[w] = cnt; pre[w] = cnt; cnt++; node t; edge e; forall_adj_edges(e, w) { if (e->source() == w) { t = e->target(); if(pre[t] == -1) { dfsStrongComponents(G, t, S, pre, low, cnt, scnt, component); } if (low[t] < low[w]) min = low[t]; } } if (min < low[w]) { low[w] = min; return; } do { t = S.pop(); component[t] = scnt; low[t] = G.numberOfNodes(); } while (t != w); scnt++; } int strongComponents(const Graph& G, NodeArray& component) { if (G.numberOfNodes() == 0) return 0; if (G.numberOfNodes() == 1){ component[G.firstNode()] = 0; return 1; } NodeArray pre(G, -1); NodeArray low(G, G.numberOfNodes()); BoundedStack S(G.numberOfNodes()); int cnt = 0; int scnt = 0; node v; forall_nodes(v, G){ if (pre[v] == -1){ dfsStrongComponents(G, v, S, pre, low, cnt, scnt, component); } } return scnt; } //--------------------------------------------------------- // isFreeForest() // testing if graph represents a free forest //--------------------------------------------------------- bool isFreeForest(const Graph &G) { NodeArray visited(G,false); node vFirst; forall_nodes(vFirst,G) { if(visited[vFirst]) continue; StackPure > S; S.push(Tuple2(vFirst,0)); while(!S.empty()) { Tuple2 t = S.pop(); node v = t.x1(); node parent = t.x2(); visited[v] = true; adjEntry adj; forall_adj(adj,v) { node w = adj->twinNode(); // skip edge to parent, but only once! if(w == parent) { parent = 0; continue; } if(visited[w] == true) return false; S.push(Tuple2(w,v)); } } } return true; } //--------------------------------------------------------- // isForest(), isTree() // testing if graph represents a forest/tree //--------------------------------------------------------- static bool dfsIsForest (node v, NodeArray &visited, NodeArray &mark) { SListPure sons; visited[v] = true; edge e; forall_adj_edges(e,v) { node w = e->target(); if (w != v && !mark[w]) { mark[w] = true; sons.pushBack(w); } } SListIterator it; for(it = sons.begin(); it.valid(); ++it) mark[*it] = false; while(!sons.empty()) { node w = sons.front(); sons.popFront(); if (visited [w] || dfsIsForest(w,visited,mark) == false) return false; } return true; } bool isForest(const Graph& G, List &roots) { roots.clear(); if (G.empty()) return true; NodeArray visited(G,false), mark(G,false); node v; forall_nodes(v,G) if (v->indeg() == 0) { roots.pushBack(v); if (dfsIsForest(v,visited,mark) == false) return false; } forall_nodes(v,G) if (!visited[v]) return false; return true; } bool isTree (const Graph& G, node &root) { List roots; if (isForest(G,roots) && roots.size() == 1) { root = roots.front(); return true; } return false; } } // end namespace ogdf