Skip to main content
Fixed improper use of blockquotes
Source Link
200_success
  • 144.2k
  • 22
  • 188
  • 473

Node class

Node class

Edge class

Edge class

QueueItem class

QueueItem class

Program class

Program class

Node class

Edge class

QueueItem class

Program class

Node class

Edge class

QueueItem class

Program class

Rollback to Revision 5
Source Link
RubberDuck
  • 30.9k
  • 6
  • 71
  • 174

Here is my first attempt at refactoring. Instead of using a Tuple, I used a STRUCT and this made it more readable. I also extracted printing paths and finding paths into interfaces so someone could print their own messages or implement there own path finding algorithm. The graph class has a corresponding interface as well. I am still not sure if I need all the path finding methods, some of them are just one liners, so I wonder if they are necessary. I also want to pass a file name as an args parameter into main, but I am not sure how to get that to my CreateGraph method. Also, IGraph graph = new Graph().CreateGraph(); seems a bit weird. Is this normal? I am also curious if there is a way to better pass in messages to my PrintXXXPath... methods, so I don't have to have so many of them?

Node class

Any other refactorings are welcome?

public class Node
{
    public string Name { get; private set; }
    public List<Edge> Edges { get; private set; }

    public Node(string name)
    {
        Name = name;
        Edges = new List<Edge>();
    }

    public void AddEdge(Node targetNode, double weight)
    {
        Edges.Add(new Edge(targetNode,weight));
    }
}

Edge class

public class Edge
{
    public Node TargetNode { get; private set; }
    public double Weight { get; private set; }

    public Edge(Node targetNode, double weight)
    {
        TargetNode = targetNode;
        Weight = weight;
    }
}

QueueItem class

public class QueueItem
{
    public Node Node { get; private set; }
    public List<Edge> VisitedEdges { get; private set; }

    public QueueItem(Node node, List<Edge> visitedEdges)
    {
        Node = node;
        VisitedEdges = visitedEdges;
    }
}

Path struct

public struct Path
{
    public readonly Node StartNode;
    public readonly Node EndNode;
    public readonly string PathRepresentation;
    public readonly int VisitedCount;
    public readonly double TotalWeight;

    public Path(Node startNode, Node endNode, string pathRepresentation, int visitedCount, double totalWeight)
    {
        StartNode = startNode;
        EndNode = endNode;
        PathRepresentation = pathRepresentation;
        VisitedCount = visitedCount;
        TotalWeight = totalWeight;
     
    }
}

IGraph interface

public interface IGraph
{
    void AddNode(string name);
    Node GetNode(string name);
    IGraph CreateGraph();
}

Graph class

public class Graph : IGraph
{
    public Dictionary<string, Node> Nodes { get; private set; }

    public Graph()
    {
        Nodes = new Dictionary<string, Node>();
    }

    public void AddNode(string name)
    {
        Nodes.Add(name, new Node(name));
    }

    public Node GetNode(string name)
    {
        if (Nodes.ContainsKey(name))
            return Nodes[name];

        return null;
    }

    public IGraph CreateGraph()
    {
        const int START_NODE_INDEX = 0;
        const int END_NODE_INDEX = 1;
        const int EDGE_WEIGHT_INDEX = 2;
        
        var nodes = File.ReadAllText("graph.csv").Split(',');

        IGraph graph = new Graph();

        foreach (var n in nodes)
        {
            var node = n.Trim();
            
            if (graph.GetNode(node[START_NODE_INDEX].ToString()) == null)
            {
                graph.AddNode(node[START_NODE_INDEX].ToString());
            }

            if (graph.GetNode(node[END_NODE_INDEX].ToString()) == null)
            {
                graph.AddNode(node[END_NODE_INDEX].ToString());
            }

            graph.GetNode(node[START_NODE_INDEX].ToString())
                 .AddEdge(graph.GetNode(node[END_NODE_INDEX].ToString()), Convert.ToInt32(node[EDGE_WEIGHT_INDEX].ToString()));

        }

        return graph;
    }
}

IPathFinder interface

public interface IPathFinder
{
    Path GetShortestPath(Node startNode, Node endNode);

    List<Path> GetPathsWithMinWeight(Node startNode, Node endNode, double minWeight, bool inclusive);

    List<Path> GetPathsWithMaxWeight(Node startNode, Node endNode, double maxWeight, bool inclusive);

    List<Path> GetPathsWithExactWeight(Node startNode, Node endNode, double weight);

    List<Path> GetAllPaths(Node startNode, Node endNode);

    List<Path> GetPathsWithMinStops(Node startNode, Node endNode, int minStops, bool inclusive);

    List<Path> GetPathsWithMaxStops(Node startNode, Node endNode, int maxStops, bool inclusive);

    List<Path> GetPathsWithExactStops(Node startNode, Node endNode, int stops);

    Path GetPath(Node startNode, Node endNode, IEnumerable<Edge> visitedEdges);

    string GetPathRepresentation(Node startNode, Node endNode, IEnumerable<Edge> visiteEdges);
}

PathFinder class

public class PathFinder : IPathFinder
{
    public string GetPathRepresentation(Node startNode, Node endNode, IEnumerable<Edge> visiteEdges)
    {
        var pathRepresentation = new StringBuilder();

        pathRepresentation.AppendFormat("{0}->", startNode.Name);

        foreach (var visitedEdge in visiteEdges)
        {
            if (visitedEdge.TargetNode == endNode)
            {
                pathRepresentation.Append(endNode.Name);
            }
            else
            {
                pathRepresentation.AppendFormat("{0}->", visitedEdge.TargetNode.Name);
            }
        }

        return pathRepresentation.ToString();
    }
    
    public Path GetPath(Node startNode, Node endNode, IEnumerable<Edge> visitedEdges)
    {
        var visitedPaths = visitedEdges as IList<Edge> ?? visitedEdges.ToList();

        string pathRepresentation = GetPathRepresentation(startNode, endNode, visitedPaths);

        double totalWeight = visitedPaths.Aggregate<Edge, double>(0,
                            (current, visitedEdge) => current + visitedEdge.Weight);

        return new Path(startNode, endNode, pathRepresentation, visitedPaths.Count(), totalWeight);
    }

    public List<Path> GetAllPaths(Node startNode, Node endNode)
    {
        var paths = new List<Path>();
        var queue = new Queue<QueueItem>();

        queue.Enqueue(new QueueItem(startNode, new List<Edge>()));

        while (queue.Count > 0)
        {
            var currentItem = queue.Dequeue();
            foreach (var edge in currentItem.Node.Edges)
            {
                if (!currentItem.VisitedEdges.Contains(edge))
                {
                    var visitedEdges = new List<Edge>(currentItem.VisitedEdges) { edge };
                    if (edge.TargetNode == endNode)
                    {
                        var path = GetPath(startNode, endNode, visitedEdges);

                        paths.Add(path);
                    }
                    else
                    {
                        queue.Enqueue(new QueueItem(edge.TargetNode, visitedEdges));
                    }
                }
            }
        }
        return paths;
    }
    
    public Path GetShortestPath(Node startNode, Node endNode)
    {
        List<Path> paths = GetAllPaths(startNode, endNode);
        var shortestPath = new Path();
        double shortestPathWeight = double.PositiveInfinity;

        if (paths.Count > 0)
        {
            foreach (var path in paths)
            {
                if (path.TotalWeight < shortestPathWeight)
                {
                    if (path.TotalWeight < shortestPathWeight)
                    {
                        shortestPathWeight = path.TotalWeight;

                        shortestPath = new Path(startNode, endNode, path.PathRepresentation, path.VisitedCount,
                            path.TotalWeight);
                    }
                }
            }
        }

        return shortestPath;
    }

    public List<Path> GetPathsWithMinWeight(Node startNode, Node endNode, double minWeight, bool inclusive)
    {
        if (inclusive)
            return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight >= minWeight).ToList();

        return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight > minWeight).ToList();
    }

    public List<Path> GetPathsWithMaxWeight(Node startNode, Node endNode, double maxWeight, bool inclusive)
    {
        if(inclusive)
            return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight <= maxWeight).ToList();

        return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight < maxWeight).ToList();
    }

    public List<Path> GetPathsWithExactWeight(Node startNode, Node endNode, double weight)
    {
        return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight.Equals(weight)).ToList();
    }

    public List<Path> GetPathsWithMinStops(Node startNode, Node endNode, int minStops, bool inclusive)
    {
        if(inclusive)
            return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount >= minStops).ToList();
            
        return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount > minStops).ToList();
    }

    public List<Path> GetPathsWithMaxStops(Node startNode, Node endNode, int maxStops, bool inclusive)
    {
        if(inclusive)
            return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount <= maxStops).ToList();

        return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount < maxStops).ToList();
    }

    public List<Path> GetPathsWithExactStops(Node startNode, Node endNode, int stops)
    {
        return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount == stops).ToList();
    }

    public List<Path> GetAllPaths2(Node startNode, Node endNode)
    {
        var paths = new List<Path>();
        var queue = new Queue<QueueItem>();

        queue.Enqueue(new QueueItem(startNode, new List<Edge>()));

        while (queue.Count > 0)
        {
            var currentItem = queue.Dequeue();
            foreach (var edge in currentItem.Node.Edges)
            {
                if (!currentItem.VisitedEdges.Contains(edge))
                {
                    var visitedEdges = new List<Edge>(currentItem.VisitedEdges) { edge };
                    if (edge.TargetNode == endNode)
                    {
                        var path = GetPath(startNode, endNode, visitedEdges);

                        paths.Add(path);
                    }
                    else
                    {
                        queue.Enqueue(new QueueItem(edge.TargetNode, visitedEdges));
                    }
                }
            }
        }
        return paths;
    }
}

IPathPrinter interface

public interface IPathPrinter
{
    void PrintShortestPath(Path path);
    void PrintPathsWithMaxWeight(List<Path> paths, Node startNode, Node endNode, double maxWeight, bool inclusive);
    void PrintPathDistance(List<Path> paths);
    void PrintPathsWithMaxStops(List<Path> paths, Node startNode, Node endNode, int maxStops, bool inclusive);
    void PrintPathsWithExactStops(List<Path> paths, Node startNode, Node endNode, int maxStops);
}

PathPrinter class

public class PathPrinter : IPathPrinter
{
    public void PrintShortestPath(Path path)
    {
        Console.WriteLine("The shortest path from '{0}' to '{1} is '{2}' with a distance of {3}",
            path.StartNode.Name, path.EndNode.Name, path.PathRepresentation, path.TotalWeight);
    }

    public void PrintPathsWithMaxWeight(List<Path> paths, Node startNode, Node endNode, double maxWeight, bool inclusive)
    {
        if (inclusive)
        {
            Console.WriteLine(
                "The number of trips from '{0}' to '{1}' with a distance of less than or equal to {2} is {3}:",
                startNode.Name, endNode.Name, maxWeight, paths.Count);
        }
        else
        {
            Console.WriteLine(
                "The number of trips from '{0}' to '{1}' with a distance of less than {2} is {3}:",
                startNode.Name, endNode.Name, maxWeight, paths.Count);
        }

        foreach (var path in paths)
        {
            Console.WriteLine("{0} with a distance of {1}", path.PathRepresentation, path.TotalWeight);
        }
    }

    public void PrintPathsWithMaxStops(List<Path> paths, Node startNode, Node endNode, int maxStops, bool inclusive)
    {
        if (inclusive)
        {
            Console.WriteLine("The number of trips from '{0}' to '{1}' with a maximum of {2} stops is {3}:", startNode.Name, endNode.Name, maxStops, paths.Count);
        }
        else
        {
            Console.WriteLine("The number of trips from '{0}' to '{1}' with a maximum of less than {2} stops is {3}:", startNode.Name, endNode.Name, maxStops, paths.Count);
        }
        

        PrintPaths(paths);
    }

    public void PrintPathsWithExactStops(List<Path> paths, Node startNode, Node endNode, int maxStops)
    {

        Console.WriteLine("The number of trips from '{0}' to '{1}' with exactly {2} stops is {3}:", startNode.Name, endNode.Name, maxStops, paths.Count);

        PrintPaths(paths);
    }

    public void PrintPathDistance(List<Path> paths)
    {
        foreach (var path in paths)
        {
            Console.WriteLine("The distance of the route '{0}' is {1}", path.PathRepresentation, path.TotalWeight);
        }
    }

    private static void PrintPaths(IEnumerable<Path> paths)
    {
        foreach (var path in paths)
        {
            Console.WriteLine(path.PathRepresentation);
        }
    }
}

Program class

public class Program
{
    public static void Main(string[] args)
    {
        IPathFinder pathFinder = new PathFinder();
        IPathPrinter pathPrinter = new PathPrinter();

        IGraph graph = new Graph().CreateGraph();
        
        var pathsAtoC = pathFinder.GetAllPaths(graph.GetNode("A"), graph.GetNode("C"));
        pathPrinter.PrintPathDistance(pathsAtoC.Where(x => x.PathRepresentation.Equals("A->B->C")).ToList());

        var pathsAtoD = pathFinder.GetAllPaths(graph.GetNode("A"), graph.GetNode("D"));
        
        pathPrinter.PrintPathDistance(pathsAtoD.Where(x => x.PathRepresentation.Equals("A->D")).ToList());

        pathPrinter.PrintPathDistance(pathsAtoC.Where(x => x.PathRepresentation.Equals("A->D->C")).ToList());

        pathPrinter.PrintPathDistance(pathsAtoD.Where(x => x.PathRepresentation.Equals("A->E->B->C->D")).ToList());

        var validPath = pathsAtoD.Any(x => x.PathRepresentation.Equals("A->E->D"));

        if(validPath)
            pathPrinter.PrintPathDistance(pathsAtoD.Where(x => x.PathRepresentation.Equals("A->E->D")).ToList());

        Console.WriteLine(Environment.NewLine);

        var path = pathFinder.GetShortestPath(graph.GetNode("A"), graph.GetNode("C"));
        pathPrinter.PrintShortestPath(path);

        path = pathFinder.GetShortestPath(graph.GetNode("C"), graph.GetNode("C"));
        pathPrinter.PrintShortestPath(path);

        Console.WriteLine(Environment.NewLine);

        var paths = pathFinder.GetPathsWithMaxStops(graph.GetNode("C"), graph.GetNode("C"),3,true);
        pathPrinter.PrintPathsWithMaxStops(paths,graph.GetNode("C"),graph.GetNode("C"),3,true);

        Console.WriteLine(Environment.NewLine);

        paths = pathFinder.GetPathsWithExactStops(graph.GetNode("A"), graph.GetNode("C"), 4);
        pathPrinter.PrintPathsWithExactStops(paths, graph.GetNode("A"), graph.GetNode("C"), 4);

        Console.WriteLine(Environment.NewLine);

        paths = pathFinder.GetPathsWithMaxWeight(graph.GetNode("C"), graph.GetNode("C"), 30, false);
        pathPrinter.PrintPathsWithMaxWeight(paths, graph.GetNode("C"), graph.GetNode("C"), 30,false);

        Console.ReadKey();
    } 
}

Here is my first attempt at refactoring. Instead of using a Tuple, I used a STRUCT and this made it more readable. I also extracted printing paths and finding paths into interfaces so someone could print their own messages or implement there own path finding algorithm. The graph class has a corresponding interface as well. I am still not sure if I need all the path finding methods, some of them are just one liners, so I wonder if they are necessary. I also want to pass a file name as an args parameter into main, but I am not sure how to get that to my CreateGraph method. Also, IGraph graph = new Graph().CreateGraph(); seems a bit weird. Is this normal? I am also curious if there is a way to better pass in messages to my PrintXXXPath... methods, so I don't have to have so many of them?

Node class

Any other refactorings are welcome?

public class Node
{
    public string Name { get; private set; }
    public List<Edge> Edges { get; private set; }

    public Node(string name)
    {
        Name = name;
        Edges = new List<Edge>();
    }

    public void AddEdge(Node targetNode, double weight)
    {
        Edges.Add(new Edge(targetNode,weight));
    }
}

Edge class

public class Edge
{
    public Node TargetNode { get; private set; }
    public double Weight { get; private set; }

    public Edge(Node targetNode, double weight)
    {
        TargetNode = targetNode;
        Weight = weight;
    }
}

QueueItem class

public class QueueItem
{
    public Node Node { get; private set; }
    public List<Edge> VisitedEdges { get; private set; }

    public QueueItem(Node node, List<Edge> visitedEdges)
    {
        Node = node;
        VisitedEdges = visitedEdges;
    }
}

Path struct

public struct Path
{
    public readonly Node StartNode;
    public readonly Node EndNode;
    public readonly string PathRepresentation;
    public readonly int VisitedCount;
    public readonly double TotalWeight;

    public Path(Node startNode, Node endNode, string pathRepresentation, int visitedCount, double totalWeight)
    {
        StartNode = startNode;
        EndNode = endNode;
        PathRepresentation = pathRepresentation;
        VisitedCount = visitedCount;
        TotalWeight = totalWeight;
     
    }
}

IGraph interface

public interface IGraph
{
    void AddNode(string name);
    Node GetNode(string name);
    IGraph CreateGraph();
}

Graph class

public class Graph : IGraph
{
    public Dictionary<string, Node> Nodes { get; private set; }

    public Graph()
    {
        Nodes = new Dictionary<string, Node>();
    }

    public void AddNode(string name)
    {
        Nodes.Add(name, new Node(name));
    }

    public Node GetNode(string name)
    {
        if (Nodes.ContainsKey(name))
            return Nodes[name];

        return null;
    }

    public IGraph CreateGraph()
    {
        const int START_NODE_INDEX = 0;
        const int END_NODE_INDEX = 1;
        const int EDGE_WEIGHT_INDEX = 2;
        
        var nodes = File.ReadAllText("graph.csv").Split(',');

        IGraph graph = new Graph();

        foreach (var n in nodes)
        {
            var node = n.Trim();
            
            if (graph.GetNode(node[START_NODE_INDEX].ToString()) == null)
            {
                graph.AddNode(node[START_NODE_INDEX].ToString());
            }

            if (graph.GetNode(node[END_NODE_INDEX].ToString()) == null)
            {
                graph.AddNode(node[END_NODE_INDEX].ToString());
            }

            graph.GetNode(node[START_NODE_INDEX].ToString())
                 .AddEdge(graph.GetNode(node[END_NODE_INDEX].ToString()), Convert.ToInt32(node[EDGE_WEIGHT_INDEX].ToString()));

        }

        return graph;
    }
}

IPathFinder interface

public interface IPathFinder
{
    Path GetShortestPath(Node startNode, Node endNode);

    List<Path> GetPathsWithMinWeight(Node startNode, Node endNode, double minWeight, bool inclusive);

    List<Path> GetPathsWithMaxWeight(Node startNode, Node endNode, double maxWeight, bool inclusive);

    List<Path> GetPathsWithExactWeight(Node startNode, Node endNode, double weight);

    List<Path> GetAllPaths(Node startNode, Node endNode);

    List<Path> GetPathsWithMinStops(Node startNode, Node endNode, int minStops, bool inclusive);

    List<Path> GetPathsWithMaxStops(Node startNode, Node endNode, int maxStops, bool inclusive);

    List<Path> GetPathsWithExactStops(Node startNode, Node endNode, int stops);

    Path GetPath(Node startNode, Node endNode, IEnumerable<Edge> visitedEdges);

    string GetPathRepresentation(Node startNode, Node endNode, IEnumerable<Edge> visiteEdges);
}

PathFinder class

public class PathFinder : IPathFinder
{
    public string GetPathRepresentation(Node startNode, Node endNode, IEnumerable<Edge> visiteEdges)
    {
        var pathRepresentation = new StringBuilder();

        pathRepresentation.AppendFormat("{0}->", startNode.Name);

        foreach (var visitedEdge in visiteEdges)
        {
            if (visitedEdge.TargetNode == endNode)
            {
                pathRepresentation.Append(endNode.Name);
            }
            else
            {
                pathRepresentation.AppendFormat("{0}->", visitedEdge.TargetNode.Name);
            }
        }

        return pathRepresentation.ToString();
    }
    
    public Path GetPath(Node startNode, Node endNode, IEnumerable<Edge> visitedEdges)
    {
        var visitedPaths = visitedEdges as IList<Edge> ?? visitedEdges.ToList();

        string pathRepresentation = GetPathRepresentation(startNode, endNode, visitedPaths);

        double totalWeight = visitedPaths.Aggregate<Edge, double>(0,
                            (current, visitedEdge) => current + visitedEdge.Weight);

        return new Path(startNode, endNode, pathRepresentation, visitedPaths.Count(), totalWeight);
    }

    public List<Path> GetAllPaths(Node startNode, Node endNode)
    {
        var paths = new List<Path>();
        var queue = new Queue<QueueItem>();

        queue.Enqueue(new QueueItem(startNode, new List<Edge>()));

        while (queue.Count > 0)
        {
            var currentItem = queue.Dequeue();
            foreach (var edge in currentItem.Node.Edges)
            {
                if (!currentItem.VisitedEdges.Contains(edge))
                {
                    var visitedEdges = new List<Edge>(currentItem.VisitedEdges) { edge };
                    if (edge.TargetNode == endNode)
                    {
                        var path = GetPath(startNode, endNode, visitedEdges);

                        paths.Add(path);
                    }
                    else
                    {
                        queue.Enqueue(new QueueItem(edge.TargetNode, visitedEdges));
                    }
                }
            }
        }
        return paths;
    }
    
    public Path GetShortestPath(Node startNode, Node endNode)
    {
        List<Path> paths = GetAllPaths(startNode, endNode);
        var shortestPath = new Path();
        double shortestPathWeight = double.PositiveInfinity;

        if (paths.Count > 0)
        {
            foreach (var path in paths)
            {
                if (path.TotalWeight < shortestPathWeight)
                {
                    if (path.TotalWeight < shortestPathWeight)
                    {
                        shortestPathWeight = path.TotalWeight;

                        shortestPath = new Path(startNode, endNode, path.PathRepresentation, path.VisitedCount,
                            path.TotalWeight);
                    }
                }
            }
        }

        return shortestPath;
    }

    public List<Path> GetPathsWithMinWeight(Node startNode, Node endNode, double minWeight, bool inclusive)
    {
        if (inclusive)
            return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight >= minWeight).ToList();

        return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight > minWeight).ToList();
    }

    public List<Path> GetPathsWithMaxWeight(Node startNode, Node endNode, double maxWeight, bool inclusive)
    {
        if(inclusive)
            return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight <= maxWeight).ToList();

        return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight < maxWeight).ToList();
    }

    public List<Path> GetPathsWithExactWeight(Node startNode, Node endNode, double weight)
    {
        return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight.Equals(weight)).ToList();
    }

    public List<Path> GetPathsWithMinStops(Node startNode, Node endNode, int minStops, bool inclusive)
    {
        if(inclusive)
            return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount >= minStops).ToList();
            
        return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount > minStops).ToList();
    }

    public List<Path> GetPathsWithMaxStops(Node startNode, Node endNode, int maxStops, bool inclusive)
    {
        if(inclusive)
            return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount <= maxStops).ToList();

        return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount < maxStops).ToList();
    }

    public List<Path> GetPathsWithExactStops(Node startNode, Node endNode, int stops)
    {
        return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount == stops).ToList();
    }

    public List<Path> GetAllPaths2(Node startNode, Node endNode)
    {
        var paths = new List<Path>();
        var queue = new Queue<QueueItem>();

        queue.Enqueue(new QueueItem(startNode, new List<Edge>()));

        while (queue.Count > 0)
        {
            var currentItem = queue.Dequeue();
            foreach (var edge in currentItem.Node.Edges)
            {
                if (!currentItem.VisitedEdges.Contains(edge))
                {
                    var visitedEdges = new List<Edge>(currentItem.VisitedEdges) { edge };
                    if (edge.TargetNode == endNode)
                    {
                        var path = GetPath(startNode, endNode, visitedEdges);

                        paths.Add(path);
                    }
                    else
                    {
                        queue.Enqueue(new QueueItem(edge.TargetNode, visitedEdges));
                    }
                }
            }
        }
        return paths;
    }
}

IPathPrinter interface

public interface IPathPrinter
{
    void PrintShortestPath(Path path);
    void PrintPathsWithMaxWeight(List<Path> paths, Node startNode, Node endNode, double maxWeight, bool inclusive);
    void PrintPathDistance(List<Path> paths);
    void PrintPathsWithMaxStops(List<Path> paths, Node startNode, Node endNode, int maxStops, bool inclusive);
    void PrintPathsWithExactStops(List<Path> paths, Node startNode, Node endNode, int maxStops);
}

PathPrinter class

public class PathPrinter : IPathPrinter
{
    public void PrintShortestPath(Path path)
    {
        Console.WriteLine("The shortest path from '{0}' to '{1} is '{2}' with a distance of {3}",
            path.StartNode.Name, path.EndNode.Name, path.PathRepresentation, path.TotalWeight);
    }

    public void PrintPathsWithMaxWeight(List<Path> paths, Node startNode, Node endNode, double maxWeight, bool inclusive)
    {
        if (inclusive)
        {
            Console.WriteLine(
                "The number of trips from '{0}' to '{1}' with a distance of less than or equal to {2} is {3}:",
                startNode.Name, endNode.Name, maxWeight, paths.Count);
        }
        else
        {
            Console.WriteLine(
                "The number of trips from '{0}' to '{1}' with a distance of less than {2} is {3}:",
                startNode.Name, endNode.Name, maxWeight, paths.Count);
        }

        foreach (var path in paths)
        {
            Console.WriteLine("{0} with a distance of {1}", path.PathRepresentation, path.TotalWeight);
        }
    }

    public void PrintPathsWithMaxStops(List<Path> paths, Node startNode, Node endNode, int maxStops, bool inclusive)
    {
        if (inclusive)
        {
            Console.WriteLine("The number of trips from '{0}' to '{1}' with a maximum of {2} stops is {3}:", startNode.Name, endNode.Name, maxStops, paths.Count);
        }
        else
        {
            Console.WriteLine("The number of trips from '{0}' to '{1}' with a maximum of less than {2} stops is {3}:", startNode.Name, endNode.Name, maxStops, paths.Count);
        }
        

        PrintPaths(paths);
    }

    public void PrintPathsWithExactStops(List<Path> paths, Node startNode, Node endNode, int maxStops)
    {

        Console.WriteLine("The number of trips from '{0}' to '{1}' with exactly {2} stops is {3}:", startNode.Name, endNode.Name, maxStops, paths.Count);

        PrintPaths(paths);
    }

    public void PrintPathDistance(List<Path> paths)
    {
        foreach (var path in paths)
        {
            Console.WriteLine("The distance of the route '{0}' is {1}", path.PathRepresentation, path.TotalWeight);
        }
    }

    private static void PrintPaths(IEnumerable<Path> paths)
    {
        foreach (var path in paths)
        {
            Console.WriteLine(path.PathRepresentation);
        }
    }
}

Program class

public class Program
{
    public static void Main(string[] args)
    {
        IPathFinder pathFinder = new PathFinder();
        IPathPrinter pathPrinter = new PathPrinter();

        IGraph graph = new Graph().CreateGraph();
        
        var pathsAtoC = pathFinder.GetAllPaths(graph.GetNode("A"), graph.GetNode("C"));
        pathPrinter.PrintPathDistance(pathsAtoC.Where(x => x.PathRepresentation.Equals("A->B->C")).ToList());

        var pathsAtoD = pathFinder.GetAllPaths(graph.GetNode("A"), graph.GetNode("D"));
        
        pathPrinter.PrintPathDistance(pathsAtoD.Where(x => x.PathRepresentation.Equals("A->D")).ToList());

        pathPrinter.PrintPathDistance(pathsAtoC.Where(x => x.PathRepresentation.Equals("A->D->C")).ToList());

        pathPrinter.PrintPathDistance(pathsAtoD.Where(x => x.PathRepresentation.Equals("A->E->B->C->D")).ToList());

        var validPath = pathsAtoD.Any(x => x.PathRepresentation.Equals("A->E->D"));

        if(validPath)
            pathPrinter.PrintPathDistance(pathsAtoD.Where(x => x.PathRepresentation.Equals("A->E->D")).ToList());

        Console.WriteLine(Environment.NewLine);

        var path = pathFinder.GetShortestPath(graph.GetNode("A"), graph.GetNode("C"));
        pathPrinter.PrintShortestPath(path);

        path = pathFinder.GetShortestPath(graph.GetNode("C"), graph.GetNode("C"));
        pathPrinter.PrintShortestPath(path);

        Console.WriteLine(Environment.NewLine);

        var paths = pathFinder.GetPathsWithMaxStops(graph.GetNode("C"), graph.GetNode("C"),3,true);
        pathPrinter.PrintPathsWithMaxStops(paths,graph.GetNode("C"),graph.GetNode("C"),3,true);

        Console.WriteLine(Environment.NewLine);

        paths = pathFinder.GetPathsWithExactStops(graph.GetNode("A"), graph.GetNode("C"), 4);
        pathPrinter.PrintPathsWithExactStops(paths, graph.GetNode("A"), graph.GetNode("C"), 4);

        Console.WriteLine(Environment.NewLine);

        paths = pathFinder.GetPathsWithMaxWeight(graph.GetNode("C"), graph.GetNode("C"), 30, false);
        pathPrinter.PrintPathsWithMaxWeight(paths, graph.GetNode("C"), graph.GetNode("C"), 30,false);

        Console.ReadKey();
    } 
}
Update
Source Link
xaisoft
  • 345
  • 1
  • 2
  • 7

Here is my first attempt at refactoring. Instead of using a Tuple, I used a STRUCT and this made it more readable. I also extracted printing paths and finding paths into interfaces so someone could print their own messages or implement there own path finding algorithm. The graph class has a corresponding interface as well. I am still not sure if I need all the path finding methods, some of them are just one liners, so I wonder if they are necessary. I also want to pass a file name as an args parameter into main, but I am not sure how to get that to my CreateGraph method. Also, IGraph graph = new Graph().CreateGraph(); seems a bit weird. Is this normal? I am also curious if there is a way to better pass in messages to my PrintXXXPath... methods, so I don't have to have so many of them?

Node class

Any other refactorings are welcome?

public class Node
{
    public string Name { get; private set; }
    public List<Edge> Edges { get; private set; }

    public Node(string name)
    {
        Name = name;
        Edges = new List<Edge>();
    }

    public void AddEdge(Node targetNode, double weight)
    {
        Edges.Add(new Edge(targetNode,weight));
    }
}

Edge class

public class Edge
{
    public Node TargetNode { get; private set; }
    public double Weight { get; private set; }

    public Edge(Node targetNode, double weight)
    {
        TargetNode = targetNode;
        Weight = weight;
    }
}

QueueItem class

public class QueueItem
{
    public Node Node { get; private set; }
    public List<Edge> VisitedEdges { get; private set; }

    public QueueItem(Node node, List<Edge> visitedEdges)
    {
        Node = node;
        VisitedEdges = visitedEdges;
    }
}

Path struct

public struct Path
{
    public readonly Node StartNode;
    public readonly Node EndNode;
    public readonly string PathRepresentation;
    public readonly int VisitedCount;
    public readonly double TotalWeight;

    public Path(Node startNode, Node endNode, string pathRepresentation, int visitedCount, double totalWeight)
    {
        StartNode = startNode;
        EndNode = endNode;
        PathRepresentation = pathRepresentation;
        VisitedCount = visitedCount;
        TotalWeight = totalWeight;
     
    }
}

IGraph interface

public interface IGraph
{
    void AddNode(string name);
    Node GetNode(string name);
    IGraph CreateGraph();
}

Graph class

public class Graph : IGraph
{
    public Dictionary<string, Node> Nodes { get; private set; }

    public Graph()
    {
        Nodes = new Dictionary<string, Node>();
    }

    public void AddNode(string name)
    {
        Nodes.Add(name, new Node(name));
    }

    public Node GetNode(string name)
    {
        if (Nodes.ContainsKey(name))
            return Nodes[name];

        return null;
    }

    public IGraph CreateGraph()
    {
        const int START_NODE_INDEX = 0;
        const int END_NODE_INDEX = 1;
        const int EDGE_WEIGHT_INDEX = 2;
        
        var nodes = File.ReadAllText("graph.csv").Split(',');

        IGraph graph = new Graph();

        foreach (var n in nodes)
        {
            var node = n.Trim();
            
            if (graph.GetNode(node[START_NODE_INDEX].ToString()) == null)
            {
                graph.AddNode(node[START_NODE_INDEX].ToString());
            }

            if (graph.GetNode(node[END_NODE_INDEX].ToString()) == null)
            {
                graph.AddNode(node[END_NODE_INDEX].ToString());
            }

            graph.GetNode(node[START_NODE_INDEX].ToString())
                 .AddEdge(graph.GetNode(node[END_NODE_INDEX].ToString()), Convert.ToInt32(node[EDGE_WEIGHT_INDEX].ToString()));

        }

        return graph;
    }
}

IPathFinder interface

public interface IPathFinder
{
    Path GetShortestPath(Node startNode, Node endNode);

    List<Path> GetPathsWithMinWeight(Node startNode, Node endNode, double minWeight, bool inclusive);

    List<Path> GetPathsWithMaxWeight(Node startNode, Node endNode, double maxWeight, bool inclusive);

    List<Path> GetPathsWithExactWeight(Node startNode, Node endNode, double weight);

    List<Path> GetAllPaths(Node startNode, Node endNode);

    List<Path> GetPathsWithMinStops(Node startNode, Node endNode, int minStops, bool inclusive);

    List<Path> GetPathsWithMaxStops(Node startNode, Node endNode, int maxStops, bool inclusive);

    List<Path> GetPathsWithExactStops(Node startNode, Node endNode, int stops);

    Path GetPath(Node startNode, Node endNode, IEnumerable<Edge> visitedEdges);

    string GetPathRepresentation(Node startNode, Node endNode, IEnumerable<Edge> visiteEdges);
}

PathFinder class

public class PathFinder : IPathFinder
{
    public string GetPathRepresentation(Node startNode, Node endNode, IEnumerable<Edge> visiteEdges)
    {
        var pathRepresentation = new StringBuilder();

        pathRepresentation.AppendFormat("{0}->", startNode.Name);

        foreach (var visitedEdge in visiteEdges)
        {
            if (visitedEdge.TargetNode == endNode)
            {
                pathRepresentation.Append(endNode.Name);
            }
            else
            {
                pathRepresentation.AppendFormat("{0}->", visitedEdge.TargetNode.Name);
            }
        }

        return pathRepresentation.ToString();
    }
    
    public Path GetPath(Node startNode, Node endNode, IEnumerable<Edge> visitedEdges)
    {
        var visitedPaths = visitedEdges as IList<Edge> ?? visitedEdges.ToList();

        string pathRepresentation = GetPathRepresentation(startNode, endNode, visitedPaths);

        double totalWeight = visitedPaths.Aggregate<Edge, double>(0,
                            (current, visitedEdge) => current + visitedEdge.Weight);

        return new Path(startNode, endNode, pathRepresentation, visitedPaths.Count(), totalWeight);
    }

    public List<Path> GetAllPaths(Node startNode, Node endNode)
    {
        var paths = new List<Path>();
        var queue = new Queue<QueueItem>();

        queue.Enqueue(new QueueItem(startNode, new List<Edge>()));

        while (queue.Count > 0)
        {
            var currentItem = queue.Dequeue();
            foreach (var edge in currentItem.Node.Edges)
            {
                if (!currentItem.VisitedEdges.Contains(edge))
                {
                    var visitedEdges = new List<Edge>(currentItem.VisitedEdges) { edge };
                    if (edge.TargetNode == endNode)
                    {
                        var path = GetPath(startNode, endNode, visitedEdges);

                        paths.Add(path);
                    }
                    else
                    {
                        queue.Enqueue(new QueueItem(edge.TargetNode, visitedEdges));
                    }
                }
            }
        }
        return paths;
    }
    
    public Path GetShortestPath(Node startNode, Node endNode)
    {
        List<Path> paths = GetAllPaths(startNode, endNode);
        var shortestPath = new Path();
        double shortestPathWeight = double.PositiveInfinity;

        if (paths.Count > 0)
        {
            foreach (var path in paths)
            {
                if (path.TotalWeight < shortestPathWeight)
                {
                    if (path.TotalWeight < shortestPathWeight)
                    {
                        shortestPathWeight = path.TotalWeight;

                        shortestPath = new Path(startNode, endNode, path.PathRepresentation, path.VisitedCount,
                            path.TotalWeight);
                    }
                }
            }
        }

        return shortestPath;
    }

    public List<Path> GetPathsWithMinWeight(Node startNode, Node endNode, double minWeight, bool inclusive)
    {
        if (inclusive)
            return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight >= minWeight).ToList();

        return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight > minWeight).ToList();
    }

    public List<Path> GetPathsWithMaxWeight(Node startNode, Node endNode, double maxWeight, bool inclusive)
    {
        if(inclusive)
            return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight <= maxWeight).ToList();

        return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight < maxWeight).ToList();
    }

    public List<Path> GetPathsWithExactWeight(Node startNode, Node endNode, double weight)
    {
        return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight.Equals(weight)).ToList();
    }

    public List<Path> GetPathsWithMinStops(Node startNode, Node endNode, int minStops, bool inclusive)
    {
        if(inclusive)
            return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount >= minStops).ToList();
            
        return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount > minStops).ToList();
    }

    public List<Path> GetPathsWithMaxStops(Node startNode, Node endNode, int maxStops, bool inclusive)
    {
        if(inclusive)
            return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount <= maxStops).ToList();

        return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount < maxStops).ToList();
    }

    public List<Path> GetPathsWithExactStops(Node startNode, Node endNode, int stops)
    {
        return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount == stops).ToList();
    }

    public List<Path> GetAllPaths2(Node startNode, Node endNode)
    {
        var paths = new List<Path>();
        var queue = new Queue<QueueItem>();

        queue.Enqueue(new QueueItem(startNode, new List<Edge>()));

        while (queue.Count > 0)
        {
            var currentItem = queue.Dequeue();
            foreach (var edge in currentItem.Node.Edges)
            {
                if (!currentItem.VisitedEdges.Contains(edge))
                {
                    var visitedEdges = new List<Edge>(currentItem.VisitedEdges) { edge };
                    if (edge.TargetNode == endNode)
                    {
                        var path = GetPath(startNode, endNode, visitedEdges);

                        paths.Add(path);
                    }
                    else
                    {
                        queue.Enqueue(new QueueItem(edge.TargetNode, visitedEdges));
                    }
                }
            }
        }
        return paths;
    }
}

IPathPrinter interface

public interface IPathPrinter
{
    void PrintShortestPath(Path path);
    void PrintPathsWithMaxWeight(List<Path> paths, Node startNode, Node endNode, double maxWeight, bool inclusive);
    void PrintPathDistance(List<Path> paths);
    void PrintPathsWithMaxStops(List<Path> paths, Node startNode, Node endNode, int maxStops, bool inclusive);
    void PrintPathsWithExactStops(List<Path> paths, Node startNode, Node endNode, int maxStops);
}

PathPrinter class

public class PathPrinter : IPathPrinter
{
    public void PrintShortestPath(Path path)
    {
        Console.WriteLine("The shortest path from '{0}' to '{1} is '{2}' with a distance of {3}",
            path.StartNode.Name, path.EndNode.Name, path.PathRepresentation, path.TotalWeight);
    }

    public void PrintPathsWithMaxWeight(List<Path> paths, Node startNode, Node endNode, double maxWeight, bool inclusive)
    {
        if (inclusive)
        {
            Console.WriteLine(
                "The number of trips from '{0}' to '{1}' with a distance of less than or equal to {2} is {3}:",
                startNode.Name, endNode.Name, maxWeight, paths.Count);
        }
        else
        {
            Console.WriteLine(
                "The number of trips from '{0}' to '{1}' with a distance of less than {2} is {3}:",
                startNode.Name, endNode.Name, maxWeight, paths.Count);
        }

        foreach (var path in paths)
        {
            Console.WriteLine("{0} with a distance of {1}", path.PathRepresentation, path.TotalWeight);
        }
    }

    public void PrintPathsWithMaxStops(List<Path> paths, Node startNode, Node endNode, int maxStops, bool inclusive)
    {
        if (inclusive)
        {
            Console.WriteLine("The number of trips from '{0}' to '{1}' with a maximum of {2} stops is {3}:", startNode.Name, endNode.Name, maxStops, paths.Count);
        }
        else
        {
            Console.WriteLine("The number of trips from '{0}' to '{1}' with a maximum of less than {2} stops is {3}:", startNode.Name, endNode.Name, maxStops, paths.Count);
        }
        

        PrintPaths(paths);
    }

    public void PrintPathsWithExactStops(List<Path> paths, Node startNode, Node endNode, int maxStops)
    {

        Console.WriteLine("The number of trips from '{0}' to '{1}' with exactly {2} stops is {3}:", startNode.Name, endNode.Name, maxStops, paths.Count);

        PrintPaths(paths);
    }

    public void PrintPathDistance(List<Path> paths)
    {
        foreach (var path in paths)
        {
            Console.WriteLine("The distance of the route '{0}' is {1}", path.PathRepresentation, path.TotalWeight);
        }
    }

    private static void PrintPaths(IEnumerable<Path> paths)
    {
        foreach (var path in paths)
        {
            Console.WriteLine(path.PathRepresentation);
        }
    }
}

Program class

public class Program
{
    public static void Main(string[] args)
    {
        IPathFinder pathFinder = new PathFinder();
        IPathPrinter pathPrinter = new PathPrinter();

        IGraph graph = new Graph().CreateGraph();
        
        var pathsAtoC = pathFinder.GetAllPaths(graph.GetNode("A"), graph.GetNode("C"));
        pathPrinter.PrintPathDistance(pathsAtoC.Where(x => x.PathRepresentation.Equals("A->B->C")).ToList());

        var pathsAtoD = pathFinder.GetAllPaths(graph.GetNode("A"), graph.GetNode("D"));
        
        pathPrinter.PrintPathDistance(pathsAtoD.Where(x => x.PathRepresentation.Equals("A->D")).ToList());

        pathPrinter.PrintPathDistance(pathsAtoC.Where(x => x.PathRepresentation.Equals("A->D->C")).ToList());

        pathPrinter.PrintPathDistance(pathsAtoD.Where(x => x.PathRepresentation.Equals("A->E->B->C->D")).ToList());

        var validPath = pathsAtoD.Any(x => x.PathRepresentation.Equals("A->E->D"));

        if(validPath)
            pathPrinter.PrintPathDistance(pathsAtoD.Where(x => x.PathRepresentation.Equals("A->E->D")).ToList());

        Console.WriteLine(Environment.NewLine);

        var path = pathFinder.GetShortestPath(graph.GetNode("A"), graph.GetNode("C"));
        pathPrinter.PrintShortestPath(path);

        path = pathFinder.GetShortestPath(graph.GetNode("C"), graph.GetNode("C"));
        pathPrinter.PrintShortestPath(path);

        Console.WriteLine(Environment.NewLine);

        var paths = pathFinder.GetPathsWithMaxStops(graph.GetNode("C"), graph.GetNode("C"),3,true);
        pathPrinter.PrintPathsWithMaxStops(paths,graph.GetNode("C"),graph.GetNode("C"),3,true);

        Console.WriteLine(Environment.NewLine);

        paths = pathFinder.GetPathsWithExactStops(graph.GetNode("A"), graph.GetNode("C"), 4);
        pathPrinter.PrintPathsWithExactStops(paths, graph.GetNode("A"), graph.GetNode("C"), 4);

        Console.WriteLine(Environment.NewLine);

        paths = pathFinder.GetPathsWithMaxWeight(graph.GetNode("C"), graph.GetNode("C"), 30, false);
        pathPrinter.PrintPathsWithMaxWeight(paths, graph.GetNode("C"), graph.GetNode("C"), 30,false);

        Console.ReadKey();
    } 
}

Here is my first attempt at refactoring. Instead of using a Tuple, I used a STRUCT and this made it more readable. I also extracted printing paths and finding paths into interfaces so someone could print their own messages or implement there own path finding algorithm. The graph class has a corresponding interface as well. I am still not sure if I need all the path finding methods, some of them are just one liners, so I wonder if they are necessary. I also want to pass a file name as an args parameter into main, but I am not sure how to get that to my CreateGraph method. Also, IGraph graph = new Graph().CreateGraph(); seems a bit weird. Is this normal? I am also curious if there is a way to better pass in messages to my PrintXXXPath... methods, so I don't have to have so many of them?

Node class

Any other refactorings are welcome?

public class Node
{
    public string Name { get; private set; }
    public List<Edge> Edges { get; private set; }

    public Node(string name)
    {
        Name = name;
        Edges = new List<Edge>();
    }

    public void AddEdge(Node targetNode, double weight)
    {
        Edges.Add(new Edge(targetNode,weight));
    }
}

Edge class

public class Edge
{
    public Node TargetNode { get; private set; }
    public double Weight { get; private set; }

    public Edge(Node targetNode, double weight)
    {
        TargetNode = targetNode;
        Weight = weight;
    }
}

QueueItem class

public class QueueItem
{
    public Node Node { get; private set; }
    public List<Edge> VisitedEdges { get; private set; }

    public QueueItem(Node node, List<Edge> visitedEdges)
    {
        Node = node;
        VisitedEdges = visitedEdges;
    }
}

Path struct

public struct Path
{
    public readonly Node StartNode;
    public readonly Node EndNode;
    public readonly string PathRepresentation;
    public readonly int VisitedCount;
    public readonly double TotalWeight;

    public Path(Node startNode, Node endNode, string pathRepresentation, int visitedCount, double totalWeight)
    {
        StartNode = startNode;
        EndNode = endNode;
        PathRepresentation = pathRepresentation;
        VisitedCount = visitedCount;
        TotalWeight = totalWeight;
     
    }
}

IGraph interface

public interface IGraph
{
    void AddNode(string name);
    Node GetNode(string name);
    IGraph CreateGraph();
}

Graph class

public class Graph : IGraph
{
    public Dictionary<string, Node> Nodes { get; private set; }

    public Graph()
    {
        Nodes = new Dictionary<string, Node>();
    }

    public void AddNode(string name)
    {
        Nodes.Add(name, new Node(name));
    }

    public Node GetNode(string name)
    {
        if (Nodes.ContainsKey(name))
            return Nodes[name];

        return null;
    }

    public IGraph CreateGraph()
    {
        const int START_NODE_INDEX = 0;
        const int END_NODE_INDEX = 1;
        const int EDGE_WEIGHT_INDEX = 2;
        
        var nodes = File.ReadAllText("graph.csv").Split(',');

        IGraph graph = new Graph();

        foreach (var n in nodes)
        {
            var node = n.Trim();
            
            if (graph.GetNode(node[START_NODE_INDEX].ToString()) == null)
            {
                graph.AddNode(node[START_NODE_INDEX].ToString());
            }

            if (graph.GetNode(node[END_NODE_INDEX].ToString()) == null)
            {
                graph.AddNode(node[END_NODE_INDEX].ToString());
            }

            graph.GetNode(node[START_NODE_INDEX].ToString())
                 .AddEdge(graph.GetNode(node[END_NODE_INDEX].ToString()), Convert.ToInt32(node[EDGE_WEIGHT_INDEX].ToString()));

        }

        return graph;
    }
}

IPathFinder interface

public interface IPathFinder
{
    Path GetShortestPath(Node startNode, Node endNode);

    List<Path> GetPathsWithMinWeight(Node startNode, Node endNode, double minWeight, bool inclusive);

    List<Path> GetPathsWithMaxWeight(Node startNode, Node endNode, double maxWeight, bool inclusive);

    List<Path> GetPathsWithExactWeight(Node startNode, Node endNode, double weight);

    List<Path> GetAllPaths(Node startNode, Node endNode);

    List<Path> GetPathsWithMinStops(Node startNode, Node endNode, int minStops, bool inclusive);

    List<Path> GetPathsWithMaxStops(Node startNode, Node endNode, int maxStops, bool inclusive);

    List<Path> GetPathsWithExactStops(Node startNode, Node endNode, int stops);

    Path GetPath(Node startNode, Node endNode, IEnumerable<Edge> visitedEdges);

    string GetPathRepresentation(Node startNode, Node endNode, IEnumerable<Edge> visiteEdges);
}

PathFinder class

public class PathFinder : IPathFinder
{
    public string GetPathRepresentation(Node startNode, Node endNode, IEnumerable<Edge> visiteEdges)
    {
        var pathRepresentation = new StringBuilder();

        pathRepresentation.AppendFormat("{0}->", startNode.Name);

        foreach (var visitedEdge in visiteEdges)
        {
            if (visitedEdge.TargetNode == endNode)
            {
                pathRepresentation.Append(endNode.Name);
            }
            else
            {
                pathRepresentation.AppendFormat("{0}->", visitedEdge.TargetNode.Name);
            }
        }

        return pathRepresentation.ToString();
    }
    
    public Path GetPath(Node startNode, Node endNode, IEnumerable<Edge> visitedEdges)
    {
        var visitedPaths = visitedEdges as IList<Edge> ?? visitedEdges.ToList();

        string pathRepresentation = GetPathRepresentation(startNode, endNode, visitedPaths);

        double totalWeight = visitedPaths.Aggregate<Edge, double>(0,
                            (current, visitedEdge) => current + visitedEdge.Weight);

        return new Path(startNode, endNode, pathRepresentation, visitedPaths.Count(), totalWeight);
    }

    public List<Path> GetAllPaths(Node startNode, Node endNode)
    {
        var paths = new List<Path>();
        var queue = new Queue<QueueItem>();

        queue.Enqueue(new QueueItem(startNode, new List<Edge>()));

        while (queue.Count > 0)
        {
            var currentItem = queue.Dequeue();
            foreach (var edge in currentItem.Node.Edges)
            {
                if (!currentItem.VisitedEdges.Contains(edge))
                {
                    var visitedEdges = new List<Edge>(currentItem.VisitedEdges) { edge };
                    if (edge.TargetNode == endNode)
                    {
                        var path = GetPath(startNode, endNode, visitedEdges);

                        paths.Add(path);
                    }
                    else
                    {
                        queue.Enqueue(new QueueItem(edge.TargetNode, visitedEdges));
                    }
                }
            }
        }
        return paths;
    }
    
    public Path GetShortestPath(Node startNode, Node endNode)
    {
        List<Path> paths = GetAllPaths(startNode, endNode);
        var shortestPath = new Path();
        double shortestPathWeight = double.PositiveInfinity;

        if (paths.Count > 0)
        {
            foreach (var path in paths)
            {
                if (path.TotalWeight < shortestPathWeight)
                {
                    if (path.TotalWeight < shortestPathWeight)
                    {
                        shortestPathWeight = path.TotalWeight;

                        shortestPath = new Path(startNode, endNode, path.PathRepresentation, path.VisitedCount,
                            path.TotalWeight);
                    }
                }
            }
        }

        return shortestPath;
    }

    public List<Path> GetPathsWithMinWeight(Node startNode, Node endNode, double minWeight, bool inclusive)
    {
        if (inclusive)
            return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight >= minWeight).ToList();

        return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight > minWeight).ToList();
    }

    public List<Path> GetPathsWithMaxWeight(Node startNode, Node endNode, double maxWeight, bool inclusive)
    {
        if(inclusive)
            return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight <= maxWeight).ToList();

        return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight < maxWeight).ToList();
    }

    public List<Path> GetPathsWithExactWeight(Node startNode, Node endNode, double weight)
    {
        return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight.Equals(weight)).ToList();
    }

    public List<Path> GetPathsWithMinStops(Node startNode, Node endNode, int minStops, bool inclusive)
    {
        if(inclusive)
            return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount >= minStops).ToList();
            
        return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount > minStops).ToList();
    }

    public List<Path> GetPathsWithMaxStops(Node startNode, Node endNode, int maxStops, bool inclusive)
    {
        if(inclusive)
            return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount <= maxStops).ToList();

        return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount < maxStops).ToList();
    }

    public List<Path> GetPathsWithExactStops(Node startNode, Node endNode, int stops)
    {
        return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount == stops).ToList();
    }

    public List<Path> GetAllPaths2(Node startNode, Node endNode)
    {
        var paths = new List<Path>();
        var queue = new Queue<QueueItem>();

        queue.Enqueue(new QueueItem(startNode, new List<Edge>()));

        while (queue.Count > 0)
        {
            var currentItem = queue.Dequeue();
            foreach (var edge in currentItem.Node.Edges)
            {
                if (!currentItem.VisitedEdges.Contains(edge))
                {
                    var visitedEdges = new List<Edge>(currentItem.VisitedEdges) { edge };
                    if (edge.TargetNode == endNode)
                    {
                        var path = GetPath(startNode, endNode, visitedEdges);

                        paths.Add(path);
                    }
                    else
                    {
                        queue.Enqueue(new QueueItem(edge.TargetNode, visitedEdges));
                    }
                }
            }
        }
        return paths;
    }
}

IPathPrinter interface

public interface IPathPrinter
{
    void PrintShortestPath(Path path);
    void PrintPathsWithMaxWeight(List<Path> paths, Node startNode, Node endNode, double maxWeight, bool inclusive);
    void PrintPathDistance(List<Path> paths);
    void PrintPathsWithMaxStops(List<Path> paths, Node startNode, Node endNode, int maxStops, bool inclusive);
    void PrintPathsWithExactStops(List<Path> paths, Node startNode, Node endNode, int maxStops);
}

PathPrinter class

public class PathPrinter : IPathPrinter
{
    public void PrintShortestPath(Path path)
    {
        Console.WriteLine("The shortest path from '{0}' to '{1} is '{2}' with a distance of {3}",
            path.StartNode.Name, path.EndNode.Name, path.PathRepresentation, path.TotalWeight);
    }

    public void PrintPathsWithMaxWeight(List<Path> paths, Node startNode, Node endNode, double maxWeight, bool inclusive)
    {
        if (inclusive)
        {
            Console.WriteLine(
                "The number of trips from '{0}' to '{1}' with a distance of less than or equal to {2} is {3}:",
                startNode.Name, endNode.Name, maxWeight, paths.Count);
        }
        else
        {
            Console.WriteLine(
                "The number of trips from '{0}' to '{1}' with a distance of less than {2} is {3}:",
                startNode.Name, endNode.Name, maxWeight, paths.Count);
        }

        foreach (var path in paths)
        {
            Console.WriteLine("{0} with a distance of {1}", path.PathRepresentation, path.TotalWeight);
        }
    }

    public void PrintPathsWithMaxStops(List<Path> paths, Node startNode, Node endNode, int maxStops, bool inclusive)
    {
        if (inclusive)
        {
            Console.WriteLine("The number of trips from '{0}' to '{1}' with a maximum of {2} stops is {3}:", startNode.Name, endNode.Name, maxStops, paths.Count);
        }
        else
        {
            Console.WriteLine("The number of trips from '{0}' to '{1}' with a maximum of less than {2} stops is {3}:", startNode.Name, endNode.Name, maxStops, paths.Count);
        }
        

        PrintPaths(paths);
    }

    public void PrintPathsWithExactStops(List<Path> paths, Node startNode, Node endNode, int maxStops)
    {

        Console.WriteLine("The number of trips from '{0}' to '{1}' with exactly {2} stops is {3}:", startNode.Name, endNode.Name, maxStops, paths.Count);

        PrintPaths(paths);
    }

    public void PrintPathDistance(List<Path> paths)
    {
        foreach (var path in paths)
        {
            Console.WriteLine("The distance of the route '{0}' is {1}", path.PathRepresentation, path.TotalWeight);
        }
    }

    private static void PrintPaths(IEnumerable<Path> paths)
    {
        foreach (var path in paths)
        {
            Console.WriteLine(path.PathRepresentation);
        }
    }
}

Program class

public class Program
{
    public static void Main(string[] args)
    {
        IPathFinder pathFinder = new PathFinder();
        IPathPrinter pathPrinter = new PathPrinter();

        IGraph graph = new Graph().CreateGraph();
        
        var pathsAtoC = pathFinder.GetAllPaths(graph.GetNode("A"), graph.GetNode("C"));
        pathPrinter.PrintPathDistance(pathsAtoC.Where(x => x.PathRepresentation.Equals("A->B->C")).ToList());

        var pathsAtoD = pathFinder.GetAllPaths(graph.GetNode("A"), graph.GetNode("D"));
        
        pathPrinter.PrintPathDistance(pathsAtoD.Where(x => x.PathRepresentation.Equals("A->D")).ToList());

        pathPrinter.PrintPathDistance(pathsAtoC.Where(x => x.PathRepresentation.Equals("A->D->C")).ToList());

        pathPrinter.PrintPathDistance(pathsAtoD.Where(x => x.PathRepresentation.Equals("A->E->B->C->D")).ToList());

        var validPath = pathsAtoD.Any(x => x.PathRepresentation.Equals("A->E->D"));

        if(validPath)
            pathPrinter.PrintPathDistance(pathsAtoD.Where(x => x.PathRepresentation.Equals("A->E->D")).ToList());

        Console.WriteLine(Environment.NewLine);

        var path = pathFinder.GetShortestPath(graph.GetNode("A"), graph.GetNode("C"));
        pathPrinter.PrintShortestPath(path);

        path = pathFinder.GetShortestPath(graph.GetNode("C"), graph.GetNode("C"));
        pathPrinter.PrintShortestPath(path);

        Console.WriteLine(Environment.NewLine);

        var paths = pathFinder.GetPathsWithMaxStops(graph.GetNode("C"), graph.GetNode("C"),3,true);
        pathPrinter.PrintPathsWithMaxStops(paths,graph.GetNode("C"),graph.GetNode("C"),3,true);

        Console.WriteLine(Environment.NewLine);

        paths = pathFinder.GetPathsWithExactStops(graph.GetNode("A"), graph.GetNode("C"), 4);
        pathPrinter.PrintPathsWithExactStops(paths, graph.GetNode("A"), graph.GetNode("C"), 4);

        Console.WriteLine(Environment.NewLine);

        paths = pathFinder.GetPathsWithMaxWeight(graph.GetNode("C"), graph.GetNode("C"), 30, false);
        pathPrinter.PrintPathsWithMaxWeight(paths, graph.GetNode("C"), graph.GetNode("C"), 30,false);

        Console.ReadKey();
    } 
}
Tweeted twitter.com/#!/StackCodeReview/status/519337368654864384
Questions
Source Link
xaisoft
  • 345
  • 1
  • 2
  • 7
Loading
split large code block into smaller ones, to reduce scrolling
Source Link
Mathieu Guindon
  • 75.1k
  • 18
  • 191
  • 463
Loading
deleted 3 characters in body
Source Link
xaisoft
  • 345
  • 1
  • 2
  • 7
Loading
added 45 characters in body; edited title
Source Link
Mathieu Guindon
  • 75.1k
  • 18
  • 191
  • 463
Loading
Source Link
xaisoft
  • 345
  • 1
  • 2
  • 7
Loading