数据结构学习笔记-Dijkstra算法(C语言邻接表实现)

本文介绍了Dijkstra算法在计算有权值图的单源最短路径问题中的应用,详细讲解了算法的核心思想和实现步骤。通过C语言实现邻接表,讨论了处理负权边的问题,并探讨了算法的时间复杂度,包括直接扫描和使用最小堆两种情况。

Dijkstra算法是计算有权值图的单源最短路径问题的经典算法

算法核心思想:定义一个顶点集合S{V0,V1,……Vn},初始条件下集合S中只包含起始源点。

对于所有顶点V,定义dist[V]来表示V到源点的最短距离。

每次循环在所有未被收录进集合S的顶点中,挑选出dist值最小的顶点Vmin,将其收录进集合S。

当一个顶点V被收录进集合S中时,检测V的所有邻接点的dist值是否产生变化。

注意:Dijkstra算法不能处理负权边。

变量定义和初始化:

double dist[Graph->nV];
int path[Graph->nV];
int dijkstra[Graph->nV];

dist数组用于记录任一顶点到源点的最短距离,path数组用来记录通过最短距离找到该顶点的路径(path[V]即V的前一个必经顶点),dijkstra数组用来标记顶点是否被收录进集合S;

for(int i=0;i<Graph->nV;i++){
    dist[i]=__DBL_MAX__,path[i]=-1,dijkstra[i]=0;
}
dijkstra[start]=1;
dist[start]=0;

将所有顶点的dist值初始为正无穷,所有顶点的路径(指示前一顶点)初始为-1,集合S初始为空。

对于源点V,首先将其收录到集合S中,并定义源点的dist值为0;

GNode *ptr=Graph->G[start];
while(ptr){
        dist[ptr->vertax]=ptr->weight;
        path[ptr->vertax]=start;
        ptr=ptr->next;
}

对于源点的所有邻接点,定义其dist值为源点到该点边的权重,并将path中的路径记录为源点。

循环算法:

while(1){
    ①寻找未被收录的顶点中dist值最小的顶点,将其收录到集合S。
    如果没找到,退出循环结束算法
    ......
    ②for(对于①中找到的顶点,访问其所有邻接点,检查邻接点的dist值是否变化){
        if(如果该点的dist值变小了,更新dist值,并将①中找到的顶点作为该点的前一最短路径点){
        ......  
        }
    }
    ③打印路径
    ......
    

①每次循环先寻找dist值最小且未被收录的点V,将其收录进S。当所有点都被收录完毕后,退出循环结束算法。

int temp=FindMin(Graph,dist,dijkstra);
if(temp==-1) break;
dijkstra[temp]=1;


/*FindMin函数用来寻找未被收录的dist值最小的点*/
int FindMinDist(LGraph *Graph,double dist[],int dijkstra[]){
    double min=__DBL_MAX__,mintag=-1;
    for(int i=0;i<Graph->nV;i++){
        if(dist[i]<min&&dijkstra[i]==0){
            min=dist[i];
            mintag=i;
        }
    }
    return mintag;
}

②访问点V的所有邻接点,对于temp的所有邻接点ptr->vertex,如果其dist值变小了,更新dist值为点V的dist值与V到该点边的权值之和,并记V为该点的上一个路径点

ptr=Graph->G[temp];//访问最小dist值的所有邻接点
while(ptr){
    if(dijkstra[ptr->vertax]==0)//对于每个temp的未被收录的邻接点ptr->vertex
        if(dist[temp]+ptr->weight<dist[ptr->vertax]){//如果ptr->vertex收录后其dist变化,更新dist值
            dist[ptr->vertax]=dist[temp]+ptr->weight;
            path[ptr->vertax]=temp;//记录路径 
        }
    ptr=ptr->next;
}

③倒序打印任意一点end到源点的路径,如果需要正序输出可以在记录路径时压栈,打印路径时出栈。

do{
    printf("%d<=",i);
    i=path[i];
}while(i!=-1);

完整代码:

void DijkstraShortestPath(LGraph *Graph,int start,int end){
    int path[Graph->nV],dijkstra[Graph->nV];
    double dist[Graph->nV];
    for(int i=0;i<Graph->nV;i++)
        dist[i]=__DBL_MAX__,path[i]=-1,dijkstra[i]=0;
    dist[start]=0;
    dijkstra[start]=1;
    GNode *ptr=Graph->G[start];
    while(ptr){
        dist[ptr->vertax]=ptr->weight;
        path[ptr->vertax]=start;
        ptr=ptr->next;
    }
    while(1){
        int temp=FindMinDist(Graph,dist,dijkstra);
        if(temp==-1) break;
        dijkstra[temp]=1;
        ptr=Graph->G[temp];
        while(ptr){
            if(dijkstra[ptr->vertax]==0)
                if(dist[temp]+ptr->weight<dist[ptr->vertax]){
                    dist[ptr->vertax]=dist[temp]+ptr->weight;
                    path[ptr->vertax]=temp;
                }
            ptr=ptr->next;
        }
    }
    int i=end;
    do{
        printf("%d<=",i);
        i=path[i];
    }while(i!=-1);
}
int FindMinDist(LGraph *Graph,double dist[],int dijkstra[]){
    double min=__DBL_MAX__,mintag=-1;
    for(int i=0;i<Graph->nV;i++){
        if(dist[i]<min&&dijkstra[i]==0){
            min=dist[i];
            mintag=i;
        }
    }
    return mintag;
}

Dijkstra的时间复杂度主要取决于如何寻找未被收录集合S中的最小dist值的点,如果采用上述直接扫描所有邻接点的方法,其时间复杂度为O(V²)。当采用最小堆时,将所有未被收录集合S的点压入最小堆,每次寻找时出堆。其时间复杂度为O(VlogV)。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值