Dijkstra 算法-利用优先队列Priority Queue实现的Lazy版本

Dijkstra 算法,优先队列法

如前所述,Dijkstra算法是图论中非常重要的算法之一。前一篇对严蔚敏《数据结构》进行梳理和代码实现,严版的算法中利用P[v][w] ,D[w] 和 final[v] 对源点和各个之间的最短距离的路径,长度和访问标记进行记录,最终求得SSSP(single source shortest path)路径。

本文主要介绍如何利用Priority Queue(PQ)队列,求得源点至各个顶点的最短距离。对于Priority Queue的实现,这里不做赘述,有兴趣的可以参考网上的相关文章介绍,本文主要用到min Priority Queue,队列的顶点永远保持最小值的元素。

实现算法之前,我们需要抽象几个数据结构体,首先需要定义Priority Queue的堆中储存的数据对象类型,我们这里采用记录顶点序列号和到达此顶点的最短距离两个记录。

a) Dijkstra_Node 结构体

/**
@param -index indicates the visiting vertex order
@param -dist indcates the current min distance from source to here
**/
typedef struct Dijkstra_Node
{
    int index;
    int dist;
} Dijkstra_Node;

b) dist[] 数组,dist将记录源点到此点的最短距离,过程中需要不断利用贪心思想,最总求得某个顶点的最小dist[w]。
c) prev[]数组,记录某个顶点的前置顶点,便于后续遍历时,能找到完整的最短路径,否则就只能得到最小值或最短距离,无法获取其具体路径。
d) visited[]数组,这个数组属于在邻接图中有定义,如果访问过,标记为1

利用William提供的示意图,对PQ为基础的Dijkstra进行说明。

  • 首先需要对dist数组进行初始,推定各个点距离源点S的距离为INT_MAX(机器中的最大整数),然后把pre[]赋值为-1,表明各个顶点还暂未前置顶点,最后把visited[]初始化为0,表明各节点还未开始访问。
  • 然后以源点为起起始点,遍历其可达的顶点,如果可达顶点还未访问,那么就需要加入到PQ列表中去
  • 在PQ中出栈最小的(index, dist)序列,重复以上步骤。
    在这里插入图片描述
    在这里插入图片描述
    过程中可能存在存在一类情况,就是到达某个顶点有多个PQ记录,这时候可以对重复的顶点记录(dist不同)进行skip处理,因为相对某个顶点的最小距离遍历后,再取相同顶点的较大距离,不会产生更好的结果。
    在这里插入图片描述
    上述遍历完成(1,3)之后,就没有必要再遍历(1,4),因为对于顶点1,后续出栈的距离,至少大于等于前面出栈的距离,也即4>3。所以对(1,4)直接出栈不需要进行后续任何操作。

由于Priority Queue已经采用C语言实现,自然而然本算法也沿用C语言进行后续的继续操作,包括结构体和函数的定义实现。

a) 头文件说明,本头文件中已经包含了PQ的实现,读者可以自己编写PQ,编写过程中,可以预定义Heap中的对象,在实际应用中可以重新定义(ShortestPath.h)。

/**
 * @file ShortestPath.h
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2023-02-09
 * 
 * @copyright Copyright (c) 2023
 * 
 */

#ifndef SHORTESTPATH_H
#define SHORTESTPATH_H
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<limits.h>
#include "../00_introduction/Status.h"
#include "../02_Adjacent_List_Graph/AdjacentListGraph.c"

typedef struct Dijkstra_Node
{
    int index;
    int dist;
} Dijkstra_Node;

typedef Dijkstra_Node priority_queue_element;

#include "../11_PriorityQueue/PriorityQueue.c"

/**
 * @brief Use priority queue to look for the single source shortest path
 * 
 * @param G Adjacent list graph
 * @param s Search start point
 * @param dist dis[] array to store each node minimum distance
 * @param prev prev[] array to store the previous index 
 */
void Dijkstra_Lazy(ALGraph G, int s, int *dist, int *prev);

/**
 * @brief Display the short path
 *
 * @param G Adjacent list graph
 * @param s Search start point
 * @param e Search end point
 * @param prev prev[] array to store the previous index
 */
void Display_Shortest_Path(ALGraph G, int s, int e,int *prev);

/**
 * @brief Display the minimum distance
 *
 * @param e end point
 * @param dist dist[] array to store each node minimum distance
 */
void Display_Distance(int e, int *dist);

/**
 * @brief Compare e1 and e2 with regard to its own dist component
 * 
 * @param e1 object 1
 * @param e2 object 2
 * @return int -Return true if e1<e2
 */
int less_than(priority_queue_element e1, priority_queue_element e2);

#endif

b) 函数实现(ShortestPath.c)

/**
 * @file ShortestPath.c
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2023-02-09
 * 
 * @copyright Copyright (c) 2023
 * 
 */
#ifndef SHORTESTPATH_C
#define SHORTESTPATH_C
#include "ShortestPath.h"

void Dijkstra_Lazy(ALGraph G, int s, int *dist, int *prev)
{
    int i;
    int index;
    int minvalue;
    Heap H;
    priority_queue_element node;
    ArcNode *p;
    int v;
    int w;

    init_min_heap(&H,G.arcnum*2);

    for(i=0;i<G.vexnum;i++)
    {
        visited[i]=0;
        *(dist+i)=INT_MAX;
        *(prev+i)=-1;
    }

    dist[s]=0;
    node.index=s;
    node.dist=0;
    min_heap_insert(H,node,less_than);

    while(H->size>0)
    {
        node=heap_extract_min(H,less_than);
        v=node.index;
        minvalue=node.dist;

        visited[v]=1;

        if(dist[v]<minvalue)
        {
            continue; // continue extract or dequeue procedure
        }

        for(p=G.vertices[v].firstarc;p;p=p->nextarc)
        {
            w=p->adjvex;

            if(!visited[w])
            {
                if((dist[v]+*(p->info))<dist[w])
                {
                    dist[w] = dist[v] + *(p->info);
                    prev[w]=v;
                    node.index=w;
                    node.dist = dist[v] + *(p->info);
                    min_heap_insert(H,node,less_than);
                }
            }
        }
    }    
}

void Display_Shortest_Path(ALGraph G, int s, int e, int *prev)
{
    if(s!=e)
    {
        Display_Shortest_Path(G,s,prev[e],prev);
    }

    printf("-%c-\n",G.vertices[e].data);
}

void Display_Distance(int e, int *dist)
{
    printf("The minimum distance is %d \n",*(dist+e));
}

int less_than(priority_queue_element e1, priority_queue_element e2)
{
    return (e1.dist<e2.dist);
}
#endif

c) 代码测试(ShortestPath_main.c)

/**
 * @file ShortestPath_main.c
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2023-02-09
 * 
 * @copyright Copyright (c) 2023
 * 
 */
#ifndef SHORTESTPATH_MAIN_C
#define SHORTESTPATH_MAIN_C
#include "ShortestPath.c"

int main(void)
{
    // void Display_Shortest_Path(ALGraph G, int s, int e, int *dist, int *prev)
    int dist[MAX_VERTEX_NUM];
    int prev[MAX_VERTEX_NUM];
    int s;
    int e;
    ALGraph G;
    FILE *fp;
    s=0;
    e=5;

    fp=fopen("DN.txt","r");
    CreateGraph(&G,fp);

    Dijkstra_Lazy(G,s,dist,prev);
    Display_Shortest_Path(G,s,e,prev);
    Display_Distance(e,dist);

    PressEnter;
    fclose(fp);
    return EXIT_SUCCESS;  
}

#endif

总结,为什么称之此版本为LAZY版本,因为对于后续访问的顶点,可能存在多个(index, dist),这个时候lazy版本全盘插入,而不是对原有的顶点进行更新。所以称之为 lazy. 后续将采用eager版本进行改进。

以上,

谢谢

参考:

Video Dijkstra’s shortest path algorithm by William Fiset
PQ实现,参考《算法》第二版

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值