【问题标题】:Shortest Path Finder from CSV in C#C# 中 CSV 的最短路径查找器
【发布时间】:2017-01-26 10:29:24
【问题描述】:

假设我有以下CSV

Sydney,Dubai,1
Dubai,Venice,2
Venice,Rio,3
Venice,Sydney,1
Sydney,Rio,7

第一个字段是From,第二个字段是To,第三个字段是Duration

我需要一种方法,它可以采用From 输入并以以下格式吐出到所有其他To 字段的最短路径-

Selected City: Sydney
To 1: Dubai, Smallest Path Length: 1, Path: Sydney, Dubai.
To 2: Venice, Smallest Path Length: 3, Path: Sydney, Dubai, Venice.
To 3: Rio, Smallest Path Length: 6, Path: Sydney, Dubai, Venice, Rio.

(N.B. Sydney-Rio is 7 hours long hence Sydney-Dubai-Venice-Rio
 is the shortest route here which takes 2 hours).

我没有任何代码可以在这里添加,而且其他人建议使用 Dijkstra 的算法,但到目前为止我还没有一个示例可以满足我的需要。

【问题讨论】:

  • 人们真的很喜欢投反对票而不是帮助和鼓励......
  • 嗨,我有一个解决方案 - 给我几分钟!
  • @WaseemS 谢谢好友

标签: c# csv dijkstra shortest-path


【解决方案1】:

我编写了一个满足您需要的小型控制台程序。它是非常基本的,如果需要可以进一步增强。

如果您需要可下载的解决方案,请告诉我。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ShortPath

{
    class Program
    {
        static void Main(string[] args)
        {
 // assuming you have loaded your CSVs into a list of string
            List<string> csvLines = new List<string>()
            {
                "Sydney,Dubai,1",
                "Dubai,Venice,2",
                "Venice,Rio,3",
                "Venice,Sydney,1",
                "Sydney,Rio,7"
            };



            // lets convert the list of string into list or route
            var routes = new List<Route>();
            csvLines.ForEach(s =>
            {
                // split by ,
                string[] pieces = s.Split(',');

                // ensure travel time is a number
                decimal travelTime = 0;
                decimal.TryParse(pieces[2], out travelTime);

                // convert string to route object
                routes.Add(new Route()
                {
                    From = pieces[0],
                    To = pieces[1],
                    TravelTime = travelTime
                });
            });

            // once all the data in place - the rest is easy.
            // lets assume our FROM is sydne
            string selectedFrom = "Sydney";

            // no lets find all the routes from sydney to every other place
            // listing the shortes route first
            // the "Where" clause allows us to filter by the selected from
            // the order by clause allows us to order by travel time
            var desiredRoutes = routes.Where(route => route.From == selectedFrom).OrderBy(route => route.TravelTime).ToList();

            // the output template allows us to format all outputs
            // the numbers in culry bracers such as {0} {1}...etc are placeholderst that get replaced with actul values
            // {0} = index number
            // {1} = To
            // {2} = duration
            // {3} = From
            // "To 1: Dubai, Smallest Path Length: 1, Path: Sydney, Dubai.";/
            string outputTemplate = "To {0}: {1}, Smallest Path Length: {2}, Path: {3}, {1}.";


            Console.WriteLine("Selected Country: '{0}'.", selectedFrom);

            // look through each selected route
            for(int index = 0; index < desiredRoutes.Count; index++)
            {
                // ensure you access to the route variable in the current instance of the loop
                var route = desiredRoutes[index];

                // write all outputs
                // (index + 1) allows our counter to start from 1 instead of 0
                Console.WriteLine(outputTemplate, (index + 1), route.To, route.TravelTime, route.From);
            }

            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }
}

注意:Route 的类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ShortPath
{
    public class Route
    {
        public string From { get; set; }

        public string To { get; set; }

        public decimal TravelTime { get; set; }

    }
}

输出应如下所示:

Selected Country: 'Sydney'.
To 1: Dubai, Smallest Path Length: 1, Path: Sydney, Dubai.
To 2: Rio, Smallest Path Length: 7, Path: Sydney, Rio.
Press any key to exit.

【讨论】:

  • 是的,我需要可下载的解决方案。但您似乎在此处对路线进行了硬编码,而不是从 CSV 中导入它。所以假设我在 List 中有 CSV。我该如何着手和使用它?
  • 看来我的解决方案确实适合理想情况。我将为 CSV 输入修改这个
  • 你去 - 我会尽快上传解决方案(更新答案)
  • 您可以在此处找到可下载的解决方案:blog.waseem-sabjee.com/wp-content/uploads/2016/09/ShortPath.zip 注意它是使用 VS 2013 创建的。如果您需要我保存为旧格式,请告诉我。
  • 注意:这不是最持久的代码 - 我们需要查看 NULL 检查和长度检查以确保安全
【解决方案2】:

试试这个

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;


namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            //this one uses strings as node names
            Dijkstra1.Program.Dijkstra();
            Console.ReadLine();
        }
    }
}
namespace Dijkstra1
{
    class Program
    {
        //Sydney,Dubai,1
        //Dubai,Venice,2
        //Venice,Rio,3
        //Venice,Sydney,1
        //Sydney,Rio,7
        static List<List<String>> input1 = new List<List<string>>{
               new List<String>() {"Sydney","0","1","7","1"},
               new List<String>() {"Dubai", "1","0","0","2"},
               new List<String>() {"Rio",   "7","0","0","3"},
               new List<String>() {"Venice","1","2","3","0"},
            };

        static public void Dijkstra()
        {
            CGraph cGraph;
            cGraph = new CGraph(input1);
            Console.WriteLine("-------------Input 1 -------------");
            cGraph.PrintGraph();

        }
        class CGraph
        {
            List<Node> graph = new List<Node>();
            public CGraph(List<List<String>> input)
            {
                foreach (List<string> inputRow in input)
                {
                    Node newNode = new Node();
                    newNode.name = inputRow[0];
                    newNode.distanceDict = new Dictionary<string, Path>();
                    newNode.visited = false;
                    newNode.neighbors = new List<Neighbor>();
                    //for (int index = 1; index < inputRow.Count; index++)
                    //{
                    //    //skip diagnol values so you don't count a nodes distance to itself.
                    //    //node count start at zero
                    //    // index you have to skip the node name
                    //    //so you have to subtract one from the index
                    //    if ((index - 1) != nodeCount)
                    //    {
                    //        string nodeName = input[index - 1][0];
                    //        int distance = int.Parse(inputRow[index]);
                    //        newNode.distanceDict.Add(nodeName, new List<string>() { nodeName });
                    //    } 
                    //}
                    graph.Add(newNode);
                }
                //initialize neighbors using predefined dictionary
                for (int nodeCount = 0; nodeCount < graph.Count; nodeCount++)
                {
                    for (int neighborCount = 0; neighborCount < graph.Count; neighborCount++)
                    {
                        //add one to neighbor count to skip Node name in index one
                        if (input[nodeCount][neighborCount + 1] != "0")
                        {
                            Neighbor newNeightbor = new Neighbor();
                            newNeightbor.node = graph[neighborCount];
                            newNeightbor.distance = int.Parse(input[nodeCount][neighborCount + 1]);
                            graph[nodeCount].neighbors.Add(newNeightbor);
                            Path path = new Path();
                            path.nodeNames = new List<string>() { input[neighborCount][0] };
                            //add one to neighbor count to skip Node name in index one
                            path.totalDistance = int.Parse(input[nodeCount][neighborCount + 1]);
                            graph[nodeCount].distanceDict.Add(input[neighborCount][0], path);
                        }
                    }
                }

                foreach (Node node in graph)
                {
                    foreach (Node nodex in graph)
                    {
                        node.visited = false;
                    }
                    TransverNode(node);
                }
            }
            public class Neighbor
            {
                public Node node { get; set; }
                public int distance { get; set; }
            }
            public class Path
            {
                public List<string> nodeNames { get; set; }
                public int totalDistance { get; set; }
            }
            public class Node
            {
                public string name { get; set; }
                public Dictionary<string, Path> distanceDict { get; set; }
                public bool visited { get; set; }
                public List<Neighbor> neighbors { get; set; }
            }
            static void TransverNode(Node node)
            {
                if (!node.visited)
                {
                    node.visited = true;
                    foreach (Neighbor neighbor in node.neighbors)
                    {
                        TransverNode(neighbor.node);
                        string neighborName = neighbor.node.name;
                        int neighborDistance = neighbor.distance;
                        //compair neighbors dictionary with current dictionary
                        //update current dictionary as required
                        foreach (string key in neighbor.node.distanceDict.Keys)
                        {
                            if (key != node.name)
                            {
                                int neighborKeyDistance = neighbor.node.distanceDict[key].totalDistance;
                                if (node.distanceDict.ContainsKey(key))
                                {
                                    int currentDistance = node.distanceDict[key].totalDistance;
                                    if (neighborKeyDistance + neighborDistance < currentDistance)
                                    {
                                        List<string> nodeList = new List<string>();
                                        nodeList.AddRange(neighbor.node.distanceDict[key].nodeNames);
                                        nodeList.Insert(0, neighbor.node.name);
                                        node.distanceDict[key].nodeNames = nodeList;
                                        node.distanceDict[key].totalDistance = neighborKeyDistance + neighborDistance;
                                    }
                                }
                                else
                                {
                                    List<string> nodeList = new List<string>();
                                    nodeList.AddRange(neighbor.node.distanceDict[key].nodeNames);
                                    nodeList.Insert(0, neighbor.node.name);
                                    Path path = new Path();
                                    path.nodeNames = nodeList;
                                    path.totalDistance = neighbor.distance + neighborKeyDistance;
                                    node.distanceDict.Add(key, path);
                                }
                            }
                        }
                    }
                }
            }
            public void PrintGraph()
            {
                foreach (Node node in graph)
                {
                    Console.WriteLine("Node : {0}", node.name);
                    foreach (string key in node.distanceDict.Keys.OrderBy(x => x))
                    {
                        Console.WriteLine(" Distance to node {0} = {1}, Path : {2}", key, node.distanceDict[key].totalDistance, string.Join(",", node.distanceDict[key].nodeNames.ToArray()));
                    }
                }
            }
        }
    }

}

【讨论】:

  • 您好,非常感谢,这似乎可行,但您能解释一下代码吗?或添加更多 cmets 解释?我为此挠头……
  • 这是标准的 Dijkstra 算法。每个位置都是一个节点类,然后节点有一个邻居列表(这是两个城市之间的路线)以及到每个邻居的距离。每个节点最初设置为visited = false。然后使用递归算法枚举每个节点的邻居列表,并在节点被标记为已访问时停止。
  • 你是怎么想到input1的?如new List&lt;String&gt;() {"Sydney","0","1","7","1"}等等
  • 列中的数字是到每个城市的距离,与第一列中城市的排列顺序相同。所以 7 是从悉尼到里约或里约到悉尼的距离。桌子围绕对角线对称。
猜你喜欢
  • 2014-01-16
  • 1970-01-01
  • 1970-01-01
  • 2020-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多