1 /** \file
2 * \brief Implementation of simple graph algorithms
3 *
4 * \author Carsten Gutwenger, Sebastian Leipert
5 *
6 * \par License:
7 * This file is part of the Open Graph Drawing Framework (OGDF).
8 *
9 * \par
10 * Copyright (C)<br>
11 * See README.md in the OGDF root directory for details.
12 *
13 * \par
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * Version 2 or 3 as published by the Free Software Foundation;
17 * see the file LICENSE.txt included in the packaging of this file
18 * for details.
19 *
20 * \par
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * \par
27 * You should have received a copy of the GNU General Public
28 * License along with this program; if not, see
29 * http://www.gnu.org/copyleft/gpl.html
30 */
31
32
33 #include <ogdf/basic/simple_graph_alg.h>
34 #include <ogdf/basic/GraphCopy.h>
35 #include <ogdf/basic/tuples.h>
36 #include <ogdf/basic/Math.h>
37
38 namespace ogdf {
39
40 // Functions related to self-loops
41
removeSelfLoops(Graph & graph,node v)42 void removeSelfLoops(Graph &graph, node v) {
43 adjEntry adj = v->firstAdj();
44 adjEntry adjPrev = nullptr;
45
46 while (adj != nullptr) {
47 edge e{adj->theEdge()};
48 if (e->isSelfLoop()) {
49 graph.delEdge(e);
50 } else {
51 adjPrev = adj;
52 }
53
54 adj = adjPrev == nullptr ? v->firstAdj() : adjPrev->succ();
55 }
56 }
57
isLoopFree(const Graph & G)58 bool isLoopFree(const Graph &G)
59 {
60 for(edge e : G.edges)
61 if(e->isSelfLoop()) return false;
62
63 return true;
64 }
65
makeLoopFree(Graph & G)66 void makeLoopFree(Graph &G)
67 {
68 safeForEach(G.edges, [&](edge e) {
69 if (e->isSelfLoop()) G.delEdge(e);
70 });
71 }
72
hasNonSelfLoopEdges(const Graph & G)73 bool hasNonSelfLoopEdges(const Graph &G) {
74 for (edge e : G.edges) {
75 if (!e->isSelfLoop()) {
76 return true;
77 }
78 }
79 return false;
80 }
81
82 // Functions related to directed parallel edges
83
parallelFreeSort(const Graph & G,SListPure<edge> & edges)84 void parallelFreeSort(const Graph &G, SListPure<edge> &edges)
85 {
86 G.allEdges(edges);
87
88 BucketSourceIndex bucketSrc;
89 edges.bucketSort(0,G.maxNodeIndex(),bucketSrc);
90
91 BucketTargetIndex bucketTgt;
92 edges.bucketSort(0,G.maxNodeIndex(),bucketTgt);
93 }
94
isParallelFree(const Graph & G)95 bool isParallelFree(const Graph &G) {
96 return numParallelEdges<true>(G) == 0;
97 }
98
99 // Functions related to undirected parallel edges
100
parallelFreeSortUndirected(const Graph & G,SListPure<edge> & edges,EdgeArray<int> & minIndex,EdgeArray<int> & maxIndex)101 void parallelFreeSortUndirected(const Graph &G,
102 SListPure<edge> &edges,
103 EdgeArray<int> &minIndex,
104 EdgeArray<int> &maxIndex)
105 {
106 G.allEdges(edges);
107
108 for(edge e : G.edges) {
109 int srcIndex = e->source()->index(), tgtIndex = e->target()->index();
110 if (srcIndex <= tgtIndex) {
111 minIndex[e] = srcIndex; maxIndex[e] = tgtIndex;
112 } else {
113 minIndex[e] = tgtIndex; maxIndex[e] = srcIndex;
114 }
115 }
116
117 BucketEdgeArray bucketMin(minIndex), bucketMax(maxIndex);
118 edges.bucketSort(0,G.maxNodeIndex(),bucketMin);
119 edges.bucketSort(0,G.maxNodeIndex(),bucketMax);
120 }
121
isParallelFreeUndirected(const Graph & G)122 bool isParallelFreeUndirected(const Graph &G) {
123 return numParallelEdgesUndirected<true>(G) == 0;
124 }
125
126 // Testing and establishing connectivity
127
isConnected(const Graph & G)128 bool isConnected(const Graph &G)
129 {
130 node v = G.firstNode();
131 if (v == nullptr) return true;
132
133 int count = 0;
134 NodeArray<bool> visited(G,false);
135 ArrayBuffer<node> S(G.numberOfNodes());
136
137 S.push(v);
138 visited[v] = true;
139 while(!S.empty()) {
140 v = S.popRet();
141 ++count;
142
143 for(adjEntry adj : v->adjEntries) {
144 node w = adj->twinNode();
145 if(!visited[w]) {
146 visited[w] = true;
147 S.push(w);
148 }
149 }
150 }
151
152 return count == G.numberOfNodes();
153 }
154
155
makeConnected(Graph & G,List<edge> & added)156 void makeConnected(Graph &G, List<edge> &added)
157 {
158 added.clear();
159 if (G.numberOfNodes() == 0) return;
160 NodeArray<bool> visited(G,false);
161 ArrayBuffer<node> S(G.numberOfNodes());
162
163 node pred = nullptr;
164 for(node u : G.nodes)
165 {
166 if (visited[u]) continue;
167
168 node vMinDeg = u;
169 int minDeg = u->degree();
170
171 S.push(u);
172 visited[u] = true;
173
174 while(!S.empty())
175 {
176 node v = S.popRet();
177
178 for(adjEntry adj : v->adjEntries) {
179 node w = adj->twinNode();
180 if(!visited[w]) {
181 visited[w] = true;
182 S.push(w);
183
184 int wDeg = w->degree();
185 if (wDeg < minDeg) {
186 vMinDeg = w;
187 minDeg = wDeg;
188 }
189 }
190 }
191 }
192
193 if (pred)
194 added.pushBack(G.newEdge(pred,vMinDeg));
195 pred = vMinDeg;
196 }
197 }
198
199
connectedComponents(const Graph & G,NodeArray<int> & component,List<node> * isolated)200 int connectedComponents(const Graph &G,
201 NodeArray<int> &component,
202 List<node> *isolated)
203 {
204 int nComponent = 0;
205 component.fill(-1);
206
207 ArrayBuffer<node> S;
208
209 for(node v : G.nodes) {
210 if (component[v] != -1) continue;
211
212 if (isolated != nullptr && v->degree() == 0) {
213 isolated->pushBack(v);
214 }
215 S.push(v);
216 component[v] = nComponent;
217
218 while(!S.empty()) {
219 node w = S.popRet();
220 for(adjEntry adj : w->adjEntries) {
221 node x = adj->twinNode();
222 if (component[x] == -1) {
223 component[x] = nComponent;
224 S.push(x);
225 }
226 }
227 }
228
229 ++nComponent;
230 }
231
232 return nComponent;
233 }
234
235 // Testing and establishing biconnectivity
236
237 //! Build up a dfs-tree starting from the node root by assigning each reachable
238 //! node in the graph a discovery time (number) and a parent.
239 /**
240 * @param root is the node which should be the root of the dfs-tree.
241 * @param number is assigned the number (discovery time) for each node.
242 * The number of root is firstNr, the number of unvisited nodes is 0.
243 * @param parent is assigned the parent in the dfs-tree for each node.
244 * @param childNr is assigned the number of children for each node.
245 * @param revS is assigned all visited nodes such that the top element of revS
246 * is the node that was visited last.
247 * @param directed should be set to true if the directionality of edges should
248 * be respected.
249 * @param firstNr is the index > 0 at which the numbering of the nodes starts.
250 * @return the number of visited nodes, i.e., nodes in the dfs-tree.
251 */
buildDfsTree(const node & root,NodeArray<int> & number,NodeArray<node> & parent,NodeArray<int> & childNr,ArrayBuffer<node> & revS,bool directed=false,int firstNr=1)252 static int buildDfsTree(const node &root,
253 NodeArray<int> &number,
254 NodeArray<node> &parent,
255 NodeArray<int> &childNr,
256 ArrayBuffer<node> &revS,
257 bool directed = false,
258 int firstNr = 1)
259 {
260 OGDF_ASSERT(firstNr > 0);
261
262 ArrayBuffer<node> S;
263 S.push(root);
264
265 int numCount = firstNr;
266 childNr.fill(0);
267
268 // Build up search tree and note discovery time and parent for each node.
269 while (!S.empty()) {
270 node v = S.popRet();
271
272 // Ignore nodes that were already visited.
273 if (number[v] != 0) {
274 continue;
275 }
276
277 revS.push(v);
278
279 // Set correct discovery time for v.
280 number[v] = numCount++;
281
282 // For all adjacent nodes w of v:
283 for (adjEntry adj : v->adjEntries) {
284 if (directed && adj->theEdge()->source() != v) {
285 continue;
286 }
287
288 node w = adj->twinNode();
289
290 // If w has not been visited yet:
291 // Push it on the stack, remember its parent and number of children.
292 if (number[w] == 0) {
293 S.push(w);
294
295 // If a parent was determined previously, revert that.
296 if (parent[w] != nullptr) {
297 childNr[parent[w]]--;
298 }
299
300 parent[w] = v;
301 childNr[v]++;
302 }
303 }
304 }
305
306 return numCount - firstNr;
307 }
308
309 //! Find cut vertices and potential edges that could be added to turn the cut
310 //! vertices into non-cut vertices.
311 /**
312 * The algorithm is applied to the graph whose nodes were pushed to the
313 * ArrayBuffer revS. number, parent and revS can be obtained with buildDfsTree.
314 *
315 * @param number contains the number (discovery time) for each node.
316 * The number of root is 1, the number of unvisited nodes is 0.
317 * @param parent contains the parent in the dfs-tree for each node.
318 * @param revS contains the nodes of a graph such that the node that was visited
319 * last during the dfs-traversal is its top element.
320 * @param cutVertices is assigned the cut vertices of the graph.
321 * @param addEdges is assigned the tuples of nodes which have to be connected in
322 * order to turn each cut vertex into a non-cut vertex.
323 * @param only_one should be set to true if the search should stop after finding
324 * one cut vertex, to false if all cut vertices should be found.
325 * @return true if the graph contains at least one cut vertex, false otherwise.
326 */
findCutVertices(NodeArray<int> & number,NodeArray<node> & parent,ArrayBuffer<node> & revS,ArrayBuffer<node> & cutVertices,ArrayBuffer<Tuple2<node,node>> & addEdges,bool only_one)327 static bool findCutVertices(NodeArray<int> &number,
328 NodeArray<node> &parent,
329 ArrayBuffer<node> &revS,
330 ArrayBuffer<node> &cutVertices,
331 ArrayBuffer<Tuple2<node,node>> &addEdges,
332 bool only_one)
333 {
334 NodeArray<int> lowpt(number);
335
336 // Go backwards through the dfs-tree:
337 // Calculate the lowpoint for each node and test for cut vertices.
338 while (!revS.empty()) {
339 node v = revS.popRet();
340 node firstChild = nullptr;
341
342 // For all adjacent nodes w of v:
343 for (adjEntry adj : v->adjEntries) {
344 node w = adj->twinNode();
345
346 // Ignore self-loops and the parent of v.
347 if (v == w || parent[v] == w) {
348 continue;
349 }
350
351 // If v->w is a backedge in the dfs-tree, update v's lowpoint.
352 if (number[v] > number[w] ) {
353 if (lowpt[v] > number[w]) {
354 lowpt[v] = number[w];
355 }
356 } else {
357 // If w is v's child in the dfs-tree, update v's lowpoint.
358 if (parent[w] == v) {
359 if (lowpt[v] > lowpt[w]) {
360 lowpt[v] = lowpt[w];
361 }
362
363 // See whether w is v's first son.
364 if (firstChild == nullptr) {
365 firstChild = w;
366 }
367
368 // Non-root v is a cut vertex if lowpt[w] >= number[v].
369 if (parent[v] != nullptr && lowpt[w] >= number[v]) {
370 // Suggest to add an edge between w and v's parent.
371 cutVertices.push(v);
372 addEdges.push(Tuple2<node,node>(w, parent[v]));
373
374 if (only_one) {
375 return true;
376 }
377 }
378
379 // Root v is a cut vertex if v has two or more children.
380 if (parent[v] == nullptr && w != firstChild) {
381 // Suggest to add an edge between those children.
382 cutVertices.push(v);
383 addEdges.push(Tuple2<node,node>(w, firstChild));
384
385 if (only_one) {
386 return true;
387 }
388 }
389 }
390 }
391 }
392 }
393
394 return !cutVertices.empty();
395 }
396
isBiconnected(const Graph & G,node & cutVertex)397 bool isBiconnected(const Graph &G, node &cutVertex)
398 {
399 cutVertex = nullptr;
400
401 if (G.empty()) {
402 return true;
403 }
404
405 NodeArray<int> number(G,0); // discovery times
406 NodeArray<node> parent(G,nullptr); // parents in the dfs tree
407 ArrayBuffer<node> revS; // nodes of the dfs tree in reverse order
408
409 // Build the dfs-tree and get the number of visited nodes.
410 NodeArray<int> childNr(G);
411 int numCount = buildDfsTree(G.firstNode(), number, parent, childNr, revS);
412
413 // If the graph is not connected, return false.
414 if (numCount != G.numberOfNodes()) {
415 return false;
416 }
417
418 // If there are cut vertices in the graph, return false, else true.
419 ArrayBuffer<node> cutVertices;
420 ArrayBuffer<Tuple2<node,node>> addEdges;
421 if (findCutVertices(number, parent, revS, cutVertices, addEdges, true)) {
422 cutVertex = cutVertices.top();
423 return false;
424 } else {
425 return true;
426 }
427 }
428
makeBiconnected(Graph & G,List<edge> & added)429 void makeBiconnected(Graph &G, List<edge> &added)
430 {
431 if (G.empty()) {
432 return;
433 }
434
435 makeConnected(G, added);
436
437 NodeArray<int> number(G,0); // discovery times
438 NodeArray<node> parent(G,nullptr); // parents in the dfs tree
439 ArrayBuffer<node> revS; // nodes of the dfs tree in reverse order
440
441 // Build the dfs-tree.
442 NodeArray<int> childNr(G);
443 buildDfsTree(G.firstNode(), number, parent, childNr, revS);
444
445 // Find all cut vertices.
446 ArrayBuffer<node> cutVertices;
447 ArrayBuffer<Tuple2<node,node>> addEdges;
448 findCutVertices(number, parent, revS, cutVertices, addEdges, false);
449
450 // Add a new edge for each cut vertex to make the graph biconnected.
451 for (Tuple2<node,node> nodes : addEdges) {
452 added.pushBack(G.newEdge(nodes.x1(), nodes.x2()));
453 }
454 }
455
456
457 // Biconnected components
458
459 /**
460 * Return true iff all incident edges of the given node v are self-loops.
461 * In particular, return true if v has no incident edges at all.
462 *
463 * @param v the node to be tested for isolation.
464 * @return true iff v has no incident edges to other nodes.
465 */
isIsolated(const node v)466 static bool isIsolated(const node v)
467 {
468 bool isolated = true;
469 for (adjEntry adj : v->adjEntries) {
470 if (adj->twinNode() != v) {
471 isolated = false;
472 break;
473 }
474 }
475
476 return isolated;
477 }
478
biconnectedComponents(const Graph & G,EdgeArray<int> & component,int & nComponent)479 int biconnectedComponents(const Graph &G, EdgeArray<int> &component, int &nComponent)
480 {
481 if (G.empty()) {
482 return 0;
483 }
484
485 NodeArray<int> number(G,0); // discovery times
486 NodeArray<int> lowpt(G); // lowpoints
487 ArrayBuffer<node> called; // already visited nodes
488
489 int nNumber = 0; // number of nodes
490 int nIsolated = 0; // number of isolated nodes
491 nComponent = 0; // number of biconnected components
492
493 // For every unvisited node u:
494 for(node u : G.nodes) {
495 if (number[u] != 0) {
496 continue;
497 }
498
499 if (isIsolated(u)) {
500 ++nIsolated;
501 }
502
503 // We have to simulate the call stack to turn the normally recursive
504 // algorithm into an iterative one. A struct for our stack variables:
505 struct StackElem {
506 node v;
507 node parent;
508 ListPure<adjEntry> *adjEntries;
509
510 StackElem() {}
511 StackElem(node vertex, node father) : v(vertex), parent(father) {
512 adjEntries = new ListPure<adjEntry>;
513 vertex->allAdjEntries(*adjEntries);
514 }
515 } initElem = {u, nullptr};
516
517 // Start a depth-first search at u.
518 ArrayBuffer<StackElem> stack;
519 stack.push(initElem);
520 bool forwards = true;
521
522 while (!stack.empty()) {
523 bool restartLoop = false;
524
525 // Get current node, parent and adjEntries from the stack.
526 StackElem elem = stack.top();
527 node v = elem.v;
528 node parent = elem.parent;
529 ListPure<adjEntry> *adjEntries = elem.adjEntries;
530
531 // If we are continuing the dfs forwards and have a new node:
532 // Note discovery time & initial lowpoint, remember v for later.
533 if (forwards) {
534 lowpt[v] = number[v] = ++nNumber;
535 called.push(v);
536 } else {
537 // If backtracking, update v's lowpt using its visited child w.
538 node w = adjEntries->popFrontRet()->twinNode();
539 if (lowpt[w] < lowpt[v]) {
540 lowpt[v] = lowpt[w];
541 }
542 }
543
544 // For all adjacent nodes w of v:
545 while (!adjEntries->empty() && !restartLoop) {
546 node w = adjEntries->front()->twinNode();
547
548 // If w is unvisited, continue the dfs with w & v as its parent.
549 if (number[w] == 0) {
550 stack.push(StackElem(w,v));
551 forwards = true;
552 restartLoop = true;
553 } else {
554 if (v == w) {
555 // Put self-loops in their own biconnected components.
556 if (adjEntries->front()->isSource()) {
557 component[adjEntries->front()->theEdge()] = nComponent++;
558 }
559 } else {
560 // Else update v's lowpoint.
561 if (number[w] < lowpt[v]) {
562 lowpt[v] = number[w];
563 }
564 }
565 adjEntries->popFront();
566 }
567 }
568
569 if (restartLoop) {
570 continue;
571 }
572
573 // If the parent of v is a cut vertex:
574 // The upwards-going edges of all the nodes below that parent in the
575 // dfs-tree form one biconnected component.
576 if (parent != nullptr && lowpt[v] == number[parent]) {
577 node w;
578 do {
579 w = called.popRet();
580 for (adjEntry adj : w->adjEntries) {
581 if (number[w] > number[adj->twinNode()]) {
582 component[adj->theEdge()] = nComponent;
583 }
584 }
585 } while (w != v);
586 ++nComponent;
587 }
588
589 // v has no more children to visit. Backtrack.
590 stack.pop();
591 forwards = false;
592 delete adjEntries;
593 }
594 }
595
596 return nComponent + nIsolated;
597 }
598
599 /**
600 * Helper function for ogdf::isTwoEdgeConnected
601 * Fills up the output parameters \p dfsOrder, \p prev and \p backEdges, by doing a dfs on the \p graph.
602 *
603 * @param graph input graph
604 * @param dfsOrder the nodes of the graph get pushed into this list
605 * in the order in which they appear in the DFS
606 * @param prev maps to the edge from which a node was entered in the DFS
607 * @param backEdges list all backedges of a node
608 * @return false, if the \p graph is not connected, true otherwise
609 */
dfsTwoEdgeConnected(const Graph & graph,List<node> & dfsOrder,NodeArray<edge> & prev,NodeArray<ArrayBuffer<edge>> & backEdges)610 static bool dfsTwoEdgeConnected(const Graph &graph,
611 List<node> &dfsOrder,
612 NodeArray<edge> &prev,
613 NodeArray<ArrayBuffer<edge>> &backEdges)
614 {
615 dfsOrder.clear();
616 prev.init(graph, nullptr);
617 backEdges.init(graph, ArrayBuffer<edge>());
618 EdgeArray<bool> visited(graph, false);
619
620 struct StackElement {
621 node v;
622 edge e; // edge to v
623 };
624 ArrayBuffer<StackElement> stack;
625 int visitCounter = 0;
626
627 auto push = [&](node vPush, edge ignoredEdge){
628 visitCounter++;
629 dfsOrder.pushBack(vPush);
630 for(adjEntry adj : vPush->adjEntries) {
631 if(adj->theEdge() != ignoredEdge && !visited[adj->theEdge()]) {
632 stack.push(StackElement{adj->twinNode(), adj->theEdge()});
633 }
634 }
635 };
636 push(graph.firstNode(), nullptr);
637
638 while(stack.size() != 0) {
639 StackElement currentElem = stack.popRet();
640 node current = currentElem.v;
641 edge prevEdge = currentElem.e;
642 if(visited[prevEdge]) {
643 continue;
644 }
645 visited[prevEdge] = true;
646 if(prev[current] != nullptr || current == graph.firstNode()) {
647 backEdges[current].push(prevEdge);
648 } else {
649 prev[current] = prevEdge;
650 push(current, prevEdge);
651 }
652 }
653
654 return visitCounter == graph.numberOfNodes();
655 }
656
657 /**
658 * Helper function for ogdf::isTwoEdgeConnected
659 *
660 * @copydetails ogdf::dfsTwoEdgeConnected
661 * @param bridge same as in ogdf::isTwoEdgeConnected
662 */
chainsTwoEdgeConnected(const Graph & graph,edge & bridge,List<node> & dfsOrder,const NodeArray<edge> & prev,const NodeArray<ArrayBuffer<edge>> & backEdges)663 static bool chainsTwoEdgeConnected(const Graph &graph,
664 edge &bridge,
665 List<node> &dfsOrder,
666 const NodeArray<edge> &prev,
667 const NodeArray<ArrayBuffer<edge>> &backEdges)
668 {
669 NodeArray<bool> visited(graph, false);
670 EdgeArray<bool> inAChain(graph, false);
671
672 while(!dfsOrder.empty()) {
673 node current = dfsOrder.popFrontRet();
674 for(edge e : backEdges[current]) {
675 inAChain[e] = true;
676 visited[current] = true;
677 node v = e->opposite(current);
678 while(!visited[v]) {
679 visited[v] = true;
680 edge prevEdge = prev[v];
681 if(prevEdge != nullptr) {
682 v = prevEdge->opposite(v);
683 inAChain[prevEdge] = true;
684 }
685 }
686 }
687 }
688
689 for(edge e : graph.edges) {
690 if(!inAChain[e]){
691 bridge = e;
692 // bridge found
693 return false;
694 }
695 }
696
697 return true;
698 }
699
isTwoEdgeConnected(const Graph & graph,edge & bridge)700 bool isTwoEdgeConnected(const Graph &graph, edge &bridge) {
701 bridge = nullptr;
702 NodeArray<edge> prev(graph, nullptr);
703 NodeArray<ArrayBuffer<edge>> backEdges(graph, ArrayBuffer<edge>());
704 List<node> dfsOrder;
705
706 if(graph.numberOfNodes() <= 1) {
707 // empty and single-node graphs are defined to be 2-edge-connected
708 return true;
709 }
710
711 if(!dfsTwoEdgeConnected(graph, dfsOrder, prev, backEdges)) {
712 // not connected
713 return false;
714 }
715
716 return chainsTwoEdgeConnected(graph, bridge, dfsOrder, prev, backEdges);
717 }
718
719 // Testing triconnectivity
720
isTriconnectedPrimitive(const Graph & G,node & s1,node & s2)721 bool isTriconnectedPrimitive(const Graph &G, node &s1, node &s2)
722 {
723 s1 = s2 = nullptr;
724
725 if (!isConnected(G) || !isBiconnected(G, s1)) {
726 return false;
727 }
728
729 if (G.numberOfNodes() <= 3)
730 return true;
731
732 // make a copy of G
733 GraphCopySimple GC(G);
734
735 // for each node v in G, we test if G \ v is biconnected
736 for(node v : G.nodes)
737 {
738 node vC = GC.copy(v), wC;
739
740 // store adjacent nodes
741 SListPure<node> adjacentNodes;
742 for(adjEntry adj : vC->adjEntries) {
743 wC = adj->twinNode();
744 // forget self-loops (vC would no longer be in GC!)
745 if (wC != vC)
746 adjacentNodes.pushBack(wC);
747 }
748
749 GC.delNode(vC);
750
751 // test for biconnectivity
752 if(!isBiconnected(GC, wC)) {
753 s1 = v; s2 = GC.original(wC);
754 return false;
755 }
756
757 // restore deleted node with adjacent edges
758 vC = GC.newNode(v);
759 for(node uC : adjacentNodes)
760 GC.newEdge(vC,uC);
761 }
762
763 return true;
764 }
765
766
767 // Triangulations
768
triangulate(Graph & G)769 void triangulate(Graph &G)
770 {
771 OGDF_ASSERT(isSimple(G));
772
773 CombinatorialEmbedding E(G);
774
775 #ifdef OGDF_DEBUG
776 E.consistencyCheck();
777 #endif
778
779 adjEntry succ, succ2, succ3;
780 NodeArray<int> marked(E.getGraph(), 0);
781
782 for(node v : E.getGraph().nodes) {
783 marked.init(E.getGraph(), 0);
784
785 for(adjEntry adj : v->adjEntries) {
786 marked[adj->twinNode()] = 1;
787 }
788
789 // forall faces adj to v
790 for(adjEntry adj : v->adjEntries) {
791 succ = adj->faceCycleSucc();
792 succ2 = succ->faceCycleSucc();
793
794 if (succ->twinNode() != v && adj->twinNode() != v) {
795 while (succ2->twinNode() != v) {
796 if (marked[succ2->theNode()] == 1) {
797 // edge e=(x2,x4)
798 succ3 = succ2->faceCycleSucc();
799 E.splitFace(succ, succ3);
800 }
801 else {
802 // edge e=(v=x1,x3)
803 edge e = E.splitFace(adj, succ2);
804 marked[succ2->theNode()] = 1;
805
806 // old adj is in wrong face
807 adj = e->adjSource();
808 }
809 succ = adj->faceCycleSucc();
810 succ2 = succ->faceCycleSucc();
811 }
812 }
813 }
814 }
815 }
816
817
818 // Testing and establishing acyclicity
819
isAcyclic(const Graph & G,List<edge> & backedges)820 bool isAcyclic(const Graph &G, List<edge> &backedges)
821 {
822 backedges.clear();
823
824 NodeArray<int> number(G,0); // discovery times
825 NodeArray<node> parent(G,nullptr); // parents in the dfs tree
826 NodeArray<int> childNr(G); // number of children in the dfs tree
827 ArrayBuffer<node> revS;
828
829 ArrayBuffer<node> leaves; // leaves of the dfs tree
830 NodeArray<int> completion(G,0); // completion times
831 int complCount = 0;
832 int numCount = 0;
833
834 // For all unvisited nodes:
835 for (node v : G.nodes) {
836 if (number[v] == 0) {
837 // Build the dfs-tree starting at v.
838 numCount += buildDfsTree(v, number, parent, childNr, revS, true, numCount+1);
839
840 // Get all leaves of the dfs-tree.
841 while (!revS.empty()) {
842 node w = revS.popRet();
843 if (childNr[w] == 0) {
844 leaves.push(w);
845 }
846 }
847
848 node lastParent = parent[leaves.top()];
849
850 // Go through leaves of the dfs-tree.
851 while (!leaves.empty()) {
852 node w = leaves.top();
853
854 // If the new leaf is a child of the same parent as before,
855 // assign it a completion time and pop it from the stack.
856 if (parent[w] == lastParent) {
857 completion[w] = complCount++;
858 leaves.pop();
859
860 // The last parent has now one child less. If it has no
861 // children anymore, push it as a new leaf on the stack.
862 if (lastParent != nullptr) {
863 childNr[lastParent]--;
864 if (childNr[lastParent] == 0) {
865 leaves.push(lastParent);
866 lastParent = parent[lastParent];
867 }
868 }
869 } else {
870 // Else just continue with the next leaves and their parent.
871 lastParent = parent[w];
872 }
873 }
874 }
875 }
876
877 // Remember backedges.
878 for(edge e : G.edges) {
879 node src = e->source();
880 node tgt = e->target();
881
882 if (number[src] >= number[tgt] && completion[src] <= completion[tgt]) {
883 backedges.pushBack(e);
884 }
885 }
886
887 return backedges.empty();
888 }
889
890
isAcyclicUndirected(const Graph & G,List<edge> & backedges)891 bool isAcyclicUndirected(const Graph &G, List<edge> &backedges)
892 {
893 backedges.clear();
894
895 NodeArray<int> number(G,0); // discovery times
896 NodeArray<node> parent(G,nullptr); // parents in the dfs tree
897 ArrayBuffer<node> S;
898 int numCount = 0;
899
900 // For all unvisited nodes:
901 for (node v : G.nodes) {
902 if (number[v] == 0) {
903 // Start depth first search at v.
904 S.push(v);
905 while (!S.empty()) {
906 node w = S.popRet();
907
908 // Ignore nodes that were already visited.
909 if (number[w] != 0) {
910 continue;
911 }
912
913 // Set correct discovery time for w.
914 number[w] = ++numCount;
915 bool parentSeen = false;
916
917 // For all adjacent nodes u of w:
918 for (adjEntry adj : w->adjEntries) {
919 node u = adj->twinNode();
920
921 // If u has not been visited yet,
922 // push it on the stack and remember its parent.
923 if (number[u] == 0) {
924 S.push(u);
925 parent[u] = w;
926 } else if (parent[w] == u && !parentSeen) {
927 // The first time you see w's parent, it is no backedge.
928 parentSeen = true;
929 } else if (w != u || adj->isSource()) {
930 // Collect backedges (self-loops only in one direction).
931 backedges.pushBack(adj->theEdge());
932 }
933 }
934 }
935 }
936 }
937
938 return backedges.empty();
939 }
940
941
makeAcyclic(Graph & G)942 void makeAcyclic(Graph &G)
943 {
944 List<edge> backedges;
945 isAcyclic(G,backedges);
946
947 for(edge e : backedges)
948 G.delEdge(e);
949 }
950
951
makeAcyclicByReverse(Graph & G)952 void makeAcyclicByReverse(Graph &G)
953 {
954 List<edge> backedges;
955 isAcyclic(G,backedges);
956
957 for(edge e : backedges)
958 if (!e->isSelfLoop()) G.reverseEdge(e);
959 }
960
961
962 // Testing sources and sinks
963
hasSingleSource(const Graph & G,node & s)964 bool hasSingleSource(const Graph& G, node &s)
965 {
966 s = nullptr;
967
968 for(node v : G.nodes) {
969 if (v->indeg() == 0) {
970 if (s != nullptr) {
971 s = nullptr;
972 return false;
973 } else s = v;
974 }
975 }
976 return G.empty() || s != nullptr;
977 }
978
979
hasSingleSink(const Graph & G,node & t)980 bool hasSingleSink(const Graph& G, node &t)
981 {
982 t = nullptr;
983
984 for(node v : G.nodes) {
985 if (v->outdeg() == 0) {
986 if (t != nullptr) {
987 t = nullptr;
988 return false;
989 } else t = v;
990 }
991 }
992 return G.empty() || t != nullptr;
993 }
994
995
996 // isStGraph()
997 // true <=> G is st-graph, i.e., is acyclic, contains exactly one source s
998 // and one sink t, and the edge (s,t); returns single source s and single
999 // sink t if contained (otherwise they are set to 0), and edge st if
1000 // contained (otherwise 0)
isStGraph(const Graph & G,node & s,node & t,edge & st)1001 bool isStGraph(const Graph &G, node &s, node &t, edge &st)
1002 {
1003 st = nullptr;
1004
1005 hasSingleSource(G,s);
1006 hasSingleSink (G,t);
1007
1008 if (s == nullptr || t == nullptr || !isAcyclic(G)) {
1009 s = t = nullptr;
1010 return false;
1011 }
1012
1013 for(adjEntry adj : s->adjEntries) {
1014 edge e = adj->theEdge();
1015
1016 if (e->target() == t) {
1017 st = e;
1018 break;
1019 }
1020 }
1021
1022 return st != nullptr;
1023 }
1024
1025
1026 // Topological numbering in acyclic graphs
1027
topologicalNumbering(const Graph & G,NodeArray<int> & num)1028 void topologicalNumbering(const Graph &G, NodeArray<int> &num)
1029 {
1030 ArrayBuffer<node> S(G.numberOfNodes());
1031 NodeArray<int> indeg(G);
1032
1033 for(node v : G.nodes)
1034 if((indeg[v] = v->indeg()) == 0)
1035 S.push(v);
1036
1037 int count = 0;
1038 while(!S.empty()) {
1039 node v = S.popRet();
1040 num[v] = count++;
1041
1042 for(adjEntry adj : v->adjEntries) {
1043 node u = adj->theEdge()->target();
1044 if (u != v && --indeg[u] == 0) {
1045 S.push(u);
1046 }
1047 }
1048 }
1049 }
1050
strongComponents(const Graph & graph,NodeArray<int> & components)1051 int strongComponents(const Graph &graph, NodeArray<int> &components)
1052 {
1053 int nNodes = graph.numberOfNodes();
1054
1055 if (nNodes == 0) {
1056 return 0;
1057 }
1058
1059 // A node v is on the stack set iff index[v] > -1 and lowLinks[v] < nNodes.
1060 NodeArray<int> lowLinks(graph, -1);
1061 NodeArray<int> index(graph, -1);
1062 ArrayBuffer<node> set(nNodes);
1063 int nextIndex = 0;
1064 int result = 0;
1065
1066 // For every unvisited node u:
1067 for (node u : graph.nodes) {
1068 if (index[u] == -1) {
1069 // We have to simulate the call stack to turn the normally recursive
1070 // algorithm into an iterative one. A struct for our stack variables:
1071 struct StackElem {
1072 node v;
1073 ListPure<edge> *outEdges;
1074
1075 StackElem() = default;
1076 explicit StackElem(node vertex) : v(vertex) {
1077 outEdges = new ListPure<edge>;
1078 vertex->outEdges(*outEdges);
1079 }
1080 } initElem = StackElem(u);
1081
1082 // Start a depth-first search at u.
1083 ArrayBuffer<StackElem> stack;
1084 stack.push(initElem);
1085 bool forwards = true;
1086
1087 while (!stack.empty()) {
1088 bool restartLoop = false;
1089
1090 // Get current node and outEdges from the stack.
1091 StackElem elem = stack.top();
1092 node v = elem.v;
1093 ListPure<edge> *outEdges = elem.outEdges;
1094
1095 // If we are continuing the dfs forwards and have a new node v:
1096 // Note v's index & initial lowlink, and remember it for later.
1097 if (forwards) {
1098 index[v] = lowLinks[v] = nextIndex++;
1099 set.push(v);
1100 } else {
1101 // If backtracking, update v's lowlink using its child w.
1102 node w = outEdges->popFrontRet()->target();
1103 Math::updateMin(lowLinks[v], lowLinks[w]);
1104 }
1105
1106 // For all direct successors w of v:
1107 while (!outEdges->empty() && !restartLoop) {
1108 node w = outEdges->front()->target();
1109
1110 // If w is unvisited, continue the dfs with w.
1111 if (index[w] == -1) {
1112 stack.push(StackElem(w));
1113 forwards = true;
1114 restartLoop = true;
1115 } else {
1116 // Else update v's lowlink.
1117 Math::updateMin(lowLinks[v], lowLinks[w]);
1118 outEdges->popFront();
1119 }
1120 }
1121
1122 if (restartLoop) {
1123 continue;
1124 }
1125
1126 // The nodes collected so far form one component.
1127 if (lowLinks[v] == index[v]) {
1128 node w;
1129 do {
1130 w = set.popRet();
1131 components[w] = result;
1132 lowLinks[w] = nNodes;
1133 } while (w != v);
1134 result++;
1135 }
1136
1137 // v has no more children to visit. Backtrack.
1138 stack.pop();
1139 forwards = false;
1140 delete outEdges;
1141 }
1142 }
1143 }
1144
1145 return result;
1146 }
1147
1148 // makes the DiGraph bimodal such that all embeddings of the
1149 // graph are bimodal embeddings!
makeBimodal(Graph & G,List<edge> & newEdge)1150 void makeBimodal(Graph &G, List<edge> &newEdge)
1151 {
1152 List<node> nodes;
1153 G.allNodes(nodes);
1154
1155 ListIterator<node> it_n = nodes.begin();
1156 while (it_n.valid()) {
1157 node v = *it_n;
1158 if (v->indeg() < 2 || v->outdeg() < 2) {
1159 ++it_n; continue;
1160 }
1161 List<adjEntry> newOrder;
1162 for (adjEntry adj : v->adjEntries) {
1163 if (adj->theEdge()->target() == v)
1164 newOrder.pushFront(adj);
1165 else
1166 newOrder.pushBack(adj);
1167 }
1168 G.sort(v, newOrder);
1169
1170 ListIterator<adjEntry> it = newOrder.begin();
1171 while ((*it)->theEdge()->target() == v)
1172 ++it;
1173 node newNode = G.splitNode(newOrder.front(), *it);
1174 for (adjEntry adj : newNode->adjEntries) {
1175 if (adj->theEdge()->target() == newNode) {
1176 newEdge.pushBack(adj->theEdge());
1177 break;
1178 }
1179 }
1180 ++it_n;
1181 }
1182 }
1183
1184 // Forest and arborescence testing
1185
isArborescenceForest(const Graph & G,List<node> & roots)1186 bool isArborescenceForest(const Graph& G, List<node> &roots)
1187 {
1188 roots.clear();
1189 if (G.empty()) {
1190 return true;
1191 }
1192
1193 if (G.numberOfNodes() <= G.numberOfEdges()) {
1194 return false;
1195 }
1196
1197 ArrayBuffer<node> stack;
1198 int nodeCount = 0;
1199
1200 // Push every source node to roots and start a dfs from it.
1201 for (node u : G.nodes) {
1202 if (u->indeg() == 0) {
1203 roots.pushBack(u);
1204 stack.push(u);
1205
1206 while (!stack.empty()) {
1207 // Get node v from the stack and increase the node counter.
1208 node v = stack.popRet();
1209 nodeCount++;
1210
1211 // Push all successors of v to the stack.
1212 // If one of them has indegree > 1, return false.
1213 for (adjEntry adj : v->adjEntries) {
1214 if (adj->isSource()) {
1215 node w = adj->twinNode();
1216 if (w->indeg() > 1) {
1217 return false;
1218 }
1219 stack.push(w);
1220 }
1221 }
1222 }
1223 }
1224 }
1225
1226 // If there are still unvisited nodes, return false, else true.
1227 return nodeCount == G.numberOfNodes();
1228 }
1229
1230
isArborescence(const Graph & G,node & root)1231 bool isArborescence (const Graph& G, node &root)
1232 {
1233 List<node> roots;
1234
1235 if (isArborescenceForest(G,roots) && roots.size() == 1) {
1236 root = roots.front(); return true;
1237 }
1238 return false;
1239 }
1240
isRegular(const Graph & G)1241 bool isRegular(const Graph& G) {
1242 if (G.numberOfEdges() == 0) {
1243 return true;
1244 }
1245 return isRegular(G, G.firstNode()->degree());
1246 }
1247
isRegular(const Graph & G,int d)1248 bool isRegular(const Graph& G, int d) {
1249 for (auto n: G.nodes) {
1250 if (n->degree() != d) {
1251 return false;
1252 }
1253 }
1254 return true;
1255 }
1256
isBipartite(const Graph & G,NodeArray<bool> & color)1257 bool isBipartite(const Graph &G, NodeArray<bool> &color) {
1258 ArrayBuffer<node> stack;
1259 NodeArray<bool> visited(G, false);
1260
1261 // Start a depth-first search from every unvisited node.
1262 for (node root : G.nodes) {
1263 if (!visited[root]) {
1264 stack.push(root);
1265 color[root] = true;
1266 visited[root] = true;
1267
1268 while (!stack.empty()) {
1269 node v = stack.popRet();
1270
1271 // For all adjacent nodes w of v:
1272 for (adjEntry adj : v->adjEntries) {
1273 node w = adj->twinNode();
1274
1275 // If w is already visited/on the stack, check its color.
1276 if (visited[w]) {
1277 if (color[w] == color[v]) {
1278 return false;
1279 }
1280 } else {
1281 // Else color it and push it on the stack.
1282 visited[w] = true;
1283 color[w] = !color[v];
1284 stack.push(w);
1285 }
1286 }
1287 }
1288 }
1289 }
1290
1291 return true;
1292 }
1293
nodeDistribution(const Graph & G,Array<int> & dist,std::function<int (node)> func)1294 void nodeDistribution(const Graph& G, Array<int> &dist, std::function<int(node)> func) {
1295 int maxval = 0;
1296 int minval = std::numeric_limits<int>::max();
1297
1298 if (G.empty()) {
1299 dist.init();
1300 return;
1301 }
1302
1303 for (node v : G.nodes) {
1304 Math::updateMax(maxval, func(v));
1305 Math::updateMin(minval, func(v));
1306 }
1307
1308 dist.init(minval, maxval, 0);
1309 for (node v : G.nodes) {
1310 ++dist[func(v)];
1311 }
1312 }
1313
1314 }
1315