/*** * Implements the Depth-First Search algorithm in two ways: Iterative and Recursive. * * Provides multiple functions for traversing graphs: * 1. PrintAll(), * 2. VisitAll(Action forEachFunc), * 3. FindFirstMatch(Predicate match). * * The VisitAll() applies a function to every graph node. The FindFirstMatch() function searches the graph for a predicate match. */ using System; using System.Collections.Generic; using DataStructures.Graphs; namespace Algorithms.Graphs { public static class DepthFirstSearcher { /// /// DFS Recursive Helper function. /// Visits the neighbors of a given vertex recusively, and applies the given Action to each one of them. /// private static void _visitNeighbors(T Vertex, ref IGraph Graph, ref Dictionary Parents, Action Action) where T : IComparable { foreach (var adjacent in Graph.Neighbours(Vertex)) { if (!Parents.ContainsKey(adjacent)) { // DFS VISIT NODE Action(adjacent); // Save adjacents parent into dictionary Parents.Add(adjacent, Vertex); // Recusively visit adjacent nodes _visitNeighbors(adjacent, ref Graph, ref Parents, Action); } } } /// /// Recursive DFS Implementation with helper. /// Traverses all the nodes in a graph starting from a specific node, applying the passed action to every node. /// public static void VisitAll(ref IGraph Graph, T StartVertex, Action Action) where T : IComparable { // Check if graph is empty if (Graph.VerticesCount == 0) throw new Exception("Graph is empty!"); // Check if graph has the starting vertex if (!Graph.HasVertex(StartVertex)) throw new Exception("Starting vertex doesn't belong to graph."); var parents = new Dictionary(Graph.VerticesCount); // keeps track of visited nodes and tree-edges foreach (var vertex in Graph.Neighbours(StartVertex)) { if (!parents.ContainsKey(vertex)) { // DFS VISIT NODE Action(vertex); // Add to parents dictionary parents.Add(vertex, null); // Visit neighbors using recusrive helper _visitNeighbors(vertex, ref Graph, ref parents, Action); } } } /// /// Iterative DFS Implementation. /// Given a starting node, dfs the graph and print the nodes as they get visited. /// public static void PrintAll(IGraph Graph, T StartVertex) where T : IComparable { // Check if graph is empty if (Graph.VerticesCount == 0) throw new Exception("Graph is empty!"); // Check if graph has the starting vertex if (!Graph.HasVertex(StartVertex)) throw new Exception("Starting vertex doesn't belong to graph."); var visited = new HashSet(); var stack = new Stack(Graph.VerticesCount); stack.Push(StartVertex); while (stack.Count > 0) { var current = stack.Pop(); if (!visited.Contains(current)) { // DFS VISIT NODE STEP Console.Write(String.Format("({0}) ", current)); visited.Add(current); // Get the adjacent nodes of current foreach (var adjacent in Graph.Neighbours(current)) if (!visited.Contains(adjacent)) stack.Push(adjacent); } } } /// /// Iterative DFS Implementation. /// Given a predicate function and a starting node, this function searches the nodes of the graph for a first match. /// public static T FindFirstMatch(IGraph Graph, T StartVertex, Predicate Match) where T : IComparable { // Check if graph is empty if (Graph.VerticesCount == 0) throw new Exception("Graph is empty!"); // Check if graph has the starting vertex if (!Graph.HasVertex(StartVertex)) throw new Exception("Starting vertex doesn't belong to graph."); var stack = new Stack(); var parents = new Dictionary(Graph.VerticesCount); // keeps track of visited nodes and tree-edges object currentParent = null; stack.Push(StartVertex); while (stack.Count > 0) { var current = stack.Pop(); // Skip loop if node was already visited if (!parents.ContainsKey(current)) { // Save its parent into the dictionary // Mark it as visited parents.Add(current, currentParent); // DFS VISIT NODE STEP if (Match(current)) return current; // Get currents adjacent nodes (might add already visited nodes). foreach (var adjacent in Graph.Neighbours(current)) if (!parents.ContainsKey(adjacent)) stack.Push(adjacent); // Mark current as the father of its adjacents. This helps keep track of tree-nodes. currentParent = current; } }//end-while throw new Exception("Item was not found!"); } } }