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