/// <summary> /// Get all exiting edges from startNode /// </summary> /// <param name="startNode"> Start node to find all edges from </param> /// <returns> an array of edges which are adjacent to the startNode</returns> public Edge[] GetAdjacentNodes(Node startNode) { return (from edge in Edges where edge.Start.NodeName == startNode.NodeName select edge).ToArray<Edge>(); }
/// <summary> /// Finds all cycles starting (and ending) at Node start, with a maxLength over the cycle /// Because this does a very brute force approach, be smart about how large of a graph and maxLength given /// Runtime will be something like (big "O") O(NumEdges ^ (maxLength/MinLength)) /// </summary> /// <param name="start"> Node start (and end) of the cycle. </param> /// <param name="maxLength"> int maximum length of the cycles to find </param> /// <returns> The number of cycles found </returns> public int CyclesShorterThan(Node start, int maxLength) { // This should use the bellman ford algorithm // But, given all edges are atleast 1 unit long, we can just get all 30/Min(Edge Lengths) step routes, // filtering out the ones which are longer thand 30 cost and those which dont end on the start node (non-cycles) // inefficient computationally, but saves coding time :) // Find the smallest edge var minEdge = (from edge in RouteMap.Edges orderby edge.Distance select edge.Distance).First(); // Find all routes with a number of edges of maxLength/minEdge // ie, MaxLength = 25 and MinEdge = 5, we can find upto 5 stop routes. var routes = getSteps(start, maxLength / minEdge, false); // filter out the ones which dont end on our start (non-cycles) var cycles = (from route in routes where route[route.Length - 1] == start.NodeName where route.Length > 2 // Must have more than just the starting location where RouteDistance(route) <= maxLength select route).Distinct().ToList(); ; return cycles.Count; }
///<summary> /// Takes a string to convert to a graph according to instructions /// Utilizes fluent interface ///</summary> ///<param name="input"> the source string to build the graph from</param> public GraphBuilder BuildFromString(string input) { // General pattern expected. string pattern = "([A-E][A-E][0-9]{1,9}(( )?,( )?)?)+"; // Disregard Malformed input, you wiley recruiters you... Match match = Regex.Match(input, pattern); // If you're not being tricksy if (!match.Success) { Console.WriteLine("Malformed input detected. Aborting."); return null; } // Split on the comma pattern = "[\\s]*[,][\\s]*"; String[] tokens = Regex.Split(input, pattern); Dictionary<char, Node> nodeDictionary = new Dictionary<char, Node>(tokens.Count()); foreach (String token in tokens) { Node node1 = new Node(token[0]); Node node2 = new Node(token[1]); // Ensure we dont add nodes more than once. if (!nodeDictionary.ContainsKey(token[0])) { nodeDictionary.Add(token[0], node1); } if (!nodeDictionary.ContainsKey(token[1])) { nodeDictionary.Add(token[1], node2); } nodes = nodeDictionary.Values.ToList(); // Dont have to check edges so stringently // Email said: "For a given route will never appear more than once, and Node1 will != Node2" edges.Add(new Edge(node1, node2, int.Parse(token.Substring(2)))); } //Save the source of this graph. source = input; return this; }
/// <summary> /// Returns a path starting at start, a maximum depth (number of stops) of maxDepth /// will only return strings of exactly maxDepth if exactLentgh is true ///</summary> /// /// <param name="start"> The starting node for the route </param> /// <param name="maxDepth"> The maximum length (number of stops) of the route </param> /// <param name="exactLength"> return only exaclt maxDepth sized routes? </param> /// <returns> A list of routes that match the invoked criteria </returns> public List<string> getSteps(Node start, int maxDepth, bool exactLength) { // So far returning nothing. List<string> retVal = new List<string>(); if (maxDepth == 0) { // Stop here and Return. retVal.Add(start.ToString()); return retVal; } // Need to go deeper! get Adjacencies Edge[] adjacent = RouteMap.GetAdjacentNodes(start); foreach (Edge edge in adjacent) { // Step deeper to build the path List<string> subGraphs = getSteps(edge.End, maxDepth - 1, exactLength); // For each Subgraph, mark that we were "here" foreach (string graph in subGraphs) { retVal.Add(start.NodeName + "-" + graph); } // Add this < maxDepth length path if (!exactLength) { retVal.Add(edge.Start.NodeName.ToString()); } } return retVal; }
/// <summary> /// Implements a modified Djikstra's Algorithm to find Min routes from Node start /// Also has the ability to search for a Cycle if bool findCycle is true /// </summary> /// <param name="start">Node to start search from, also the end node if finding cycles</param> /// <param name="findCycle"> Should we find cycles or just find all other nodes?</param> /// <returns> Returns a dictionary giving the distance and route from Start to the key in the dictionary</returns> public Dictionary<char, Tuple<int, string>> GetMinRoutes(Node start, bool findCycle) { // So that we dont count it a million times var count = graph.Nodes.Count; // We want to keep track of distances, which we've visited, routes. And eventually return a formated value Dictionary<char, int> distances = new Dictionary<char, int>(count); Dictionary<char, bool> visited = new Dictionary<char, bool>(count); Dictionary<char, string> routes = new Dictionary<char, string>(count); Dictionary<char, Tuple<int, string>> retVal = new Dictionary<char, Tuple<int, string>>(count); // Initialize to default values foreach (Node n in graph.Nodes) { distances.Add(n.NodeName, Int32.MaxValue); routes.Add(n.NodeName, start.NodeName.ToString()); } // We're not coming back here, so assume distance of 0 if (!findCycle) { distances[start.NodeName] = 0; } // Copy the Edges list so this function doesnt have side-effects var edges = graph.Edges.ToList(); // Start from our start position. var current = start; // We want to visit every node if possible (see "break;" below) while (visited.Keys.Count < count) { // Skip adding the start node once, so that we'll return to it again. if (findCycle) { // But from now on, track which we've visited findCycle = false; } else { visited.Add(current.NodeName, true); } // Check each edge for a better route foreach (Edge edge in graph.GetAdjacentNodes(current)) { // Never check this edge again. edges.Remove(edge); // If we dont have a route, or if we've found a better route if (distances[edge.End.NodeName] == Int32.MaxValue || distances[current.NodeName] + edge.Distance < distances[edge.End.NodeName]) { if (current.NodeName == start.NodeName) { distances[edge.End.NodeName] = edge.Distance; } else { distances[edge.End.NodeName] = distances[current.NodeName] + edge.Distance; } routes[edge.End.NodeName] = routes[current.NodeName] + "-" + edge.End; } } // Figure out all nodes which are adjacent to the currently visited nodes List<char> adjacencies = new List<char>(); adjacencies = adjacencies.Union( graph.GetAdjacentNodes(current) .Select(edge => edge.End.NodeName)).ToList(); foreach (char n in visited.Keys) { adjacencies = adjacencies.Union( graph.GetAdjacentNodes(new Node(n)) .Select(edge => edge.End.NodeName)).ToList(); } // Select the node with the lowest distance, and hasnt been visited current = (from KeyValuePair<char, int> pair in distances where pair.Key != current.NodeName where !visited.Keys.Contains(pair.Key) where adjacencies.Contains(pair.Key) orderby pair.Value select new Node(pair.Key)).DefaultIfEmpty(null).First(); // There are maybe nodes left, but they're not accessible if (current == null) { break; } } // Build our return value. foreach (KeyValuePair<char, string> pair in routes) { retVal.Add(pair.Key, new Tuple<int, string>(distances[pair.Key], pair.Value)); } return retVal; }
/// <summary> /// Constructor /// </summary> /// <param name="start">starting point of this directed edge</param> /// <param name="end">end point</param> /// <param name="distance">Distance or cost of this edge</param> public Edge(Node start, Node end, int distance) { Start = start; End = end; Distance = distance; }