加载中...

第十七题 Dijkstra算法


或许在生活中,经常会碰到针对某一个问题,在众多的限制条件下,如何去寻找一个最优解?可能大家想到了很多诸如“线性规划”,“动态规划”

这些经典策略,当然有的问题我们可以用贪心来寻求整体最优解,在图论中一个典型的贪心法求最优解的例子就莫过于“最短路径”的问题。

一:概序

   从下图中我要寻找V0到V3的最短路径,你会发现通往他们的两点路径有很多:V0->V4->V3,V0->V1->V3,当然你会认为前者是你要找的最短

路径,那如果说图的顶点非常多,你还会这么轻易的找到吗?下面我们就要将刚才我们那点贪心的思维系统的整理下。

二:构建

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

1.边节点

  这里有点不一样的地方就是我在边上面定义一个vertexs来记录贪心搜索到某一个节点时曾经走过的节点,比如从V0贪心搜索到V3时,我们V3

的vertexs可能存放着V0,V4,V3这些曾今走过的节点,或许最后这三个节点就是我们要寻找的最短路径。

2.Dijkstra算法

首先我们分析下Dijkstra算法的步骤:

有集合M={V0,V1,V2,V3,V4}这样5个元素,我们用

TempVertex表示该顶点是否使用。

Weight表示该Path的权重(默认都为MaxValue)。

Path表示该顶点的总权重。

①. 从集合M中挑选顶点V0为起始点。给V0的所有邻接点赋值,要赋值的前提是要赋值的weight要小于原始的weight,并且排除已经访问过

     的顶点,然后挑选当前最小的weight作为下一次贪心搜索的起点,就这样V0V1为挑选为最短路径,如图2。

②. 我们继续从V1这个顶点开始给邻接点以同样的方式赋值,最后我们发现V0V4为最短路径。也就是图3。

。。。

③. 最后所有顶点的最短路径就这样求出来了 。

  1. #region Dijkstra算法
  2. /// <summary>
  3. /// Dijkstra算法
  4. /// </summary>
  5. public Dictionary<int, Edge> Dijkstra()
  6. {
  7. //收集顶点的相邻边
  8. Dictionary<int, Edge> dic_edges = new Dictionary<int, Edge>();
  9.  
  10. //weight=MaxValue:标识没有边
  11. for (int i = 0; i < graph.vertexsNum; i++)
  12. {
  13. //起始边
  14. var startEdge = i;
  15.  
  16. dic_edges.Add(startEdge, new Edge() { weight = int.MaxValue });
  17. }
  18.  
  19. //取第一个顶点
  20. var start = 0;
  21.  
  22. for (int i = 0; i < graph.vertexsNum; i++)
  23. {
  24. //标记该顶点已经使用过
  25. dic_edges[start].isUse = true;
  26.  
  27. for (int j = 1; j < graph.vertexsNum; j++)
  28. {
  29. var end = j;
  30.  
  31. //取到相邻边的权重
  32. var weight = graph.edges[start, end];
  33.  
  34. //赋较小的权重
  35. if (weight < dic_edges[end].weight)
  36. {
  37. //与上一个顶点的权值累加
  38. var totalweight = dic_edges[start].weight == int.MaxValue ? weight : dic_edges[start].weight + weight;
  39.  
  40. if (totalweight < dic_edges[end].weight)
  41. {
  42. //将该顶点的相邻边加入到集合中
  43. dic_edges[end] = new Edge()
  44. {
  45. startEdge = start,
  46. endEdge = end,
  47. weight = totalweight
  48. };
  49.  
  50. //将上一个边的节点的vertex累加
  51. dic_edges[end].vertexs = new HashSet<int>(dic_edges[start].vertexs);
  52.  
  53. dic_edges[end].vertexs.Add(start);
  54. dic_edges[end].vertexs.Add(end);
  55. }
  56. }
  57. }
  58.  
  59. var min = int.MaxValue;
  60.  
  61. //下一个进行比较的顶点
  62. int minkey = 0;
  63.  
  64. //取start邻接边中的最小值
  65. foreach (var key in dic_edges.Keys)
  66. {
  67. //取当前 最小的 key(使用过的除外)
  68. if (min > dic_edges[key].weight && !dic_edges[key].isUse)
  69. {
  70. min = dic_edges[key].weight;
  71. minkey = key;
  72. }
  73. }
  74.  
  75. //从邻接边的顶点再开始找
  76. start = minkey;
  77. }
  78.  
  79. return dic_edges;
  80. }
  81. #endregion

总的代码:复杂度很烂O(N2)。。。

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Diagnostics;
  6. using System.Threading;
  7. using System.IO;
  8. using System.Threading.Tasks;
  9. namespace ConsoleApplication2
  10. {
  11. public class Program
  12. {
  13. public static void Main()
  14. {
  15. Dictionary<int, string> dic = new Dictionary<int, string>();
  16. MatrixGraph graph = new MatrixGraph();
  17. graph.Build();
  18. var result = graph.Dijkstra();
  19. Console.WriteLine("各节点的最短路径为:");
  20. foreach (var key in result.Keys)
  21. {
  22. Console.WriteLine("{0}", string.Join("->", result[key].vertexs));
  23. }
  24. Console.Read();
  25. }
  26. }
  27. #region 定义矩阵节点
  28. /// <summary>
  29. /// 定义矩阵节点
  30. /// </summary>
  31. public class MatrixGraph
  32. {
  33. Graph graph = new Graph();
  34. public class Graph
  35. {
  36. /// <summary>
  37. /// 顶点信息
  38. /// </summary>
  39. public int[] vertexs;
  40. /// <summary>
  41. /// 边的条数
  42. /// </summary>
  43. public int[,] edges;
  44. /// <summary>
  45. /// 顶点个数
  46. /// </summary>
  47. public int vertexsNum;
  48. /// <summary>
  49. /// 边的个数
  50. /// </summary>
  51. public int edgesNum;
  52. }
  53. #region 矩阵的构建
  54. /// <summary>
  55. /// 矩阵的构建
  56. /// </summary>
  57. public void Build()
  58. {
  59. //顶点数
  60. graph.vertexsNum = 5;
  61. //边数
  62. graph.edgesNum = 6;
  63. graph.vertexs = new int[graph.vertexsNum];
  64. graph.edges = new int[graph.vertexsNum, graph.vertexsNum];
  65. //构建二维数组
  66. for (int i = 0; i < graph.vertexsNum; i++)
  67. {
  68. //顶点
  69. graph.vertexs[i] = i;
  70. for (int j = 0; j < graph.vertexsNum; j++)
  71. {
  72. graph.edges[i, j] = int.MaxValue;
  73. }
  74. }
  75. //定义 6 条边
  76. graph.edges[0, 1] = graph.edges[1, 0] = 2;
  77. graph.edges[0, 2] = graph.edges[2, 0] = 5;
  78. graph.edges[0, 4] = graph.edges[4, 0] = 3;
  79. graph.edges[1, 3] = graph.edges[3, 1] = 4;
  80. graph.edges[2, 4] = graph.edges[4, 2] = 5;
  81. graph.edges[3, 4] = graph.edges[4, 3] = 2;
  82. }
  83. #endregion
  84. #region 边的信息
  85. /// <summary>
  86. /// 边的信息
  87. /// </summary>
  88. public class Edge
  89. {
  90. //开始边
  91. public int startEdge;
  92. //结束边
  93. public int endEdge;
  94. //权重
  95. public int weight;
  96. //是否使用
  97. public bool isUse;
  98. //累计顶点
  99. public HashSet<int> vertexs = new HashSet<int>();
  100. }
  101. #endregion
  102. #region Dijkstra算法
  103. /// <summary>
  104. /// Dijkstra算法
  105. /// </summary>
  106. public Dictionary<int, Edge> Dijkstra()
  107. {
  108. //收集顶点的相邻边
  109. Dictionary<int, Edge> dic_edges = new Dictionary<int, Edge>();
  110. //weight=MaxValue:标识没有边
  111. for (int i = 0; i < graph.vertexsNum; i++)
  112. {
  113. //起始边
  114. var startEdge = i;
  115. dic_edges.Add(startEdge, new Edge() { weight = int.MaxValue });
  116. }
  117. //取第一个顶点
  118. var start = 0;
  119. for (int i = 0; i < graph.vertexsNum; i++)
  120. {
  121. //标记该顶点已经使用过
  122. dic_edges[start].isUse = true;
  123. for (int j = 1; j < graph.vertexsNum; j++)
  124. {
  125. var end = j;
  126. //取到相邻边的权重
  127. var weight = graph.edges[start, end];
  128. //赋较小的权重
  129. if (weight < dic_edges[end].weight)
  130. {
  131. //与上一个顶点的权值累加
  132. var totalweight = dic_edges[start].weight == int.MaxValue ? weight : dic_edges[start].weight + weight;
  133. if (totalweight < dic_edges[end].weight)
  134. {
  135. //将该顶点的相邻边加入到集合中
  136. dic_edges[end] = new Edge()
  137. {
  138. startEdge = start,
  139. endEdge = end,
  140. weight = totalweight
  141. };
  142. //将上一个边的节点的vertex累加
  143. dic_edges[end].vertexs = new HashSet<int>(dic_edges[start].vertexs);
  144. dic_edges[end].vertexs.Add(start);
  145. dic_edges[end].vertexs.Add(end);
  146. }
  147. }
  148. }
  149. var min = int.MaxValue;
  150. //下一个进行比较的顶点
  151. int minkey = 0;
  152. //取start邻接边中的最小值
  153. foreach (var key in dic_edges.Keys)
  154. {
  155. //取当前 最小的 key(使用过的除外)
  156. if (min > dic_edges[key].weight && !dic_edges[key].isUse)
  157. {
  158. min = dic_edges[key].weight;
  159. minkey = key;
  160. }
  161. }
  162. //从邻接边的顶点再开始找
  163. start = minkey;
  164. }
  165. return dic_edges;
  166. }
  167. #endregion
  168. }
  169. #endregion
  170. }


还没有评论.