/*** * Computes Shortest-Paths for Unweighted Graphs using the Breadth-First Search algorithm. * It provides the capability to find shortest-paths from a single-source and multiple-sources, in addition to looking up reachable and unreachable nodes. */ using System; using System.Diagnostics; using System.Collections.Generic; using Algorithms.Common; using DataStructures.Graphs; namespace Algorithms.Graphs { public class BreadthFirstShortestPaths<T> where T : IComparable<T> { private int _edgesCount { get; set; } private int _verticesCount { get; set; } private bool[] _visited { get; set; } private Int64[] _distances { get; set; } private int[] _predecessors { get; set; } // A dictionary that maps node-values to integer indeces private Dictionary<T, int> _nodesToIndices { get; set; } // A dictionary that maps integer index to node-value private Dictionary<int, T> _indicesToNodes { get; set; } // A const that represent an infinite distance private const Int64 INFINITY = Int64.MaxValue; /// <summary> /// CONSTRUCTOR. /// Breadth First Searcher from Single Source. /// </summary> public BreadthFirstShortestPaths(IGraph<T> Graph, T Source) { if (Graph == null) throw new ArgumentNullException(); if (!Graph.HasVertex(Source)) throw new ArgumentException("The source vertex doesn't belong to graph."); // Init _initializeDataMembers(Graph); // Single source BFS _breadthFirstSearch(Graph, Source); //bool optimalityConditionsSatisfied = checkOptimalityConditions (Graph, Source); Debug.Assert(checkOptimalityConditions(Graph, Source)); } /// <summary> /// CONSTRUCTOR. /// Breadth First Searcher from Multiple Sources. /// </summary> public BreadthFirstShortestPaths(IGraph<T> Graph, IList<T> Sources) { if (Graph == null) throw new ArgumentNullException(); if (Sources == null || Sources.Count == 0) throw new ArgumentException("Sources list is either null or empty."); // Init _initializeDataMembers(Graph); // Multiple sources BFS _breadthFirstSearch(Graph, Sources); } /************************************************************************************************************/ /// <summary> /// Constructors helper function. Initializes some of the data memebers. /// </summary> private void _initializeDataMembers(IGraph<T> Graph) { _edgesCount = Graph.EdgesCount; _verticesCount = Graph.VerticesCount; _visited = new bool[_verticesCount]; _distances = new Int64[_verticesCount]; _predecessors = new int[_verticesCount]; _nodesToIndices = new Dictionary<T, int>(); _indicesToNodes = new Dictionary<int, T>(); // Reset the visited, distances and predeccessors arrays int i = 0; foreach (var node in Graph.Vertices) { if (i >= _verticesCount) break; _visited[i] = false; _distances[i] = INFINITY; _predecessors[i] = -1; _nodesToIndices.Add(node, i); _indicesToNodes.Add(i, node); ++i; } } /// <summary> /// Privat helper. Breadth First Search from Single Source. /// </summary> private void _breadthFirstSearch(IGraph<T> graph, T source) { // Set distance to current to zero _distances[_nodesToIndices[source]] = 0; // Set current to visited: true. _visited[_nodesToIndices[source]] = true; var queue = new Queue<T>(_verticesCount); queue.Enqueue(source); while (queue.Count > 0) { var current = queue.Dequeue(); int indexOfCurrent = _nodesToIndices[current]; foreach (var adjacent in graph.Neighbours(current)) { int indexOfAdjacent = _nodesToIndices[adjacent]; if (!_visited[indexOfAdjacent]) { _predecessors[indexOfAdjacent] = indexOfCurrent; _distances[indexOfAdjacent] = _distances[indexOfCurrent] + 1; _visited[indexOfAdjacent] = true; queue.Enqueue(adjacent); } }//end-foreach }//end-while } /// <summary> /// Privat helper. Breadth First Search from Multiple Sources. /// </summary> private void _breadthFirstSearch(IGraph<T> graph, IList<T> sources) { // Define helper variables. var queue = new Queue<T>(_verticesCount); foreach (var source in sources) { if (!graph.HasVertex(source)) throw new Exception("Graph doesn't has a vertex '" + source + "'"); int index = _nodesToIndices[source]; _distances[index] = 0; _visited[index] = true; queue.Enqueue(source); } while (queue.Count > 0) { var current = queue.Dequeue(); int indexOfCurrent = _nodesToIndices[current]; foreach (var adjacent in graph.Neighbours(current)) { int indexOfAdjacent = _nodesToIndices[adjacent]; if (!_visited[indexOfAdjacent]) { _predecessors[indexOfAdjacent] = indexOfCurrent; _distances[indexOfAdjacent] = _distances[indexOfCurrent] + 1; _visited[indexOfAdjacent] = true; queue.Enqueue(adjacent); } }//end-foreach }//end-while } /// <summary> /// Private helper. Checks optimality conditions for single source /// </summary> private bool checkOptimalityConditions(IGraph<T> graph, T source) { int indexOfSource = _nodesToIndices[source]; // check that the distance of s = 0 if (_distances[indexOfSource] != 0) { Console.WriteLine("Distance of source '" + source + "' to itself = " + _distances[indexOfSource]); return false; } // check that for each edge v-w dist[w] <= dist[v] + 1 // provided v is reachable from s foreach (var node in graph.Vertices) { int v = _nodesToIndices[node]; foreach (var adjacent in graph.Neighbours(node)) { int w = _nodesToIndices[adjacent]; if (HasPathTo(node) != HasPathTo(adjacent)) { Console.WriteLine("edge " + node + "-" + adjacent); Console.WriteLine("hasPathTo(" + node + ") = " + HasPathTo(node)); Console.WriteLine("hasPathTo(" + adjacent + ") = " + HasPathTo(adjacent)); return false; } if (HasPathTo(node) && (_distances[w] > _distances[v] + 1)) { Console.WriteLine("edge " + node + "-" + adjacent); Console.WriteLine("distanceTo[" + node + "] = " + _distances[v]); Console.WriteLine("distanceTo[" + adjacent + "] = " + _distances[w]); return false; } } } // check that v = edgeTo[w] satisfies distTo[w] + distTo[v] + 1 // provided v is reachable from source foreach (var node in graph.Vertices) { int w = _nodesToIndices[node]; if (!HasPathTo(node) || node.IsEqualTo(source)) continue; int v = _predecessors[w]; if (_distances[w] != _distances[v] + 1) { Console.WriteLine("shortest path edge " + v + "-" + w); Console.WriteLine("distanceTo[" + v + "] = " + _distances[v]); Console.WriteLine("distanceTo[" + w + "] = " + _distances[w]); return false; } } return true; } /************************************************************************************************************/ /// <summary> /// Determines whether there is a path from the source vertex to this specified vertex. /// </summary> public bool HasPathTo(T destination) { if (!_nodesToIndices.ContainsKey(destination)) throw new Exception("Graph doesn't have the specified vertex."); int dstIndex = _nodesToIndices[destination]; return (_visited[dstIndex]); } /// <summary> /// Returns the distance between the source vertex and the specified vertex. /// </summary> public long DistanceTo(T destination) { if (!_nodesToIndices.ContainsKey(destination)) throw new Exception("Graph doesn't have the specified vertex."); int dstIndex = _nodesToIndices[destination]; return (_distances[dstIndex]); } /// <summary> /// Returns an enumerable collection of nodes that specify the shortest path from the source vertex to the destination vertex. /// </summary> public IEnumerable<T> ShortestPathTo(T destination) { if (!_nodesToIndices.ContainsKey(destination)) throw new Exception("Graph doesn't have the specified vertex."); if (!HasPathTo(destination)) return null; int dstIndex = _nodesToIndices[destination]; var stack = new DataStructures.Lists.Stack<T>(); int index; for (index = dstIndex; _distances[index] != 0; index = _predecessors[index]) stack.Push(_indicesToNodes[index]); // Push the source vertex stack.Push(_indicesToNodes[index]); return stack; } } }