经典算法题每日演练——第十七题 Dijkstra算法

原文: 经典算法题每日演练——第十七题 Dijkstra算法

 

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

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

 

一:概序

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

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

经典算法题每日演练——第十七题 Dijkstra算法

二:构建

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

1.边节点

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

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

 1 #region 边的信息

 2         /// <summary>

 3         /// 边的信息

 4         /// </summary>

 5         public class Edge

 6         {

 7             //开始边

 8             public int startEdge;

 9 

10             //结束边

11             public int endEdge;

12 

13             //权重

14             public int weight;

15 

16             //是否使用

17             public bool isUse;

18 

19             //累计顶点

20             public HashSet<int> vertexs = new HashSet<int>();

21         }

22         #endregion

2.Dijkstra算法

经典算法题每日演练——第十七题 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)。。。

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Diagnostics;

using System.Threading;

using System.IO;

using System.Threading.Tasks;



namespace ConsoleApplication2

{

    public class Program

    {

        public static void Main()

        {

            Dictionary<int, string> dic = new Dictionary<int, string>();



            MatrixGraph graph = new MatrixGraph();



            graph.Build();



            var result = graph.Dijkstra();



            Console.WriteLine("各节点的最短路径为:");



            foreach (var key in result.Keys)

            {

                Console.WriteLine("{0}", string.Join("->", result[key].vertexs));

            }



            Console.Read();

        }

    }



    #region 定义矩阵节点

    /// <summary>

    /// 定义矩阵节点

    /// </summary>

    public class MatrixGraph

    {

        Graph graph = new Graph();



        public class Graph

        {

            /// <summary>

            /// 顶点信息

            /// </summary>

            public int[] vertexs;



            /// <summary>

            /// 边的条数

            /// </summary>

            public int[,] edges;



            /// <summary>

            /// 顶点个数

            /// </summary>

            public int vertexsNum;



            /// <summary>

            /// 边的个数

            /// </summary>

            public int edgesNum;

        }



        #region 矩阵的构建

        /// <summary>

        /// 矩阵的构建

        /// </summary>

        public void Build()

        {

            //顶点数

            graph.vertexsNum = 5;



            //边数

            graph.edgesNum = 6;



            graph.vertexs = new int[graph.vertexsNum];



            graph.edges = new int[graph.vertexsNum, graph.vertexsNum];



            //构建二维数组

            for (int i = 0; i < graph.vertexsNum; i++)

            {

                //顶点

                graph.vertexs[i] = i;



                for (int j = 0; j < graph.vertexsNum; j++)

                {

                    graph.edges[i, j] = int.MaxValue;

                }

            }



            //定义 6 条边

            graph.edges[0, 1] = graph.edges[1, 0] = 2;

            graph.edges[0, 2] = graph.edges[2, 0] = 5;

            graph.edges[0, 4] = graph.edges[4, 0] = 3;

            graph.edges[1, 3] = graph.edges[3, 1] = 4;

            graph.edges[2, 4] = graph.edges[4, 2] = 5;

            graph.edges[3, 4] = graph.edges[4, 3] = 2;



        }

        #endregion



        #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



        #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

    }

    #endregion

}

  

经典算法题每日演练——第十七题 Dijkstra算法

 

你可能感兴趣的:(dijkstra)