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

简介:

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

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

 

一:概序

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

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

二:构建

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

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.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。

。。。

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

#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

 

总的代码:复杂度很烂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
}

  

相关文章
|
25天前
|
算法 编译器 C++
【狂热算法篇】探秘图论之Dijkstra 算法:穿越图的迷宫的最短路径力量(通俗易懂版)
【狂热算法篇】探秘图论之Dijkstra 算法:穿越图的迷宫的最短路径力量(通俗易懂版)
|
5月前
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
135 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
6月前
|
大数据 UED 开发者
实战演练:利用Python的Trie树优化搜索算法,性能飙升不是梦!
在数据密集型应用中,高效搜索算法至关重要。Trie树(前缀树/字典树)通过优化字符串处理和搜索效率成为理想选择。本文通过Python实战演示Trie树构建与应用,显著提升搜索性能。Trie树利用公共前缀减少查询时间,支持快速插入、删除和搜索。以下为简单示例代码,展示如何构建及使用Trie树进行搜索与前缀匹配,适用于自动补全、拼写检查等场景,助力提升应用性能与用户体验。
102 2
|
5月前
|
存储 算法 程序员
迪杰斯特拉(Dijkstra)算法(C/C++)
迪杰斯特拉(Dijkstra)算法(C/C++)
|
7月前
|
机器学习/深度学习 算法 Java
算法设计(动态规划应用实验报告)实现基于贪婪技术思想的Prim算法、Dijkstra算法
这篇文章介绍了基于贪婪技术思想的Prim算法和Dijkstra算法,包括它们的伪代码描述、Java源代码实现、时间效率分析,并展示了算法的测试用例结果,使读者对贪婪技术及其应用有了更深入的理解。
算法设计(动态规划应用实验报告)实现基于贪婪技术思想的Prim算法、Dijkstra算法
|
7月前
|
算法
路径规划算法 - 求解最短路径 - Dijkstra(迪杰斯特拉)算法
路径规划算法 - 求解最短路径 - Dijkstra(迪杰斯特拉)算法
156 0
|
8月前
|
算法
基于Dijkstra算法的最优行驶路线搜索matlab仿真,以实际城市复杂路线为例进行测试
使用MATLAB2022a实现的Dijkstra算法在城市地图上搜索最优行驶路线的仿真。用户通过鼠标点击设定起点和终点,算法规划路径并显示长度。测试显示,尽管在某些复杂情况下计算路径可能与实际有偏差,但多数场景下Dijkstra算法能找到接近最短路径。核心代码包括图的显示、用户交互及Dijkstra算法实现。算法基于图论,不断更新未访问节点的最短路径。测试结果证明其在简单路线及多数复杂城市路况下表现良好,但在交通拥堵等特殊情况下需结合其他数据提升准确性。
|
8月前
|
资源调度 算法 定位技术
|
8月前
|
算法 Java C++
《经典图论算法》迪杰斯特拉算法(Dijkstra)
这个是求最短路径的迪杰斯特拉算法,另外我还写了50多种《经典图论算法》,每种都使用C++和Java两种语言实现,熟练掌握之后无论是参加蓝桥杯,信奥赛,还是其他比赛,或者是面试,都能轻松应对。
|
9月前
|
算法
详尽分享经典算法题每日演练——第一题百钱买百鸡
详尽分享经典算法题每日演练——第一题百钱买百鸡
71 0

热门文章

最新文章