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算法高明,至少在解决这类没有负边负圈问题上。此外有一点得说明,本人在实现查询哪些点探索哪些点没有探索时采用的是链表,在链表上进行查询。此外也可以用优先队列,斐波那契堆等来实现,这样在查询阶段可以节省一点时间复杂度。
相关文章推荐
- 数据结构与算法问题 单源最短路径 浙大OJ
- 图算法 单源最短路径问题 无权最短路径
- 【算法】Bellman-Ford算法(单源最短路径问题)(判断负圈)
- 贪心算法解决单源最短路径问题
- 【算法】Dijkstra算法(单源最短路径问题) 邻接矩阵和邻接表实现
- 算法java实现--分支限界法--单源最短路径问题
- AOJ GRL_1_A: Single Source Shortest Path (Dijktra算法求单源最短路径,邻接表)
- 0024算法笔记——【贪心算法】单源最短路径问题
- 算法笔记——【贪心算法】单源最短路径问题
- Bellman-Ford算法之单源最短路径问题
- 单源最短路径问题(dijkstra算法 及其 优化算法(优先队列实现))
- Dijkstra算法,Bellman-Ford算法和BFS算法解决有向图的单源最短路径问题
- Bellman-Ford 算法 单源最短路径问题
- 0024算法笔记——【贪心算法】单源最短路径问题
- 算法java实现--贪心算法--单源最短路径问题--Dijkstra算法
- Dijkstra算法是解单源最短路径问题的一个贪心算法
- 单源最短路径问题
- 单源最短路径问题[Dijkstra实现]
- 最短路径算法之AStar算法(三) 《A* Pathfinding for Beginners》一文中的两个问题
- 单源最短路径问题[Dijkstra实现]