public static IWeightedGraph <TV, TW> FindTour <TV, TW>(IWeightedGraph <TV, TW> graph, IVertex <TV> source) where TV : IEquatable <TV> { GraphBuilder <TV, TW> builder = new GraphBuilder <TV, TW>(); // Starting point IVertex <TV> currentVertex = source; builder.AddVertex(source.Value); // Track which verteces have already been added to the tour, and how many edges have already been used HashSet <TV> visitedVerteces = new HashSet <TV>() { source.Value }; int edgesUsed = 0; // Find and use minimum non-used edge leading from each vertex while (edgesUsed < graph.VertexCount) { // Last edge, select edge back to start if (edgesUsed == graph.VertexCount - 1) { try { IWeightedEdge <TV, TW> closingEdge = graph.GetEdgeBetweenVerteces(currentVertex, source); builder.AddEdge(currentVertex.Value, source.Value, closingEdge.Weight); } catch (VertecesNotConnectedException <TV> exception) { throw new GraphNotCompleteException("The NearestNeighbor algorithm expects the input graph to be complete!", exception); } } else { IWeightedEdge <TV, TW> minEdge = graph.GetEdgesOfVertex(currentVertex) .Where(e => !visitedVerteces.Contains(e.ConnectedVertex(currentVertex).Value)) .OrderBy(e => e.Weight) .FirstOrDefault(); if (minEdge == null) { throw new GraphNotCompleteException("The NearestNeighbor algorithm expects the input graph to be complete!"); } // Get target vertex and update current vertex IVertex <TV> target = minEdge.ConnectedVertex(currentVertex); currentVertex = target; // Update tracking visitedVerteces.Add(target.Value); // Update result graph builder.AddVertex(target.Value); builder.AddEdge(source.Value, target.Value, minEdge.Weight); } edgesUsed++; } // Done: used n-1 edges for n verteces return(builder.Build()); }
public static IWeightedGraph <TV, TW> FindShortestPath <TV, TW>(IWeightedGraph <TV, TW> graph, IVertex <TV> source, IVertex <TV> target, TW zeroValue, TW maxValue, Func <TW, TW, TW> combineCosts) where TV : IEquatable <TV> where TW : IComparable { Dictionary <IVertex <TV>, TW> distance = new Dictionary <IVertex <TV>, TW>(); Dictionary <IVertex <TV>, IWeightedEdge <TV, TW> > predecessor = new Dictionary <IVertex <TV>, IWeightedEdge <TV, TW> >(); // Initialize distance and predecessor maps foreach (IVertex <TV> vertex in graph.GetAllVerteces()) { distance.Add(vertex, maxValue); predecessor.Add(vertex, null); } distance[source] = zeroValue; // Relax edges repeatedly IEnumerable <IWeightedEdge <TV, TW> > edges = graph.GetAllEdges(); for (int i = 0; i < graph.VertexCount - 1; i++) { foreach (IWeightedEdge <TV, TW> edge in edges) { if (graph.IsDirected) { try { IWeightedDirectedEdge <TV, TW> directedEdge = (IWeightedDirectedEdge <TV, TW>)edge; RelaxEdge(distance, predecessor, directedEdge.OriginVertex, directedEdge.TargetVertex, directedEdge.Weight, directedEdge, combineCosts); } catch { throw new Exception("Failed casting edge to directed edge for directed graph."); } } else { IVertex <TV> sourceVertex = edge.Verteces.First(); IVertex <TV> targetVertex = edge.ConnectedVertex(sourceVertex); RelaxEdge(distance, predecessor, sourceVertex, targetVertex, edge.Weight, edge, combineCosts); RelaxEdge(distance, predecessor, targetVertex, sourceVertex, edge.Weight, edge, combineCosts); } } } // Check for negative - weight cycles foreach (IWeightedEdge <TV, TW> edge in graph.GetAllEdges()) { if (graph.IsDirected) { IWeightedDirectedEdge <TV, TW> directedEdge = (IWeightedDirectedEdge <TV, TW>)edge; CheckForNegativeCycles(distance, directedEdge.OriginVertex, directedEdge.TargetVertex, directedEdge.Weight, combineCosts); } else { IVertex <TV> sourceVertex = edge.Verteces.First(); IVertex <TV> targetVertex = edge.ConnectedVertex(sourceVertex); CheckForNegativeCycles(distance, sourceVertex, targetVertex, edge.Weight, combineCosts); CheckForNegativeCycles(distance, targetVertex, sourceVertex, edge.Weight, combineCosts); } } // Build and output path IVertex <TV> currentVertex = target; GraphBuilder <TV, TW> builder = new GraphBuilder <TV, TW>(true).AddVertex(target.Value); while (currentVertex != source) { IWeightedEdge <TV, TW> pathEdge = predecessor[currentVertex]; IVertex <TV> previousVertex = pathEdge.ConnectedVertex(currentVertex); builder .AddVertex(previousVertex.Value) .AddEdge(previousVertex.Value, currentVertex.Value, pathEdge.Weight); currentVertex = previousVertex; } return(builder.Build()); }
public static IWeightedGraph <TV, TW> FindShortestPath <TV, TW>(IWeightedGraph <TV, TW> graph, IVertex <TV> source, IVertex <TV> target, TW zeroValue, TW maxValue, Func <TW, TW, TW> combineCosts) where TV : IEquatable <TV> where TW : IComparable { HashSet <IVertex <TV> > visitedVerteces = new HashSet <IVertex <TV> >(); IPriorityQueue <IVertex <TV>, TW> vertecesToVisit = new NaivePriorityQueue <IVertex <TV>, TW>(); Dictionary <IVertex <TV>, IWeightedEdge <TV, TW> > predecessor = new Dictionary <IVertex <TV>, IWeightedEdge <TV, TW> >(); // Initialize queue and predecessor map foreach (IVertex <TV> vertex in graph.GetAllVerteces()) { vertecesToVisit.Enqueue(vertex, maxValue); predecessor.Add(vertex, null); } vertecesToVisit.UpdatePriority(source, zeroValue); // Continue as long as there are verteces to visit IVertex <TV> currentVertex = null; TW currentCosts = zeroValue; while (!vertecesToVisit.Empty) { // Get the closest next vertex (currentVertex, currentCosts) = vertecesToVisit.Dequeue(); // Check whether we reached our target if (currentVertex == target) { break; } visitedVerteces.Add(currentVertex); // If not, discover the next verteces that can be visited foreach (IWeightedEdge <TV, TW> edge in graph.GetEdgesOfVertex(currentVertex)) { if (edge.Weight.CompareTo(zeroValue) < 0) { throw new NegativeEdgeWeightException(); } IVertex <TV> connectedVertex = edge.ConnectedVertex(currentVertex); if (!visitedVerteces.Contains(connectedVertex)) { TW newCosts = combineCosts(currentCosts, edge.Weight); if (newCosts.CompareTo(vertecesToVisit.GetPriorityOf(connectedVertex)) < 0) { vertecesToVisit.UpdatePriority(connectedVertex, newCosts); predecessor[connectedVertex] = edge; } } } } // If we reached the target vertex, build result graph if (currentVertex == target) { GraphBuilder <TV, TW> builder = new GraphBuilder <TV, TW>(true).AddVertex(target.Value); while (currentVertex != source) { IWeightedEdge <TV, TW> pathEdge = predecessor[currentVertex]; IVertex <TV> previousVertex = pathEdge.ConnectedVertex(currentVertex); builder .AddVertex(previousVertex.Value) .AddEdge(previousVertex.Value, currentVertex.Value, pathEdge.Weight); currentVertex = previousVertex; } return(builder.Build()); } else { throw new VertexNotReachableException <TV>(target); } }
/// <summary> /// Builds the minimum spanning tree of a given graph. /// </summary> /// <typeparam name="TV">Type of the graph vertex values.</typeparam> /// <typeparam name="TW">Type of the graph edge weights.</typeparam> /// <param name="graph">The graph got which to build the minimum spanning tree.</param> /// <returns>Returns the minimum spanning tree of the given graph.</returns> public static IWeightedGraph <TV, TW> FindMinimumSpanningTree <TV, TW>(IWeightedGraph <TV, TW> graph, TW min, TW max) where TV : IEquatable <TV> where TW : IComparable { // Builder used to incrementally build the target minimum spanning tree GraphBuilder <TV, TW> builder = new GraphBuilder <TV, TW>(); // Get all verteces and edges of graph IEnumerable <IVertex <TV> > verteces = graph.GetAllVerteces(); IEnumerable <IWeightedEdge <TV, TW> > edges = graph.GetAllEdges(); // TODO: replace with more performant priority queue implementation // Track minimum known cost to connect to target vertex IPriorityQueue <TV, TW> vertexCosts = new NaivePriorityQueue <TV, TW>(); // Track target vertex and edge connecting to target vertex with minimum known cost Dictionary <TV, IVertex <TV> > vertexValue = new Dictionary <TV, IVertex <TV> >(); Dictionary <TV, IWeightedEdge <TV, TW> > vertexEdges = new Dictionary <TV, IWeightedEdge <TV, TW> >(); // Initialize vertex costs with all costs set to max foreach (IVertex <TV> vertex in verteces) { vertexCosts.Enqueue(vertex.Value, max); vertexValue.Add(vertex.Value, vertex); vertexEdges.Add(vertex.Value, null); } // Set starting vertex vertexCosts.UpdatePriority(verteces.First().Value, min); // While there are verteces left to add, select vertex with smallest cost while (!vertexCosts.Empty) { // Get and remove vertex with smallest cost TV minCost = vertexCosts.Dequeue().Item1; // For easier handling, get vertex and edge IVertex <TV> vertex = vertexValue[minCost]; IWeightedEdge <TV, TW> edge = vertexEdges[minCost]; // Add vertex and edge to target MST builder.AddVertex(vertex.Value); if (edge != null) { builder.AddEdge(vertex.Value, edge.ConnectedVertex(vertex.Value).Value, edge.Weight); } // Update vertex cost for adjacent verteces and store edges foreach (IWeightedEdge <TV, TW> connectedEdge in graph.GetEdgesOfVertex(vertex)) { // Ignore edges leading to verteces already added to the MST IVertex <TV> targetVertex = connectedEdge.ConnectedVertex(vertex); if (vertexCosts.Contains(targetVertex.Value) && connectedEdge.Weight.CompareTo(vertexCosts.GetPriorityOf(targetVertex.Value)) < 0) { vertexCosts.UpdatePriority(targetVertex.Value, connectedEdge.Weight); vertexEdges[targetVertex.Value] = connectedEdge; } } } // All verteces added to MST - done! return(builder.Build()); }