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


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

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

    一:概序

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

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

    二:构建

        如果大家已经了解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算法的步骤:

    有集合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
    }
    

      

  • 相关阅读:
    JavaScript中严格模式"use strict";需注意的几个雷区:
    React 学习,需要注意几点
    安装了VS2012 还有Update4 我的Silverlight5安装完后 我的Silverlight4项目打不开
    SilverLight抛出 System.InvalidOperationException: 超出了2083 的最大URI
    Dictionary集合运用
    配置OpenCV开发环境心得
    调用第三方库出现的问题
    7-19 答疑课小结
    工欲善其事必先利其器(篇一)
    VMware Ubuntu Kaldi
  • 原文地址:https://www.cnblogs.com/huangxincheng/p/2823042.html
Copyright © 2020-2023  润新知