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