精华内容
下载资源
问答
  • 最短路径算法

    2015-06-20 09:18:37
    而从学校出来到现在,最短路径算法都没有实际运用过,最近在一个GIS项目中总算用到了,于是乎把教材重温了下,同时查阅了网上一些资料,借鉴了一些别人的东西,并顺利用写进了项目中,文中的主要代码来自于园子里

    最短路径算法——Dijkstra and Floyd算法

    最短路径算法——Dijkstra算法

    Dijkstra算法在刚开始在学数据结构的时候,完全没弄明白,主要是也不怎么想去弄明白。而从学校出来到现在,最短路径算法都没有实际运用过,最近在一个GIS项目中总算用到了,于是乎把教材重温了下,同时查阅了网上一些资料,借鉴了一些别人的东西,并顺利用写进了项目中,文中的主要代码来自于园子里的一位大哥,这位大哥对通用框架的研究很深入,他的链接为:http://zhuweisky.cnblogs.com/archive/2005/09/29/246677.html(最短路径)。另外,文章的最后面的一些链接是我找资料的时候用到过的,有兴趣

    的朋友可以去看看。

    最短路径分析在事故抢修、交通指挥、GPS导航等行业应用中使用的非常广泛, 以至于大多数GIS平台都会把这个分析功能作为一个最基础的功能集成进去,如ARCGIS,SuperMap等。个人感觉想要了解这个算法的来龙去脉,一方面是参与相关书籍仔细理解,另外一个最重要的是要去调试代码。由于历史原因,对于书上的伪C代码,我是完全不感兴趣的,而且由于有严格的数学证明,所以看起来相对较难,而对于面向对象实现的算法,我很感兴趣,也感觉很容易理解,所以一边针对C#实现的面向对象代码再一边对照书籍,感觉理解的层次就加深了。

    Dijkstra算法又称为单源最短路径,所谓单源是在一个有向图中,从一个顶点出发,求该顶点至所有可到达顶点的最短路径问题。要顺利实现算法,要求理解Dijstra的算法,同时还要理解图的一些基本概念,图由节点和边构成,将节点和边看成对象,每个对象有自己的特有属性,如在GIS中,一个节点必须都有ID,横坐标,纵坐标等基本属性,边有起点节点,终点节点,长度等属性,而最短路径分析,就是根据边的长度(权值)进行分析的。

    边的定义如下:

    public class Edge
    {
    public string StartNodeID;
    public string EndNodeID;
    public double Weight; //权值,代价
    }

    节点的定义:

    复制代码
    public class Node
    {
    private string iD ;
    private List<Edge> edgeList ;//Edge的集合--出边表

    public Node(string id )
    {
    this.iD = id ;
    this.edgeList = new List<Edge>() ;
    }

    #region property

    public string ID
    {
    get
    {
    return this.iD ;
    }
    }

    public List<Edge> EdgeList
    {
    get
    {
    return this.edgeList ;
    }
    }
    #endregion
    }
    复制代码

    本次用于分析的拓扑图如下:(A为起点,D为终点,边上的数字为权值)

      

    利用上述的边与节点的定义,可以通过代码简单的构成如下图:

    复制代码
    public class Graph
    {
    public List<Node> m_nodeList = null;
    public Graph()
    {
    m_nodeList = new List<Node>();
    }

    ///<summary>
    /// 获取图的节点集合
    ///</summary>
    public List<Node> NodeList
    {
    get { return this.m_nodeList; }
    }

    ///<summary>
    /// 初始化拓扑图
    ///</summary>
    public void Init()
    {
    //***************** B Node *******************
    Node aNode = new Node("A");
    m_nodeList.Add(aNode);
    //A -> B
    Edge aEdge1 = new Edge();
    aEdge1.StartNodeID = aNode.ID;
    aEdge1.EndNodeID = "B";
    aEdge1.Weight = 10;
    aNode.EdgeList.Add(aEdge1);
    //A -> C
    Edge aEdge2 = new Edge();
    aEdge2.StartNodeID = aNode.ID;
    aEdge2.EndNodeID = "C";
    aEdge2.Weight = 20;
    aNode.EdgeList.Add(aEdge2);
    //A -> E
    Edge aEdge3 = new Edge();
    aEdge3.StartNodeID = aNode.ID;
    aEdge3.EndNodeID = "E";
    aEdge3.Weight = 30;
    aNode.EdgeList.Add(aEdge3);

    //***************** B Node *******************
    Node bNode = new Node("B");
    m_nodeList.Add(bNode);
    //B -> C
    Edge bEdge1 = new Edge();
    bEdge1.StartNodeID = bNode.ID;
    bEdge1.EndNodeID = "C";
    bEdge1.Weight = 5;
    bNode.EdgeList.Add(bEdge1);
    //B -> E
    Edge bEdge2 = new Edge();
    bEdge2.StartNodeID = bNode.ID;
    bEdge2.EndNodeID = "E";
    bEdge2.Weight = 10;
    bNode.EdgeList.Add(bEdge2);

    //***************** C Node *******************
    Node cNode = new Node("C");
    m_nodeList.Add(cNode);
    //C -> D
    Edge cEdge1 = new Edge();
    cEdge1.StartNodeID = cNode.ID;
    cEdge1.EndNodeID = "D";
    cEdge1.Weight = 30;
    cNode.EdgeList.Add(cEdge1);

    //***************** D Node *******************
    Node dNode = new Node("D");
    m_nodeList.Add(dNode);

    //***************** E Node *******************
    Node eNode = new Node("E");
    m_nodeList.Add(eNode);
    //E -> D
    Edge eEdge1 = new Edge();
    eEdge1.StartNodeID = eNode.ID;
    eEdge1.EndNodeID = "D";
    eEdge1.Weight = 20;
    eNode.EdgeList.Add(eEdge1);
    }
    }
    复制代码

    有了拓扑节点和边,就可以根据算法构造其他最短路径分析的对象了,主要步骤如下:

    Ø 初始化图中的从A出发的路径集合:

    复制代码
    ///<summary>
    /// PlanCourse 缓存从源节点到其它任一节点的最小权值路径(路径表)
    ///</summary>
    public class PlanCourse
    {
    private Hashtable htPassedPath;

    #region ctor
    public PlanCourse(List<Node> nodeList, string originID)
    {
    this.htPassedPath = new Hashtable();

    Node originNode = null;
    foreach (Node node in nodeList)
    {
    if (node.ID == originID)
    {
    originNode = node;
    }
    else
    {
    PassedPath pPath = new PassedPath(node.ID);
    this.htPassedPath.Add(node.ID, pPath);
    }
    }

    if (originNode == null)
    {
    throw new Exception("The origin node is not exist !");
    }

    this.InitializeWeight(originNode);
    }

    ///<summary>
    /// 通过指定节点的边的权值初始化路径表
    ///</summary>
    ///<param name="originNode"></param>
    private void InitializeWeight(Node originNode)
    {
    if ((originNode.EdgeList == null) || (originNode.EdgeList.Count == 0))
    {
    return;
    }

    foreach (Edge edge in originNode.EdgeList)
    {
    PassedPath pPath = this[edge.EndNodeID];
    if (pPath == null)
    {
    continue;
    }

    pPath.PassedIDList.Add(originNode.ID);
    pPath.Weight = edge.Weight;
    }
    }
    #endregion

    ///<summary>
    /// 获取指定点的路径表
    ///</summary>
    ///<param name="nodeID"></param>
    ///<returns></returns>
    public PassedPath this[string nodeID]
    {
    get
    {
    return (PassedPath)this.htPassedPath[nodeID];
    }
    }
    }
    复制代码

    Ø 从A中最短路径集合中找到一个最短的路径点Vi开始分析

    复制代码
    ///<summary>
    /// 从PlanCourse取出一个当前累积权值最小,并且没有被处理过的节点
    ///</summary>
    ///<returns></returns>
    private Node GetMinWeightRudeNode(PlanCourse planCourse, List<Node> nodeList, string originID)
    {
    double weight = double.MaxValue;
    Node destNode = null;

    foreach (Node node in nodeList)
    {
    if (node.ID == originID)
    {
    continue;
    }

    PassedPath pPath = planCourse[node.ID];
    if (pPath.BeProcessed)
    {
    continue;
    }

    if (pPath.Weight < weight)
    {
    weight = pPath.Weight;
    destNode = node;
    }
    }

    return destNode;
    }
    复制代码

    Ø 修正从A出发至Vi最短路径,并重新选择另一个最短路径点Vj点开始分析,重新执行上述步骤的路径分析

    复制代码
    while (curNode != null)
    {
    PassedPath curPath = planCourse[curNode.ID];
    foreach (Edge edge in curNode.EdgeList)
    {
    PassedPath targetPath = planCourse[edge.EndNodeID];
    double tempWeight = curPath.Weight + edge.Weight;

    if (tempWeight < targetPath.Weight)
    {
    targetPath.Weight = tempWeight;
    targetPath.PassedIDList.Clear();

    for (int i = 0; i < curPath.PassedIDList.Count; i++)
    {
    targetPath.PassedIDList.Add(curPath.PassedIDList[i].ToString());
    }

    targetPath.PassedIDList.Add(curNode.ID);
    }
    }

    //标志为已处理
    planCourse[curNode.ID].BeProcessed = true;
    //获取下一个未处理节点
    curNode = this.GetMinWeightRudeNode(planCourse, nodeList, originID);
    }
    复制代码

    Ø 重复上述两个步骤,一直到所有的对象都分析完为止。

    Ø 这个时候的路径集合表中已经保存了从A到任意一点的最短路径集合了。

    复制代码
    ///<summary>
    /// 从PlanCourse表中取出目标节点的PassedPath,这个PassedPath即是规划结果
    ///</summary>
    ///<returns></returns>
    private RoutePlanResult GetResult(PlanCourse planCourse, string destID)
    {
    PassedPath pPath = planCourse[destID];

    if (pPath.Weight == int.MaxValue)
    {
    RoutePlanResult result1 = new RoutePlanResult(null, int.MaxValue);
    return result1;
    }

    string[] passedNodeIDs = new string[pPath.PassedIDList.Count];
    for (int i = 0; i < passedNodeIDs.Length; i++)
    {
    passedNodeIDs[i] = pPath.PassedIDList[i].ToString();
    }

    RoutePlanResult result = new RoutePlanResult(passedNodeIDs, pPath.Weight);

    return result;
    }
    复制代码

    最短路径的结果类定义如下:

    复制代码
    public class RoutePlanResult
    {
    public RoutePlanResult(string[] passedNodes, double value)
    {
    m_resultNodes = passedNodes;
    m_value = value;
    }

    private string[] m_resultNodes;
    ///<summary>
    /// 最短路径经过的节点
    ///</summary>
    public string[] ResultNodes
    {
    get { return m_resultNodes; }
    }

    private double m_value;
    ///<summary>
    /// 最短路径的值
    ///</summary>
    private double Value
    {
    get { return m_value; }
    }
    }
    复制代码

    Demo下载:最短路径分析demo

    其他技术文章链接:

    1. Dijkstra算法http://www.cnblogs.com/gzydn/archive/2009/07/09/1520019.html

    2.最短路径 dijsktra 模板http://www.cnblogs.com/yezizhe/archive/2009/04/16/1437062.html

    3. Shortest Path Problem: Dijkstra's Algorithm http://www.codeproject.com/KB/recipes/Shortest_Path_Problem.aspx

    4. Dijkstra:Shortest Route Calculation - Object Oriented
    http://www.codeproject.com/KB/recipes/ShortestPathCalculation.aspx

    5.推荐:路径规划(最短路径)算法C#实现http://zhuweisky.cnblogs.com/archive/2005/09/29/246677.html

    6.Floyd最短路径算法http://www.cnblogs.com/gzydn/archive/2009/07/10/1520646.html

    7.【最短路径算法及应用】

    http://blog.csdn.net/baggioan/archive/2007/07/28/1713294.aspx

    作者彭立云
    本文版权归作者所有,欢迎转载,但必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
    展开全文
  • Dijkstra路由最短路径算法Dijkstra路由最短路径算法算法实现 Dijkstra路由最短路径算法 先简单解释一下该算法的字面含义:根据百度百科的解释为:是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径...

    Dijkstra路由最短路径算法

    Dijkstra路由最短路径算法

    先简单解释一下该算法的字面含义:根据百度百科的解释为:是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。

    下面开始图文解释:

    我们要寻找从a点到f点的最短路径,上面的数字表示权重,代表每经过一个节点所要付出的代价。
    在这里插入图片描述
    从a点选取一个权重最小的节点c:

    然后根据选中的节点进行更新,更新的是从a点到各个节点的权重。
    在这里插入图片描述
    然后更新下一个权重最小的节点b:

    去掉的节点表示不会再从那个节点路过,因为有更优秀的节点取代。
    在这里插入图片描述
    接下来选取节点e:
    在这里插入图片描述
    接下来选取d:

    该节点选取后就能更新完所有相关的最终节点,确定出最佳路线。
    在这里插入图片描述

    算法实现

    public static final int INF = 99999;
        public static final int[][] a = {
    
                {0,300,100,INF,INF,INF}, //顶点a
                {300,0,150,50,INF,INF}, //顶点b
                {100,150,0,200,200,INF},//顶点c
                {INF,50,200,0,600,400},//顶点d
                {INF,INF,200,600,0,700},//顶点e
                {INF,INF,INF,400,700,0} //顶点f
    
        };
        //是否访问过该节点
        public static boolean[] b = new boolean[a.length];
        //前驱节点 通过该节点可以追踪最佳链路
        public static int[] prev = new int[a.length];
        //各个节点的最短路径
        public static int[] dist = new int[a.length];
    
        public static void solution(){
    
            dist[0] = 0;
            b[0] = true;
            int tmp;
            int k = 0;
            //从顶点a到定点f
            for (int i = 0; i < a[0].length; i++) {
                dist[i] = a[0][i];
            }
            // 从剩余节点中选取权重最小的节点开始
            for (int i = 1; i < a[0].length; i++) {
    
                int min = INF;
                for (int j = 0; j < a[0].length; j++) {
                    if (!b[j] && dist[j] < min) {
                        min = dist[j];
                        k = j;
                    }
                }
    
                b[k] = true;
                // 选中一个节点后 根据这个节点更新所有和它相关的节点
                for (int j = 0; j < a[k].length; j++) {
                    tmp = (min + a[k][j]) < 0 ? INF : (min + a[k][j]);
                    if (!b[j] && tmp < dist[j]) {
                        dist[j] = tmp;
                        prev[j] = k;
                    }
                }
            }
    
    
    
        }
    

    也解答了我上一篇文章的一个遗留问题

    按下回车的那一刻发生了什么?

    更多干货内容请关注公众号:“计算机基础爱好者”

    谢谢

    展开全文
  • 图论算法-最短路径算法 无权最短路径 从图G中,选择s为开始的点,从s到s的最短路径是长为0的路径,将这个信息做个标记;然后开始寻找所有与s距离为1的顶点,将这些顶点做标记;然后开始找出从s出发最短路径恰为2的...

    图论算法-最短路径算法

    无权最短路径

    从图G中,选择s为开始的点,从s到s的最短路径是长为0的路径,将这个信息做个标记;然后开始寻找所有与s距离为1的顶点,将这些顶点做标记;然后开始找出从s出发最短路径恰为2的顶点;直到所有顶点已经被计算。

    这种搜索一个图的方法称为***广度优先搜索(breadth-first search)*。该方法按层处理顶点:距开始点最近的那些顶点首先被赋值,而最远的那些顶点最后被赋值。这很像对树的层序遍历(level-order traversal)

    伪代码

    void Unweighted(Table T)	// Assume T is intialized
    {
        int CurrDist;
        Vertex v,w;
        
        for(CurrDist = 0; CurrDist < NumVertex; CurrDist++)
            for each vertex v	// 使用 v 遍历每个点
                if (!T[v].know && T[v].Dist == CurrDist)
                {
                    T[v].know = True;	// 表示表 T 中 v 顶点已被访问过
                    for each w adjacency to v	// 使用 w 遍历 v 的所有邻接点
                        if(T[w].dist == Infinity)	// 表示顶点 w 的距离是否已被更新过
                        {
                            T[w].dist = CurrDist + 1;
                            T[w].path = v;
                        }
                }
    }
    

    由于双层嵌套for循环,因此该算法的运行时间为O(V2)O(\left | V\right |^2)。这个效率明显地低,因为尽管所有的顶点早就被访问过(表 T 中know为True),但是外层循环还是要继续。

    使用类似于拓扑排序的做法来排除这种低效性,使用一个队列优化,在迭代开始的时候,队列只含有距离为CurrDistCurrDist的那些顶点。我们添加CurrDist+1CurrDist+1的那些邻接顶点时,由于它们自队尾入队,因此这就保证它们直到所有距离为CurrDistCurrDist的那些邻接顶点都被处理之后才被处理。

    伪代码

    void Unweighted(Table T)
    {
        Queue Q;
        Vertex v, w;
        
        Q = CreateQueue(NumVertex);
        MakeEmpty(Q);
        // Enqueue the start vertex S, determined elsewhere
        Enqueue(S, Q);
        while(!IsEmpty(Q))
        {
            v = Dequeue(Q);
            
            for each w adjacency to v
                if(T[w].dist == Infinity)
                {
                    T[w].dist = T[v].dist + 1;
                    t[w].path = v;
                    Enqueue(w, Q);
                }
        }
        DisposeQueue(Q);	// Free the memory
    }
    

    Dijkstra算法

    解决单源最短路径问题的一般方法叫Dijkstra算法。这个有历史的解法是*贪婪算法(greedy algorithm)*最好的例子。贪婪算法一般地分阶段求解一个问题,在每个阶段它都把当前出现的当作是最好的去处理。

    在每个阶段,Dijkstra算法选择一个顶点vv,它在所有未知顶点中具有最小的dvd_v,同时算法声明从ssvv的最短路径是已知的。阶段的其余部分有dwd_w值的更新的工作组成。

    伪代码

    void Dijkstra(Table T)
    {
        Vertex v, w;
        
        for(;;)
        {
            v = smallest unknown distance vertex;
            if(v == NotAVertex)
                break;
            T[v].known = True;
            for each w adjacency to v
                if(!T[w].known)
                    if(T[v].dist + Cvw < T[w].dist)
                    {
                        // Update w
                        Decrease(T[w].dist to T[v].dist + Cvw);
                        T[w].path = v;
                    }
        }
    }
    

    只要没有边的值为负,该算法总能够顺利完成。如果任何一边出现负值,则算法可能得出错误的答案。总的运行时间为O(V2)O(\left | V\right |^2)

    具有负边值的图

    如果图出现负边值,那么Dijkstra算法是行不通的。

    把带权和不带权的算法结合起来将会解决这个问题。开始,我们把开始的顶点s放入到队列中,然后,在每一阶段我们让一个顶点v出队。找出所有与v邻接的顶点w,使得d_w>d_v+c_{v,w}。然后更新,d_w和p_w,并在w不在队列中的时候把它放到队列中。

    伪代码

    void WeightedNegative(Table T)
    {
        Queue Q;
        Vertex v, w;
        
        Q = CreateQueue(NumVertex);
        MakeEmpty(Q);
       
        while(!IsEmpty(Q))
        {
            v = Dequeue(Q);
            for each w adjacency to v
                if(T[v].dist + Cvw < T[w].dist)
                {
                    // Update w
                    T[w].dist = T[v].dist + Cvw;
                    T[w].path = v;
                    if(w is not already in Q)
                        Enqueue(w, Q);
                }
        }
        DisposeQueue(Q);	// Free the memory
    }
    
    展开全文
  • 最短路径算法-2

    2020-10-08 17:47:52
    最短路径算法-2 在这部分的最短路径算法中,我将介绍两种算法:Dijkstra和Bellman-Ford算法。 Dijkstra算法 Dijkstra算法是用来求两点之间最短路径的算法,适用于正权图,算法流程如下: 1.将所有节点标记为未访问并...

    最短路径算法-2

    在这部分的最短路径算法中,我将介绍两种算法:Dijkstra和Bellman-Ford算法。

    Dijkstra算法

    Dijkstra算法是用来求两点之间最短路径的算法,适用于正权图,算法流程如下:

    1.将所有节点标记为未访问并将其存储。
    2.将初始节点的距离设置为零,将其他节点的距离设置为无穷大。
    3.选择距离最近的未访问节点,它是当前节点。
    4.查找当前节点的未访问邻居,并计算它们到当前节点的距离。将新计算的距离与分配的距离进行比较,然后保存较小的距离。
    5.将当前节点标记为已访问,并将其从未访问的集中删除。
    6.如果访问了目标节点(在计划两个特定节点之间的路由时),或者未访问的节点之间的最小距离是无穷大,则停止。如果不是,请重复步骤3-6。

    下面我们结合一个实际的例子来说明上述算法流程,假设集合S保存访问过的节点,集合Q保存未访问的节点。

    在这里插入图片描述

    如上图,每一条边的权重都是正数,现在需要找到A到B的最短路径。首先将所有节点保存在Q中,初始节点距离为0,其余节点的距离为无穷大。接着,选择距离最近的未访问节点A为当前节点,查找当前节点的未访问邻居(B、C),并计算它们到当前节点的距离(10,3),将新计算的距离与分配的距离(无穷大)进行比较,然后保存较小的距离(10,3)。将当前节点标记为已访问,并将其从未访问的集中删除(Q集合删掉A节点)。一直重复上述步骤直到访问到了节点B,如图中所示。这时即可得出A到B的最短路径为7。

    下面根据上述算法流程来编写程序,可以看到我们每次需要选择距离最短的节点进行访问,那么可以利用优先队列数据结构实现这个过程。利用二叉堆实现的优先队列的时间复杂度更小,可以提高算法效率。

    算法中所用的优先队列代码如下,其详细介绍在博客:优先队列

        class PriorityQueue:
        """Min-heap-based priority queue, using 1-based indexing. Adapted from CLRS.
        
        Augmented to include a map of keys to their indices in the heap so that
        key lookup is constant time and decrease_key(key) is O(log n) time.
        """
        
        def __init__(self):
            """Initializes the priority queue."""
            self.heap = [None] # To make the index 1-based.
            self.key_index = {} # key to index mapping.
        
        def __len__(self):
            return len(self.heap) - 1
           
        def __getitem__(self, i):
            return self.heap[i]
    
        def __setitem__(self, i, key):
            self.heap[i] = key
    
        def decrease_key(self, key):
            """Decreases the value of the key if it is in the priority queue and 
            maintains the heap property."""
            index = self.key_index[key]
            if index:
                self._decrease_key(index, key)
        
        def insert(self, key):
            """Inserts a key into the priority queue."""
            self.heap.append(key)
            self.key_index[key] = len(self)
            self._decrease_key(len(self), key)
    
        def extract_min(self):
            """Removes and returns the minimum key."""
            if len(self) < 1:
                return None
            self._swap(1, len(self))
            min = self.heap.pop()
            del self.key_index[min]
            self._min_heapify(1)
            return min
        
        def _decrease_key(self, i, key):
            """Decreases key at a give index.
            
            Args:
                i: index of the key.
                key: key with decreased value.
            """
            while i > 1:
                parent = i // 2
                if self[parent] > key:
                    self._swap(i, parent)
                    i = parent
                else:
                    break
                
        def _min_heapify(self, i):
            """Restores the heap property from index i downwards."""
            l = 2 * i
            r = 2 * i + 1
            smallest = i
            if l <= len(self) and self[l] < self[i]:
                smallest = l
            if r <= len(self) and self[r] < self[smallest]:
                smallest = r
            if smallest != i:
                self._swap(i, smallest)
                self._min_heapify(smallest)
    
        def _swap(self, i, j):
            # Swaps the key at indices i and j and updates the key to index map.
            self.heap[i], self.heap[j] = self.heap[j], self.heap[i]
            self.key_index[self.heap[i]], self.key_index[self.heap[j]] = i, j
    
    

    同时需要NodeDistancePair类将节点封装起来,以便优先队列根据距离取出节点。

    class NodeDistancePair(object):
        """Wraps a node and its distance representing it in the priority queue."""
    
        def __init__(self, node, distance):
            """Creates a NodeDistancePair to be used as a key in the priority queue.
            """
            self.node = node
            self.distance = distance
    
        def __lt__(self, other):
            # :nodoc: Delegate comparison to distance.
            return (self.distance < other.distance or
                    (self.distance == other.distance and
                     id(self.node) < id(other.node)))
    
        def __le__(self, other):
            # :nodoc: Delegate comparison to distance.
            return (self.distance < other.distance or
                    (self.distance == other.distance and
                     id(self.node) <= id(other.node)))
    
        def __gt__(self, other):
            # :nodoc: Delegate comparison to distance.
            return (self.distance > other.distance or
                    (self.distance == other.distance and
                     id(self.node) > id(other.node)))
    
        def __ge__(self, other):
            # :nodoc: Delegate comparison to distance.
            return (self.distance > other.distance or
                    (self.distance == other.distance and
                     id(self.node) >= id(other.node)))
    

    graph也需要一个类进行封装,定义其表示的方法:

    class Vertex(object):
        def __init__(self, name, d=None, p=None):
            self.name = name
            self.d = d
            self.p = p
    
    
    class Graph(object):
        def __init__(self, vertex, edge, weight):
            """
            :param vertex: list of vertices
            :param edge: dict of the edges
            :param weight: dict of the weight
            """
            self.vertex = [Vertex(v) for v in vertex]
            self.edge = edge
            self.weight = weight
    
        def get_vertex(self, name):
            for v in self.vertex:
                if name == v.name:
                    return v
    
        def weight(self, u, v):
            index = u + v
            return self.weight[index]
    
    
        def get_adj(self, node):
            return self.edge[node]
    

    Dijkstra代码:

     def dijkstra(graph, source, destination):
            """Performs Dijkstra's algorithm until it finds the shortest
            path from source to destination in the graph with nodes and edges.
            Assumes that all weights are non-negative.
        
            Args:
                graph: the graph to be implemented
                source: the source node in the graph.
                destination: the destination node in the graph.
             
            Returns:
                A tuple of the path as a list of nodes from source to destination 
                and the number of visited nodes.
            """
            Q = PriorityQueue()
            # 初始化
            for node in graph.vertex:
                node.parent = None
                node.queue_key = None
            # 起始节点添加进队列,表示已经访问
            source = graph.get_vertex(source)
            source.queue_key = NodeDistancePair(source, 0)
            Q.insert(source.queue_key)
            num_visited = 0
            while len(Q) > 0:
                # 获取队列中离当前节点距离最近的节点
                u = Q.extract_min()
                num_visited = num_visited + 1
                # 判断是否为目标节点
                if u.node is destination:
                    break
                # 遍历当前节点的邻居节点 
                for next_vertex in graph.get_adj(u.node):
                    # 算下一节点到当前节点的距离
                    next_dist = graph.weight(u.node, next_vertex) + u.distance
                    next = graph.get_vertex(next_vertex)
                    # 没有被访问,则添加进队列
                    if next.queue_key is None:
                        next.queue_key = NodeDistancePair(next.name, next_dist)
                        Q.insert(next.queue_key)
                        next.parent = u.node
                    # 有被访问过,则重新计算距离并更新
                    elif next_dist < next.queue_key.distance:
                        next.queue_key.distance = next_dist
                        Q.decrease_key(next.queue_key)
                        next.parent = u.node
                        
            #反向遍历找到源节点到目标节点的路径
            p = []
            des = graph.get_vertex(destination)
            while des is not None:
                p.append(des.name)
                des = graph.get_vertex(des.parent)
            p.reverse()
            return (p, num_visited)
    

    对代码进行简单的测试,其计算所得的路径和路径长度与人工计算的结果相同。

    graph1 = Graph(vertex=['A','B','C','D','E'],
                   edge={'A':['B','C'],
                         'B':['C','D'],
                         'C':['B','D','E'],
                         'D':['E'],
                         'E':['D']},
                   weight={'AB':10,'AC':3,'BD':2,'BC':1,'CB':4,
                           'CD':8,'CE':2,'DE':7,'ED':9})
    
    print(dijkstra(graph1, 'A','D'))
    print(graph1.get_vertex('D').queue_key.distance)
    
    # output: (['A', 'C', 'B', 'D'], 5)  9
    

    利用二叉堆实现的Dijkstra算法的时间复杂度为 O(V lg V + E lg V )。

    Bellman-Ford算法

    当图中有负权边时,有些情况下Dijkstra算法将不再适用,例如下述情况中出现了negative cycle,若采用Dijkstras算法,程序将无法停止,会进入死循环。因此需要一种算法可以检测neagtive cycles。

    在这里插入图片描述

    与Dijkstra算法相比,Bellman-Ford算法比较简单,在寻找最短路径时,它不像Dijkstra算法那样每次选择最近的节点,因此不需要优先队列的数据结构。

    class Vertex(object):
        def __init__(self, name, d=None, p=None):
            self.name = name
            self.d = d
            self.p = p
    
    
    class Graph(object):
        def __init__(self, vertex, edge, weight):
            """
    
            :param vertex: list of vertices
            :param edge: dict of the edges
            :param weight: dict of the weight
            """
            self.vertex_string = vertex
            self.vertex = [Vertex(v) for v in vertex]
            self.edge = edge
            self.weight = weight
    
        def get_vertex(self, name):
            for v in self.vertex:
                if name == v.name:
                    return v
    
        def V(self):
            return self.vertex_string
    
        def E(self):
            return self.edge
    
        def w(self, u, v):
            index = u + v
            return self.weight[index]
    
        def initialize_single_source(self, s):
            for v in self.vertex:
                v.d = float('Inf')
                v.p = None
                if s == v.name:
                    v.d = 0
    
        def check(self, u, v):
            ve = self.get_vertex(v)
            ue = self.get_vertex(u)
            if ve.d > ue.d + self.w(u, v):
                print("negative cycle appears")
    
    
        def relax(self, u, v):
            """
            :param u: curr vertex
            :param v: next vertex
            :return: None
            """
            ve = self.get_vertex(v)
            ue = self.get_vertex(u)
            if ve.d > ue.d + self.w(u, v):
                ve.d = ue.d + self.w(u, v)
                ve.p = ue
    
        def get_adj(self, node):
            return self.edge[node]
    
    

    上面的代码用来表示图,下面是Bellman-Ford算法。首先对图进行了初始化,源节点的距离设置为0,其余节点设置为无穷大。对图中的每条边进行遍历更新距离,迭代|V-1|次,V是图的节点数。最后一步检测是否存在negative cycles,即判断是否存在ve.d > ue.d + w(u, v)的情况,其中ue为当前节点,ve为下一节点。

    def bellman_ford(G, s):
        G.initialize_single_source(s)
        for i in range(len(G.V())):
            for vertex in G.E():
                for next_vertex in G.E()[vertex]:
                    G.relax(vertex, next_vertex)
        for vertex in G.E():
            for next_vertex in G.E()[vertex]:
                G.check(vertex, next_vertex)
    
                
    # 图中有negative cycle时:
    graph1 = Graph(vertex=['A','B','C','D','E'],
                   edge={'A':['B','C'],
                         'B':['C','D'],
                         'C':['B','D','E'],
                         'D':['E'],
                         'E':['D']},
                   weight={'AB':10,'AC':3,'BD':2,'BC':1,'CB':-4,
                           'CD':8,'CE':2,'DE':7,'ED':9})
    bellman_ford(graph1, 'A')
    print(graph1.get_vertex('B').d)
    
    # output:
    # negative cycle appears
    # negative cycle appears
    # -13
    
    # 更改权重:计算的距离与人工手算相同
    graph1 = Graph(vertex=['A','B','C','D','E'],
                   edge={'A':['B','C'],
                         'B':['C','D'],
                         'C':['B','D','E'],
                         'D':['E'],
                         'E':['D']},
                   weight={'AB':10,'AC':3,'BD':2,'BC':1,'CB':4,
                           'CD':8,'CE':2,'DE':7,'ED':9})
    # output: 7
    
    

    该算法的时间复杂度为O(VE),因为初始化需要O(V)时间,对E条边进行遍历更新距离,迭代|V-1|次需要O(VE)时间。

    展开全文
  • 最短路径算法dijkstra

    2020-02-14 10:32:43
    最短路径算法dijkstra 该算法的核心就是从当前节点的邻居节点中找到距离起始节点的最近那个节点,作为下步迭代的节点。 一 算法步骤 给定起始节点s(v0)s(v_0)s(v0​)、邻接矩阵AAA; 初始化nnn维零标记向量label...
  • 最短路径算法——Dijkstra算法

    千次阅读 2013-04-25 00:05:18
    而从学校出来到现在,最短路径算法都没有实际运用过,最近在一个GIS项目中总算用到了,于是乎把教材重温了下,同时查阅了网上一些资料,借鉴了一些别人的东西,并顺利用写进了项目中,文中的主要代码来自于园子里的...
  • 单源最短路径算法

    2018-12-06 20:04:28
    Dijkstra最短路径算法 算法的基本思想是每次找离源点最近的一个点,然后以该点为中心进行扩展,直到|v|-1个点的最短路径都找到。 1. 将所有的点都划分为两部分P和Q,P代表已知最短路径的顶点集合,Q代表未知最短...
  • 并行最短路径算法Dijkstra。 为实现并行最短路径计算,我们必须要解决如下问题: (1)数据获取:利用随机函数生成大约2000个节点及其节点之间的距离。本程序使用邻接矩阵来存储带权有向图的信息。矩阵大小2000*2000...
  • 最短路径算法总结

    2018-01-31 21:41:56
    算法步骤:找不在s集合内的所有点中离源点最近的点—>将找到的点加入s集合—>更新所有未加入s集合的点的最短路径(在刚加入的点的基础上更新) 小结:应用floy和dijkstra算法的程序的共同步骤——1.初始化map[][]...
  • Dijkstra算法是求单源最短路径 主要思想就是先初始化例如起点为1 先初始化点1到各点的距离,dis[] 然后用一个数组book[]标记那些点已经用了.该假设先标记点1, 然后从没被标记的点中选出一个到源点1最近的点 ...
  • 是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。 ...
  • 最近在找工作,把自己当时参加比赛用的最短路径算法复习一下,简单实现一下 图如下: 输入和输出数据: Input: 6 9 1 2 1 1 3 12 2 3 9 2 4 3 3 5 5 4 3 4 4 5 13 4 6 15 5 6 4 Output: 0 1 8 4 13 17 代码...
  • 迪克斯特拉(Dijkstras)算法,适用于加权图(weighted graph),找出的是总权重最小的路径。 Dijsktra算法包含四个步骤: 找出当前离起点最近的节点; 对于该节点的邻居,检查是否有前往它们的更短路径,如果有,就...
  • 最短路径算法-1

    2020-10-08 17:44:42
    最短路径算法-1 爬山法(Hill-Climbing) 在图的表示中,我介绍了如何自定义类来表示图,其中就有启发式距离的表示。爬山法是在DFS上基于启发式距离的一种算法。有点类似于贪婪算法,每一次选择离目标顶点最近的顶点...
  • 内容会持续更新,有错误的地方欢迎...以 S 为源节点,实现Bellman-Ford(中文名:贝尔曼福特算法) 单源最短路径算法。 Dijkstra算法 请见下方图解: 但Dijkstra算法不适用于有负权边的图,所以这里,用Be...
  • 最短路径算法的弗洛伊德算法的数学归纳法冥想证明-Version-1 精品文档 精品文档 收集于网络如有...我二十年前已了解迪杰斯特拉算法最近忽有兴趣开发了一款最短路径算法小软件EXE了却二十年前的心愿余庆未了网上了解了还
  • 最短路径算法实现

    2020-01-28 17:15:38
    关于最短路径的一个简单模板...1找出源点距离哪个点最近,然后记录最近距离,记录最近点下标,将这个点加入已访问数组。 2.接下来将这个点作为!中介点!,遍历所有顶点,判断是否 顶点未被访问且中介点能到该点且中...
  • 而从学校出来到现在,最短路径算法都没有实际运用过,最近在一个GIS项目中总算用到了,于是乎把教材重温了下,同时查阅了网上一些资料,借鉴了一些别人的东西,并顺利用写进了项目中,文中的主要代码来自于园子里的...
  • 算法解决的是有向图中单个源点到其他顶点的最短路径问题,其主要特点是每次迭代时选择的下一个顶点是标记点之外距离源点最近的顶点。但由于dijkstra算法主要计算从源点到其他所有点的最短路径,所以算法的效率较低。...
  • 无权最短路径算法:借助广度优先搜索,距开始最近的那些顶点首先被赋值,而最远的顶点最后被赋值 伪码如下 void Unweighted(Table T) { Queue Q; Vertext V, W; Q = CreateQueue(NumVertex); MakeEmpty...
  • 最近上课的内容非常多,有些内容有点跟不上了,只能课后自己看了一下。 边带有权值的图称为带权...是最简单的最短路径算法,可以计算图中任意两点的最短路径,适用于出现负边权的情况。 Dijkstra算法O(N2) 用来...
  • 我二十年前已了解迪杰斯特拉算法最近忽有兴趣开发了一款最短路径算法小软件EXE了却二十年前的心愿余庆未了网上了解了还有多种方法如A-Star,johnson,bellman,SPFA等算法其中最感兴趣的是弗洛伊德算法百度了看了很多...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,925
精华内容 770
关键字:

最近路径算法