blob: 85a089103acab5f4064de3b3f0dcb05fd713cd14 [file] [log] [blame]
Thomas Vachuska24c849c2014-10-27 09:53:05 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2014-present Open Networking Foundation
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
Ray Milkey9a4f7ce2017-12-14 15:38:16 -080018import java.util.Deque;
tom41c3fcc2014-08-30 17:57:15 -070019import java.util.HashMap;
20import java.util.HashSet;
Ray Milkey9a4f7ce2017-12-14 15:38:16 -080021import java.util.LinkedList;
tom41c3fcc2014-08-30 17:57:15 -070022import java.util.Map;
23import java.util.Set;
tom41c3fcc2014-08-30 17:57:15 -070024
25/**
26 * DFS graph search algorithm implemented via iteration rather than recursion.
27 */
28public class DepthFirstSearch<V extends Vertex, E extends Edge<V>>
29 extends AbstractGraphPathSearch<V, E> {
30
31 /**
32 * Graph edge types as classified by the DFS algorithm.
33 */
34 public static enum EdgeType {
35 TREE_EDGE, FORWARD_EDGE, BACK_EDGE, CROSS_EDGE
36 }
37
38 @Override
Andrey Komarov2398d962016-09-26 15:11:23 +030039 protected SpanningTreeResult internalSearch(Graph<V, E> graph, V src, V dst,
40 EdgeWeigher<V, E> weigher, int maxPaths) {
tom41c3fcc2014-08-30 17:57:15 -070041
42 // Prepare the search result.
Thomas Vachuskac31d9f12015-01-22 12:33:27 -080043 SpanningTreeResult result = new SpanningTreeResult(src, dst, maxPaths);
tom41c3fcc2014-08-30 17:57:15 -070044
45 // The source vertex has cost 0, of course.
Andrey Komarov2398d962016-09-26 15:11:23 +030046 result.updateVertex(src, null, weigher.getInitialWeight(), true);
tom41c3fcc2014-08-30 17:57:15 -070047
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<>();
Ray Milkey9a4f7ce2017-12-14 15:38:16 -080051 Deque<V> stack = new LinkedList<>();
tom41c3fcc2014-08-30 17:57:15 -070052 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
Andrey Komarov2398d962016-09-26 15:11:23 +030061 Weight cost = result.cost(vertex);
tom41c3fcc2014-08-30 17:57:15 -070062 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);
Andrey Komarov2398d962016-09-26 15:11:23 +030077 Weight newCost = cost.merge(weigher.weight(edge));
tom41c3fcc2014-08-30 17:57:15 -070078 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 *
Thomas Vachuskac31d9f12015-01-22 12:33:27 -0800144 * @param src search source
145 * @param dst optional search destination
146 * @param maxPaths limit on the number of paths
tom41c3fcc2014-08-30 17:57:15 -0700147 */
Thomas Vachuskac31d9f12015-01-22 12:33:27 -0800148 public SpanningTreeResult(V src, V dst, int maxPaths) {
149 super(src, dst, maxPaths);
tom41c3fcc2014-08-30 17:57:15 -0700150 }
151
152 /**
153 * Returns the map of edge type.
154 *
155 * @return edge to edge type bindings
156 */
157 public Map<E, EdgeType> edges() {
158 return edges;
159 }
160
161 /**
162 * Indicates whether or not the edge has been marked with type.
163 *
164 * @param edge edge to test
165 * @return true if the edge has been marked already
166 */
167 boolean isEdgeMarked(E edge) {
168 return edges.containsKey(edge);
169 }
170
171 /**
172 * Marks the edge with the specified type.
173 *
174 * @param edge edge to mark
175 * @param type edge type
176 */
177 void markEdge(E edge, EdgeType type) {
178 edges.put(edge, type);
179 }
180
181 }
182
183}