基于C#实现Dijkstra算法

或许在生活中,经常会碰到针对某一个问题,在众多的限制条件下,如何去寻找一个最优解?可能大家想到了很多诸如“线性规划”,“动态规划”这些经典策略,当然有的问题我们可以用贪心来寻求整体最优解,在图论中一个典型的贪心法求最优解的例子就莫过于“最短路径”的问题。

一、概序

从下图中我要寻找 V0 到 V3 的最短路径,你会发现通往他们的两点路径有很多:V0->V4->V3,V0->V1->V3,当然你会认为前者是你要找的最短路径,那如果说图的顶点非常多,你还会这么轻易的找到吗?下面我们就要将刚才我们那点贪心的思维系统的整理下。
image.png

二、构建

如果大家已经了解 Prim 算法,那么 Dijkstra 算法只是在它的上面延伸了下,其实也是很简单的。

2.1、边节点

这里有点不一样的地方就是我在边上面定义一个 vertexs 来记录贪心搜索到某一个节点时曾经走过的节点,比如从 V0 贪心搜索到 V3 时,我们 V3 的 vertexs 可能存放着 V0,V4,V3 这些曾今走过的节点,或许最后这三个节点就是我们要寻找的最短路径。

 #region 边的信息
 /// <summary>
 /// 边的信息
 /// </summary>
 public class Edge
 {
     //开始边
     public int startEdge;

     //结束边
     public int endEdge;

     //权重
     public int weight;

     //是否使用
     public bool isUse;

     //累计顶点
     public HashSet<int> vertexs = new HashSet<int>();
 }
 #endregion

2.2、Dijkstra 算法

image.png
首先我们分析下 Dijkstra 算法的步骤:
有集合 M={V0,V1,V2,V3,V4}这样 5 个元素,我们用 TempVertex 表示该顶点是否使用。
Weight 表示该 Path 的权重(默认都为 MaxValue)。
Path 表示该顶点的总权重。
①. 从集合 M 中挑选顶点 V0 为起始点。给 V0 的所有邻接点赋值,要赋值的前提是要赋值的 weight 要小于原始的 weight,并且排除已经访问过的顶点,然后挑选当前最小的 weight 作为下一次贪心搜索的起点,就这样 V0V1 为挑选为最短路径,如图 2。
②. 我们继续从 V1 这个顶点开始给邻接点以同样的方式赋值,最后我们发现 V0V4 为最短路径。也就是图 3。
……
③. 最后所有顶点的最短路径就这样求出来了 。

 #region Dijkstra算法
 /// <summary>
 /// Dijkstra算法
 /// </summary>
 public Dictionary<int, Edge> Dijkstra()
 {
     //收集顶点的相邻边
     Dictionary<int, Edge> dic_edges = new Dictionary<int, Edge>();

     //weight=MaxValue:标识没有边
     for (int i = 0; i < graph.vertexsNum; i++)
     {
         //起始边
         var startEdge = i;

         dic_edges.Add(startEdge, new Edge() { weight = int.MaxValue });
     }

     //取第一个顶点
     var start = 0;

     for (int i = 0; i < graph.vertexsNum; i++)
     {
         //标记该顶点已经使用过
         dic_edges[start].isUse = true;

         for (int j = 1; j < graph.vertexsNum; j++)
         {
             var end = j;

             //取到相邻边的权重
             var weight = graph.edges[start, end];

             //赋较小的权重
             if (weight < dic_edges[end].weight)
             {
                 //与上一个顶点的权值累加
                 var totalweight = dic_edges[start].weight == int.MaxValue ? weight : dic_edges[start].weight + weight;

                 if (totalweight < dic_edges[end].weight)
                 {
                     //将该顶点的相邻边加入到集合中
                     dic_edges[end] = new Edge()
                     {
                         startEdge = start,
                         endEdge = end,
                         weight = totalweight
                     };

                     //将上一个边的节点的vertex累加
                     dic_edges[end].vertexs = new HashSet<int>(dic_edges[start].vertexs);

                     dic_edges[end].vertexs.Add(start);
                     dic_edges[end].vertexs.Add(end);
                 }
             }
         }

         var min = int.MaxValue;

         //下一个进行比较的顶点
         int minkey = 0;

         //取start邻接边中的最小值
         foreach (var key in dic_edges.Keys)
         {
             //取当前 最小的 key(使用过的除外)
             if (min > dic_edges[key].weight && !dic_edges[key].isUse)
             {
                 min = dic_edges[key].weight;
                 minkey = key;
             }
         }

         //从邻接边的顶点再开始找
         start = minkey;
     }

     return dic_edges;
 }
 #endregion

image.png

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
C#代码实现Dijkstra算法的基本步骤如下: 1. 定义一个节点类,包含节点名称、邻接节点、边权重等信息。 2. 定义一个图类,包含节点集合、边集合等信息。 3. 实现Dijkstra算法,首先初始化起点到各个节点的距离为无穷大,起点到自身的距离为0。然后遍历所有节点,找到距离起点最近的节点,并更新其邻接节点的距离。重复此过程直到所有节点都被遍历过。 4. 最后根据起点到各个节点的距离,构建最短路径。 下面是一个简单的C#代码实现Dijkstra算法的例子: ``` using System; using System.Collections.Generic; public class Node { public string Name { get; set; } public List<Edge> Edges { get; set; } public int Distance { get; set; } public bool Visited { get; set; } public Node(string name) { Name = name; Edges = new List<Edge>(); Distance = int.MaxValue; Visited = false; } } public class Edge { public Node Start { get; set; } public Node End { get; set; } public int Weight { get; set; } public Edge(Node start, Node end, int weight) { Start = start; End = end; Weight = weight; } } public class Graph { public List<Node> Nodes { get; set; } public List<Edge> Edges { get; set; } public Graph() { Nodes = new List<Node>(); Edges = new List<Edge>(); } public void AddNode(Node node) { Nodes.Add(node); } public void AddEdge(Node start, Node end, int weight) { Edge edge = new Edge(start, end, weight); start.Edges.Add(edge); Edges.Add(edge); } public void Dijkstra(Node start) { start.Distance = 0; Node current = start; while (current != null) { current.Visited = true; foreach (Edge edge in current.Edges) { Node neighbor = edge.End; if (!neighbor.Visited) { int distance = current.Distance + edge.Weight; if (distance < neighbor.Distance) { neighbor.Distance = distance; } } } current = null; int shortestDistance = int.MaxValue; foreach (Node node in Nodes) { if (!node.Visited && node.Distance < shortestDistance) { shortestDistance = node.Distance; current = node; } } } } public List<Node> GetShortestPath(Node start, Node end) { List<Node> path = new List<Node>(); Node current = end; while (current != start) { path.Insert(0, current); foreach (Edge edge in current.Edges) { Node neighbor = edge.End; if (current.Distance == neighbor.Distance + edge.Weight) { current = neighbor; break; } } } path.Insert(0, start); return path; } } // 使用示例 Graph graph = new Graph(); Node a = new Node("A"); Node b = new Node("B"); Node c = new Node("C"); Node d = new Node("D"); Node e = new Node("E"); graph.AddNode(a); graph.AddNode(b); graph.AddNode(c); graph.AddNode(d); graph.AddNode(e); graph.AddEdge(a, b, 5); graph.AddEdge(a, c, 3); graph.AddEdge(b, c, 1); graph.AddEdge(b, d, 2); graph.AddEdge(c, d, 4); graph.AddEdge(d, e, 6); graph.Dijkstra(a); List<Node> path = graph.GetShortestPath(a, e); foreach (Node node in path) { Console.Write(node.Name + " "); } // 输出结果为:A C B D E --相关问题--:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

神仙别闹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值