您的位置:首页 > 其它

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

2016-10-18 17:20 453 查看

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

问题:求下图中点S到其他所有点的最短距离。



分析:根据之前的Bellman-Ford算法我们知道最优子结构如下:

opt(v,k)=min⎧⎩⎨ opt(v,k−1)min(u,v|E)opt(u,k−1)+d(u,v),where: E是所有点的集合。

我们现在回到问题,我们发现所有点边都是大于零的,现在我们考察一个特殊的节点V*,它是从S出发 最多经过第k−1步到达的最近的一个节点。当然器最优子结构也存在如下形式:

opt(v∗,k)=min⎧⎩⎨ opt(v∗,k−1)min(u,v∗|E)opt(u,k−1)+d(u,v∗),

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

opt(v∗,k)=opt(v∗,k−1)

这个性质可以用下面这个图说明:



从列开始看:只要在一列中间找到最小值之后,红圈中的值,在看最小值所在的行右边的值都不会变化了。如果按照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算法高明,至少在解决这类没有负边负圈问题上。此外有一点得说明,本人在实现查询哪些点探索哪些点没有探索时采用的是链表,在链表上进行查询。此外也可以用优先队列,斐波那契堆等来实现,这样在查询阶段可以节省一点时间复杂度。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: