blob: a5b33b3763d699f52d7a407a48a708a012ede799 [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2014-present 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
Andrey Komarov2398d962016-09-26 15:11:23 +030038 protected SpanningTreeResult internalSearch(Graph<V, E> graph, V src, V dst,
39 EdgeWeigher<V, E> weigher, int maxPaths) {
tom41c3fcc2014-08-30 17:57:15 -070040
41 // Prepare the search result.
Thomas Vachuskac31d9f12015-01-22 12:33:27 -080042 SpanningTreeResult result = new SpanningTreeResult(src, dst, maxPaths);
tom41c3fcc2014-08-30 17:57:15 -070043
44 // The source vertex has cost 0, of course.
Andrey Komarov2398d962016-09-26 15:11:23 +030045 result.updateVertex(src, null, weigher.getInitialWeight(), true);
tom41c3fcc2014-08-30 17:57:15 -070046
47 // Track finished vertexes and keep a stack of vertexes that have been
48 // started; start this stack with the source on it.
49 Set<V> finished = new HashSet<>();
50 Stack<V> stack = new Stack<>();
51 stack.push(src);
52
53 while (!stack.isEmpty()) {
54 V vertex = stack.peek();
55 if (vertex.equals(dst)) {
56 // If we have reached our destination, bail.
57 break;
58 }
59
Andrey Komarov2398d962016-09-26 15:11:23 +030060 Weight cost = result.cost(vertex);
tom41c3fcc2014-08-30 17:57:15 -070061 boolean tangent = false;
62
63 // Visit all egress edges of the current vertex.
64 for (E edge : graph.getEdgesFrom(vertex)) {
65 // If we have seen the edge already, skip it.
66 if (result.isEdgeMarked(edge)) {
67 continue;
68 }
69
70 // Examine the destination of the current edge.
71 V nextVertex = edge.dst();
72 if (!result.hasCost(nextVertex)) {
73 // If this vertex have not finished this vertex yet,
74 // not started it, then start it as a tree-edge.
75 result.markEdge(edge, EdgeType.TREE_EDGE);
Andrey Komarov2398d962016-09-26 15:11:23 +030076 Weight newCost = cost.merge(weigher.weight(edge));
tom41c3fcc2014-08-30 17:57:15 -070077 result.updateVertex(nextVertex, edge, newCost, true);
78 stack.push(nextVertex);
79 tangent = true;
80 break;
81
82 } else if (!finished.contains(nextVertex)) {
83 // We started the vertex, but did not yet finish it, so
84 // it must be a back-edge.
85 result.markEdge(edge, EdgeType.BACK_EDGE);
86 } else {
87 // The target has been finished already, so what we have
88 // here is either a forward-edge or a cross-edge.
89 result.markEdge(edge, isForwardEdge(result, edge) ?
90 EdgeType.FORWARD_EDGE : EdgeType.CROSS_EDGE);
91 }
92 }
93
94 // If we have not been sent on a tangent search and reached the
95 // end of the current scan normally, mark the node as finished
96 // and pop it off the vertex stack.
97 if (!tangent) {
98 finished.add(vertex);
99 stack.pop();
100 }
101 }
102
103 // Finally, but the paths on the search result and return.
104 result.buildPaths();
105 return result;
106 }
107
108 /**
109 * Determines whether the specified edge is a forward edge using the
110 * accumulated set of parent edges for each vertex.
111 *
112 * @param result search result
113 * @param edge edge to be classified
114 * @return true if the edge is a forward edge
115 */
116 protected boolean isForwardEdge(DefaultResult result, E edge) {
117 // Follow the parent edges until we hit the edge source vertex
118 V target = edge.src();
119 V vertex = edge.dst();
120 Set<E> parentEdges;
121 while ((parentEdges = result.parents.get(vertex)) != null) {
122 for (E parentEdge : parentEdges) {
123 vertex = parentEdge.src();
124 if (vertex.equals(target)) {
125 return true;
126 }
127 }
128 }
129 return false;
130 }
131
132 /**
133 * Graph search result which includes edge classification for building
134 * a spanning tree.
135 */
136 public class SpanningTreeResult extends DefaultResult {
137
138 protected final Map<E, EdgeType> edges = new HashMap<>();
139
140 /**
141 * Creates a new spanning tree result.
142 *
Thomas Vachuskac31d9f12015-01-22 12:33:27 -0800143 * @param src search source
144 * @param dst optional search destination
145 * @param maxPaths limit on the number of paths
tom41c3fcc2014-08-30 17:57:15 -0700146 */
Thomas Vachuskac31d9f12015-01-22 12:33:27 -0800147 public SpanningTreeResult(V src, V dst, int maxPaths) {
148 super(src, dst, maxPaths);
tom41c3fcc2014-08-30 17:57:15 -0700149 }
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}