blob: c4d3f83310a3915f8424e46c78fc06830442c7a1 [file] [log] [blame]
tom41c3fcc2014-08-30 17:57:15 -07001package org.onlab.graph;
2
3import java.util.HashMap;
4import java.util.HashSet;
5import java.util.Map;
6import java.util.Set;
7import java.util.Stack;
8
9/**
10 * DFS graph search algorithm implemented via iteration rather than recursion.
11 */
12public class DepthFirstSearch<V extends Vertex, E extends Edge<V>>
13 extends AbstractGraphPathSearch<V, E> {
14
15 /**
16 * Graph edge types as classified by the DFS algorithm.
17 */
18 public static enum EdgeType {
19 TREE_EDGE, FORWARD_EDGE, BACK_EDGE, CROSS_EDGE
20 }
21
22 @Override
23 public SpanningTreeResult search(Graph<V, E> graph, V src, V dst,
24 EdgeWeight<V, E> weight) {
25 checkArguments(graph, src, dst);
26
27 // Prepare the search result.
28 SpanningTreeResult result = new SpanningTreeResult(src, dst);
29
30 // The source vertex has cost 0, of course.
31 result.updateVertex(src, null, 0.0, true);
32
33 // Track finished vertexes and keep a stack of vertexes that have been
34 // started; start this stack with the source on it.
35 Set<V> finished = new HashSet<>();
36 Stack<V> stack = new Stack<>();
37 stack.push(src);
38
39 while (!stack.isEmpty()) {
40 V vertex = stack.peek();
41 if (vertex.equals(dst)) {
42 // If we have reached our destination, bail.
43 break;
44 }
45
46 double cost = result.cost(vertex);
47 boolean tangent = false;
48
49 // Visit all egress edges of the current vertex.
50 for (E edge : graph.getEdgesFrom(vertex)) {
51 // If we have seen the edge already, skip it.
52 if (result.isEdgeMarked(edge)) {
53 continue;
54 }
55
56 // Examine the destination of the current edge.
57 V nextVertex = edge.dst();
58 if (!result.hasCost(nextVertex)) {
59 // If this vertex have not finished this vertex yet,
60 // not started it, then start it as a tree-edge.
61 result.markEdge(edge, EdgeType.TREE_EDGE);
62 double newCost = cost + (weight == null ? 1.0 : weight.weight(edge));
63 result.updateVertex(nextVertex, edge, newCost, true);
64 stack.push(nextVertex);
65 tangent = true;
66 break;
67
68 } else if (!finished.contains(nextVertex)) {
69 // We started the vertex, but did not yet finish it, so
70 // it must be a back-edge.
71 result.markEdge(edge, EdgeType.BACK_EDGE);
72 } else {
73 // The target has been finished already, so what we have
74 // here is either a forward-edge or a cross-edge.
75 result.markEdge(edge, isForwardEdge(result, edge) ?
76 EdgeType.FORWARD_EDGE : EdgeType.CROSS_EDGE);
77 }
78 }
79
80 // If we have not been sent on a tangent search and reached the
81 // end of the current scan normally, mark the node as finished
82 // and pop it off the vertex stack.
83 if (!tangent) {
84 finished.add(vertex);
85 stack.pop();
86 }
87 }
88
89 // Finally, but the paths on the search result and return.
90 result.buildPaths();
91 return result;
92 }
93
94 /**
95 * Determines whether the specified edge is a forward edge using the
96 * accumulated set of parent edges for each vertex.
97 *
98 * @param result search result
99 * @param edge edge to be classified
100 * @return true if the edge is a forward edge
101 */
102 protected boolean isForwardEdge(DefaultResult result, E edge) {
103 // Follow the parent edges until we hit the edge source vertex
104 V target = edge.src();
105 V vertex = edge.dst();
106 Set<E> parentEdges;
107 while ((parentEdges = result.parents.get(vertex)) != null) {
108 for (E parentEdge : parentEdges) {
109 vertex = parentEdge.src();
110 if (vertex.equals(target)) {
111 return true;
112 }
113 }
114 }
115 return false;
116 }
117
118 /**
119 * Graph search result which includes edge classification for building
120 * a spanning tree.
121 */
122 public class SpanningTreeResult extends DefaultResult {
123
124 protected final Map<E, EdgeType> edges = new HashMap<>();
125
126 /**
127 * Creates a new spanning tree result.
128 *
129 * @param src search source
130 * @param dst optional search destination
131 */
132 public SpanningTreeResult(V src, V dst) {
133 super(src, dst);
134 }
135
136 /**
137 * Returns the map of edge type.
138 *
139 * @return edge to edge type bindings
140 */
141 public Map<E, EdgeType> edges() {
142 return edges;
143 }
144
145 /**
146 * Indicates whether or not the edge has been marked with type.
147 *
148 * @param edge edge to test
149 * @return true if the edge has been marked already
150 */
151 boolean isEdgeMarked(E edge) {
152 return edges.containsKey(edge);
153 }
154
155 /**
156 * Marks the edge with the specified type.
157 *
158 * @param edge edge to mark
159 * @param type edge type
160 */
161 void markEdge(E edge, EdgeType type) {
162 edges.put(edge, type);
163 }
164
165 }
166
167}