【算法导论】第24章迪杰斯特拉算法
1、问题引入
在带权有向图中求解某个源点到其余各个顶点的最短路径一般可以采用迪杰斯特拉算法(Dijkstra算法)。
2、算法的主体思想:
引用:(http://hi.baidu.com/wangziming/blog/item/d352be19d53e554642a9ad62.html)
A、找到v——Other所有路径中的的最短路径vd=v——d(Other的一个元素);
B、找到v——S——Other所有路径中的的最短路径vi=v——i(Other的一个元素);
C、比较vd和vi如果vd<=vi则将d加入S且从Other中删除,否则将i加入S且从Other中删除。
重复以上步骤直至Other为空集。
3、一个问题:
这里说明一个问题:假设S为已经求的的最短路径的终点的集合,则可以证明:下一条最短路径(设其终点为x)或者是弧(v,x),或者是中间只经过S中的顶点而最后到达顶点x的路径;可以用反证法证明:(引用)我们求得的最短路径是升序排列的,假设x是存在于v——Other(v1)——Other(v2)之中,那么肯定有一条比这条路径更短的路径v——Other(v1)我们没找到,也就是说,我们所找的v2是从已经求的的最短路径开始搜索的,,其前一个节点必然存在于S中,如果不存在于S中,这就算法本身的思路相背离,必然为不正确的。。。我刚开始看这个算法的时候也是纠结在这里,,一直看不明白,,觉得有些不理解,,以为v-other-other中说不定就存在比v-S-other或者v-other更短的路径,,其实这是不可能的,,如果有最短的路径,必然是已经求解出来的。。。。。
4、具体实现过程:
这个算法在许多书上都有介绍,严蔚敏老师的数据结构上就有,,,这里算法实现中和严老师的有一点点的区别,即:使用prev[i]来记录从起始点到点i的最短路径中i点的前一个节点,,这样在最后得出路径的时候直接从i向前直到起始点就可以方便得出了。。。
程序实现中用了一个txt文件,其中第一个值用于记录点的个数,其他数据表示各个点之间的可达关系:比如
用txt文件表示为:
其中6表示有6个节点,,9999表示无穷大(本图中最大的路径长度不超过100),其他小于9999的表示实际路径长度。。。。
(1)实现方法1:未采用最小优先级队列
1 /*---------------------------------------------------------- 2 *name:迪杰斯特拉算法实现 3 *data:2012-5 4 *author:lp 5 *---------------------------------------------------------*/ 6 #include<stdio.h> 7 #include<stdlib.h> 8 #define max 9999 9 void Dijkstra(int ,int,int *,int *,int **); 10 void Search(int,int,int*); 11 void main() 12 { 13 int i,j,num,v,last;//num表示点的个数,v表示起始点; 14 FILE *p; 15 //2.txt中第一个数据表示点的个数,第二个数据表示起始点; 16 //其他数据为图中点之间的可达关系,-1表示不可达 17 p=fopen("2.txt","r"); 18 if(!p) 19 { 20 printf("cannot open file 2.txt"); 21 exit(0); 22 } 23 fscanf(p,"%d",&num);//获得点的个数 24 printf("请输入起始点:"); 25 scanf("%d",&v); 26 int *d=(int*)malloc(sizeof(int)*num); 27 //prev[]用于记录最短路径中每个点的前一个点 28 int *prev=(int *)malloc(sizeof(int)*num); 29 //a[][]用于记录各个点之间的路径,-1表示不可直达,具体数据参见2.txt; 30 int **a=(int**)malloc(sizeof(int*)*num); 31 for(i=0;i<num;i++) 32 a[i]=(int*)malloc(sizeof(int)*num); 33 for(i=0;i<num;i++) 34 for(j=0;j<num;j++) 35 fscanf(p,"%d",&a[i][j]); 36 37 Dijkstra(v,num,prev,d,a); 38 printf("请输入需要达到的终点(不大于%d): ",num-1); 39 scanf("%d",&last); 40 if(d[last]==max){} 41 else 42 printf("起始点%d到达终点%d的路径长度为:%d\n",v,last,d[last]); 43 Search(v,last,prev); 44 45 } 46 void Dijkstra(int v,int num,int *prev,int *d,int **a) 47 { 48 //d[]用于记录起始点到达终点的最短路径 49 //s[]用于记录节点是否已经加入到S(S:已经求的的最短路径的终点的集合) 50 //prev[i]用于记录从起始节点开始到达i节点的最短路径中i节点的前一个节点 51 int i,j; 52 int *s=(int*)malloc(sizeof(int)*num);//s[i]=1表示i点已经为S中的点; 53 for(i=0;i<num;i++)//初始化的d[],s[],prev[]; 54 { 55 s[i]=0; 56 d[i]=a[v][i]; 57 if(d[i]<max) 58 prev[i]=v; 59 else prev[i]=-1; 60 } 61 d[v]=0; 62 s[v]=1; 63 for(i=1;i<num;i++) 64 { 65 int tmp=max; 66 int k=v; 67 for(j=0;j<num;j++)//得出一个V-S中的节点放入到S中。 68 if((!s[j])&& d[j]<tmp)//找到d[]最小的点。。。 69 { 70 k=j; 71 tmp=d[j]; 72 } 73 s[k]=1;//k节点放入S中 74 for(j=0;j<num;j++)//更新d[]和prev[] 75 { 76 if((!s[j])&& a[k][j]<max) 77 { 78 int newdist; 79 newdist=d[k]+a[k][j]; 80 if(newdist<d[j]) 81 { 82 d[j]=newdist; 83 prev[j]=k; 84 } 85 } 86 } 87 } 88 } 89 //Search函数主要利用prev[]得出节点v到节点last的最短路径上的点, 90 //prev[]的值在Dijkstra函数中已经得到,,,, 91 void Search(int v,int last,int*prev) 92 { 93 if(prev[last]==-1) 94 { 95 printf("起始点%d不可达终点%d \n",v,last); 96 exit(0); 97 } 98 else 99 { 100 printf("起始点点%d到达终点%d的路径为: ",v,last); 101 printf("%d <-- ",last); 102 int k=prev[last]; 103 while(k>v) 104 { 105 printf("%d <-- ",k); 106 k=prev[k]; 107 } 108 printf("%d\n",v); 109 } 110 }
(2)实现方法2:采用最小优先级队列
(1)数据结构,严蔚敏。
(2):http://2728green-rock.blog.163.com/blog/static/43636790200901211848284/
(3):http://hi.baidu.com/wangziming/blog/item/d352be19d53e554642a9ad62.html
(4):http://blog.csdn.net/zrjdds/article/details/6728332