Dijktra算法之单源最短路径问题

Dijktra算法之单源最短路径问题

问题:求下图中点 S 到其他所有点的最短距离。
这里写图片描述
分析:根据之前的Bellman-Ford算法我们知道最优子结构如下:

opt(v,k)=min opt(v,k1)min(u,v|E)opt(u,k1)+d(u,v),
where: E 是所有点的集合。
我们现在回到问题,我们发现所有点边都是大于零的,现在我们考察一个特殊的节点V*,它是从S出发 最多经过第 k1 步到达的最近的一个节点。当然器最优子结构也存在如下形式:

opt(v,k)=min opt(v,k1)min(u,v|E)opt(u,k1)+d(u,v),

这个时候我们发现,V*是从 S 出发 最多经过第k1步到达的最近的一个节点。所以 opt(v,k1)<opt(u,k1) . 此外第二项还加上了 d(u,v) ,它是大于0 的数,所以以上最优子结构可以写成:
opt(v,k)=opt(v,k1)

这个性质可以用下面这个图说明:
这里写图片描述
从列开始看:只要在一列中间找到最小值之后,红圈中的值,在看最小值所在的行右边的值都不会变化了。如果按照Bellman-Ford算法则需要把所有的数都算出来。
具体实现过程如下:

//main.c文件
#include<stdio.h>
#include<stdlib.h>
#include"list.h" 
#define non 65535
struct edge{
    int start;//起点 
    int end;//端点 
    int w;//边的权值 
}Edge[9]={{'s','u',1},{'s','v',2},{'s','x',4},
          {'u','x',1},{'u','y',3},
          {'v','z',3},{'v','x',2},
          {'x','y',1},{'x','z',2}};

int nodenum=6;
int EdgeNum=9;
void PrintAllNodeInfor(){
    int i;
    printf("起点  端点  距离\n");
    for(i=0;i<EdgeNum;i++){
        printf("%c--->%c:      %d\n",Edge[i].start,Edge[i].end,Edge[i].w);
    }
}
int Distance(char a,char b){
    int k;
    for(k=0;k<EdgeNum;k++){
        if( Edge[k].start ==a&&Edge[k].end==b)
            return Edge[k].w;
    }
    return non;
}

/***************************************************
查询字符 v是否在集合S中,存在返回0,不存在返回1
size:集合S的大小 
***************************************************/ 
int SingleCharMatchSet(char v,char S[],int size){
    int i;
    if(size==0){
        return 1;
    }
    else{
        for(i=0;i<size;i++){
           if(v==S[i]){
              return 0;
           }
        }
        return 1;
    }
}
void Diskstra(List L,int nodenum,char T[]){
    int *d=(int*)malloc(sizeof(int)*(nodenum));
    char *S=(char *)malloc(sizeof(char)*nodenum);
    int S_length,d_length;
    int dv;
    char tempchar;
    int flag=0;
    int i,j;
    Position P_min;
    //PrintList(L);
    //初始化 
    S_length=0;
    d_length=0;
    S[S_length++]=T[0]; 
    d[d_length++]=0 ;
    Delete('s' ,L);//
    while(S_length<nodenum){//当集合S不大于节点个数时,即存在点未探索 
    //  printf("集合s!=V空间");
        for(i=0;i<nodenum;i++){//对于所有不在集合S中的元素 
            dv=non;
            if(SingleCharMatchSet(T[i],S,S_length)==1 ){//T[i]不属于S 

                for(j=0;j<S_length;j++){//遍历已经集合S中的元素,使dv最小 
                    if( dv> d[j]+Distance(S[j],T[i]) &&Distance(S[j],T[i])!=non){
                        dv=d[j]+Distance(S[j],T[i]);
                        tempchar=T[i];//S[j]就是  V* 
                            flag=1;                 
                    }
                }
                if(flag==1){//找到新的最小值 
                    Exchange(tempchar,dv,L);
                    flag=0; 
                }               
            }   
        }   

        S[S_length++]=Find_MinElement_Location(L)->Element;//选择目标节点加入到探索的集合 
        d[d_length++]=Find_MinElement_Location(L)->d ;
        Delete(Find_MinElement_Location(L)->Element ,L);//从链表中删除改元素 
    //  PrintList(L);           
    }
    printf("Dijkstra算法:最短距离\n"); 
    for(i=0;i<6;i++){
        printf("s-->%c: %d\n",S[i],d[i]);
    }   
}

void main(){
    struct Node  node0,nodeS,nodeU,nodeV,nodeY,nodeX,nodeZ,*header,*p;
    char T[6]={'s','u','v','y','x','z'};//所有点标记 
    List L; 
    //初始化链表 
    node0.Element ='F';  node0.d =non;
    nodeS.Element ='s';  nodeS.d =0;
    nodeU.Element ='u';  nodeU.d =non;
    nodeV.Element ='v';  nodeV.d =non;
    nodeY.Element ='y';  nodeY.d =non;
    nodeX.Element ='x';  nodeX.d =non;
    nodeZ.Element ='z';  nodeZ.d =non;
    header=&node0;
    node0.Next =&nodeS;
    nodeS.Next =&nodeU;
    nodeU.Next =&nodeV;
    nodeV.Next =&nodeY;
    nodeY.Next =&nodeX;
    nodeX.Next =&nodeZ;
    nodeZ.Next=NULL;
    L=header;
    PrintAllNodeInfor();    
    Diskstra(L,nodenum,T);
}

//list.c文件
#include"list.h"
#include<stdio.h>
#include<stdlib.h>
/* Return true if L is empty*/
int IsEmpty(List L)
{
    return  L->Next==NULL; 
} 

 /*return true if P is the last position in list L */
 /*Parameter L is unused in this implementation */
int IsLast(Position P,List L)
{
    return P->Next==NULL;
}

 /*Return Position of X in L; NULL if not found*/
Position Find(ElementType X,List L)
{
    Position P;
 // P=L->Next;
    P=L;
    while(P!=NULL && P->Element!=X)
      P=P->Next;
    return P;
}
int Find_exist(ElementType X,List L){
    Position P;
 // P=L->Next;
    P=L;
    while(P!=NULL){
          if(P->Element==X)
             return 1;
         P=P->Next;
    }

    return 0;
}

Position Find_MinElement_Location(List L)
{
    Position P,temp_P;
    int min;
 // P=L->Next;
    P=L;
    temp_P=L;
    min=L->d;
    while(P!=NULL){
        if(min> P->d){
            min=P->d;
            temp_P=P;
        }
        P=P->Next;          
    }
    return temp_P;
 }
int Get_List_min_And_Delete_minvalue(List L)
{
    Position min_Pos;
    int min;
    min_Pos=Find_MinElement_Location(L);
    min=min_Pos->d ;
   // Delete(min_Pos->Element ,L);
    return min;
}

 /*Delete first occurrence of X from a list*/
 /*Assume use of a header node*/
void Delete(ElementType X,List L)
{
      Position P,TmpCell,P1,Newhead;
      P=FindPrevious(X,L);
      if(!IsLast(P,L) ) /*Assumption of header use*/
      {                  /*X is found; delete if*/
          TmpCell=P->Next;
           P->Next=TmpCell->Next; /*Bypass deleted cell*/
           free(TmpCell);  
       }
 }

 /*If X is not found,then next field of returned*/
 /*Psition is nULL*/
 /*Assumes a header*/
Position  FindPrevious(ElementType X ,List L)
{
    Position P;
    P=L;
    while(P->Next!=NULL && P->Next->Element!=X)
       P=P->Next;

       return P;
}

 /*Insert (after legal position P)*/
 /*Header implementation assumed*/
 /*Parameter L is unused in this implementation*/
void Insert(ElementType X,int d,List L,Position P)
{
    Position TmpCell,Pre_p;
    TmpCell=malloc(sizeof(struct Node));
    if( TmpCell==NULL )
    {
     // FatalError("out of space!!!");
    }
    TmpCell->Element=X;
    TmpCell->d =d;
    TmpCell->Next=P->Next;
    P->Next=TmpCell;
 }
 /*替换链表中某一元素*/ 
void Exchange(ElementType X,int d,List L)
{
    Position TmpCell;
    TmpCell= Find(X,L);
    TmpCell->d=d;

}

/*Correct DeleteList algorithm*/
void DeleteList(List L)
{
    Position P,Tmp;
    printf("Delete Linkd List:\n");
    P=L->Next;/*Header assumed*/
    L->Next=NULL;
    while(P!=NULL)
    {
        Tmp=P->Next;
        free(P);
        P=Tmp;
     }
}
 /********************return 链表的首地址**************************/ 
 Position Header(List L)
 {
    return L;
 }
 /*************************打印整个链表**************************/ 
 void PrintList(List L)
 {
    printf("打印整个链表:\n");
    while(L->Next!=NULL)
    {
        printf("%c  %d\n", L->Element,L->d);
        L=L->Next;
    }
    printf("%c  %d\n", L->Element,L->d);
 }
//list.h文件
#ifndef _List_H
typedef char ElementType;
typedef struct Node * PtrToNode; //新类型名  PtrToNode 为指向Node结构体 
typedef PtrToNode List;
typedef PtrToNode Position;
struct Node
{
    ElementType Element;
    int d;
    Position Next;
};
List MakeEmpty(List L);
int IsEmpty(List L);//
int IsLast(Position P,List L);//
Position Find(ElementType X,List L);//
void Delete(ElementType X,List L);//
Position FindPrevious(ElementType X,List L);//
void Insert(ElementType X,int d,List L,Position P);//
void DeleteList(List L);  //
Position Header(List L);//
Position First(List L);
Position Advance(Position P);
ElementType Retrieve(Position P) ;
void PrintList(List L);
Position Find_MinElement_Location(List L);
int get_List_min_And_Delete_minvalue(List L);
int Find_exist(ElementType X,List L);
#endif

运行结果如下图:
这里写图片描述

总结:我们可以看到Dijstra算法确实比Bellman-Ford算法高明,至少在解决这类没有负边负圈问题上。此外有一点得说明,本人在实现查询哪些点探索哪些点没有探索时采用的是链表,在链表上进行查询。此外也可以用优先队列,斐波那契堆等来实现,这样在查询阶段可以节省一点时间复杂度。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值