本篇文章介绍了,稀疏图上的约翰逊算法的详解。需要的朋友参考下
算法步骤简述:
1.计算图G加入新结点后的图g ,加入的新结点0到所有原结点之间距离为0,同时形成新的边集e’;
2.使用贝尔曼-福特公司算法处理g ,并形成0结点到各结点的最小距离d。
3.如果贝尔曼-福特公司算法检测出有负权回路则提示错误的并退出,否则继续。
4.对所有 g 中的顶点v,根据0结点到v的最小距离,将h(v)设置为这个值。
5.对所有的边w(u,v),权值更新为w(u,v) h(u)-h(v)
6.对图G中所有结点运行迪克斯特拉算法计算与其他顶点最短距离d[u][v]
(此处假定G和w集合是分开存储的。直接使用 g 也可以,因为0结点对其他结点是不可达的,但这显然浪费了计算时间。如果权值信息存在 g 中,可以对 g 进行操作,只不过跳过了0结点的处理)
7.原图G中最短距离d[u][v]=d[u][v] h(v)-h(u)
代码中有的地方没有优化,比如辅助结构vassist其实在贝尔曼-福特公司算法和迪克斯特拉算法两个函数中用法稍微有所不同,而且成员变量在前者中只用了2个;同时松弛算法放松也有类似的情况。前者是简单的复用,后者直接用名字区分。
代码包含三部分:贝尔曼-福特算法、迪杰斯特拉算法、用二项堆实现的优先级数组(迪杰斯特拉算法要用到)。
以下是算法的C语言版本,测试实例同 《算法导论》 图25-1
复制代码代码如下:#包含stdio。h #包含stdlib。h# define U 65535 # define PARENT(I)((I-1)/2)# define LEFT(I)(2 *(I)1)# define RIGHT(I)(2 *(I)2)# define N 5
结构顶点{ int key struct vtable * adj };
结构虚拟表{ int key//这个键是在顶点数组的序号//struct vertext * v;int w;struct vtable * next };
struct vassist { int d;int p;(同Internationalorganizations)国际组织键;};
int insert(struct vertex *,int,int,int,int);int walk(struct vertex *,int,int);struct va sist * initialize _ ss(int,int);int relaxd(int *,int,int,int);int relax b(struct va sist *,int,int,int);int build _ min _ heap(struct va sist *,int);int min _ heap ify(struct va sist *,int,int);int heap _ extract _ min(struct va sist *,int);堆中的int(struct va sist *,int,int);int heap _ decrease(struct va sist *,int,int);int dijkstra(struct vertex *,int,int,int * *);int bellman_ford(struct vertex *,int*,int,int);
int insert(struct vertex *p,int len,int i,int j,int w) { struct vtable *q,* prevq=p[i].adjprintf(key:%dn ,p[i].关键);prev=NULL而(q!=NULL) { if (q-key==j) { printf(错误:v %d到%d已经存在. n ,I,j);返回0;} else { prev=q;q=q-next;} } q=(struct vtable *)malloc((struct vtable)的大小);q-key=j;q-w=w;q-next=NULL;if(prev!=NULL)prev-next=q;else p[i].adj=q;返回1;}
int walk(struct vertex *p,int len,int i) { struct vtable *q=p[i].形容词而(q!=NULL) { printf( %d,w是%dn ,q-key,q-w);q=q-next;} printf( n );}
struct va sist * initialize _ ss(int size,int s){ int I;struct va sist * vava=(struct va sist *)malloc(size * sizeof(struct va sist));for(I=0;isizei ) { va[i].key=I;//建堆后我!=密钥va[i].d=U;va[i].p=-1;} va[s].d=0;返回va;}
//relax for dijkstraint relax d(int * p,int u,int v,int w) {//w=w(u,v)if(p[v]p[u]w){ p[v]=p[u]w;//为了简单处理,p使用的是数组//没有父母标记//如果想用父母标记,请将p改为一个自定义的结构体}返回1;}
//relax for belt man _ fordint relax b(struct va sist * va,int u,int v,int w) {//w=w(u,v) if(va[v].dva[u].va[v].d=va[u].d w;va[v].p=u;}返回1;}
国际贝尔曼_福特(结构顶点*图形,int *h,int大小,int s) {//算法要求不含源点可达的负权回路int i,j;struct vtable * p;struct va sist * vava=initialize _ ss(size,s);for(I=1;isi zei)for(j=0;jsize-1;j ) { p=graph[j].形容词而(p!=NULL) { relaxb(va,j,p-key,p-w);p=p-next;} }
printf(from %d,n ,s);for(j=0;jsizej ) printf(to %d: %dn ,j,va[j].d);
for(j=0;jsizej ) {//对0结点不必要p=图形[j]的缩写.形容词而(p!=NULL) { if(va[p-key].dva[j].d p-w)返回0;p=p-next;} } for(j=1;j=尺寸;h[j]=va[j].d;免费(va);h[0]=0;返回1;}
int build _ min _ heap(struct va sist * va,int size) {//建堆int I;for(I=大小/2-1;I=0;i - ) min_heapify(va,I,size);
返回1;}
int min _ heap ify(struct va sist * va,int i,int heap_size) { int l,r,min;结构温度;int tmin=U;l=左;r=右;if ((l heap_size) (va[l]).dva[i].d)){ min=l;tmin=va[l].d;} else { min=I;tmin=va[i].d;} if ((r heap_size) (va[r]).dva[最小值]。d)){ min=r;tmin=va[r].d;}如果(!(min==i)) { temp.d=va[min].d;temp.p=va[min].p;temp.key=va[min].关键;
va[最小值]。d=va[i].d;va[最小值]。p=va[i].p;va[最小值]。key=va[i].关键;
va[i].d=温度;va[i].p=temp.pva[i].key=temp.key
min_heapify(va,min,heap _ size);}返回1;}
int heap _ extract _ min(struct va sist * va,int heap _ size){ int min;if ( heap_size1)返回-1;min=va[0].关键;va[0].p=va[堆大小-1]。p;va[0].d=va[堆大小-1]。d;va[0].key=va[heap_size -1].关键;堆大小=堆大小-1;min_heapify(va,0,heap _ size);返回最小值;}
int in heap(struct va sist * va,int heap_size,int j){ int I;for(I=0;iheap _ sizei ) if(va[i].key==j)返回我;return-1;}
int heap _ decrease(struct va sist * va,int i,int key _ new){ struct va sist temp;if(key_newva[i].d)返回0;va[i].d=key _ new while((i0)(va[PARENT(I)].d va[i].d)) { temp.d=va[i].d;temp.p=va[i].p;temp.key=va[i].关键;va[i].d=va[父代(一)].d;va[i].p=va[父代(一)].p;va[i].key=va[PARENT(i)].关键;父代d=。温度;父代p=临时温度父代. key=temp.keyi=父代;}返回1;}
int dijkstra(struct vertex *graph,int len,int s,int **delta) { int i,j,heap _ size struct vtable * q;struct v assist * vaint * p;p=(int *)malloc(len * sizeof(int));for(I=0;ilenI)p[I]=U;p[s]=0;堆大小=长度
va=initialize_ss(len,s);build_min_heap(va,heap _ size);//va被拿去建堆,后续输出距离时不能再用了
while(heap _ size 0){ I=heap _ extract _ min(va,heap _ size);printf(node:%dn ,I);heap _ size-;for(j=0;jheap _ sizej ) printf(key:%d,d:%d,in数组:%dn ,va[j].弗吉尼亚州基[j]的缩写.d,p[va]j .key]);q=图形[我].形容词而(q!=NULL) { j=inheap(va,heap_size,q-key);if(j=0) if(va[j].dp[i] q-w) heap_decrease(va,j,p[I]q-w);relaxd(p,I,q-key,q-w);//其实可以合并heap_decreas和放松,不过为了接口简单没有这样做printf(将%d放松到%d,w是%dn ,I,q-key,q-w);q=q-next;} for(j=0;jheap _ sizej ) printf(key:%d,d:%d,in数组:%dn ,va[j].弗吉尼亚州基[j]的缩写.d,p[va]j .key]);} for(I=0;ileni ) printf(从%d到%d,距离是%dn ,s,I,p[I]);
免费(va);
for(I=0;ilenI){ delta[s][I]=p[I];}免费(p);
}
int **johnson(struct vertex *g,int n) { int i,j;int *h,**delta,* * d;结构顶点* gnstructvtable * p;gn=(结构顶点*)malloc(n*sizeof(结构顶点));h=(int *)malloc(n * sizeof(int));delta=(int * *)malloc(n * sizeof(int *));d=(int * *)malloc(n * sizeof(int *));for(I=0;在;I){ delta[I]=(int *)malloc(n * sizeof(int));d[I]=(int *)malloc(n * sizeof(int));} for(I=0;在;I)gn[I]=g[I];
for(I=1;在;我)插入(gn,n,0,I,0);如果(!bellman_ford(gn,h,n,0)) { printf(输入图包含负权循环. n );返回NULL}
for(I=0;在;i ) { p=gn[i].形容词而(p!=NULL){ p-w=p-w h[I]-h[p-key];p=p-next;} } for(I=0;在;我)行走(gn,n,I);
printf( before Dijkstra n );for(I=1;在;i ) { dijkstra(gn,n,I,delta);for(j=1;jn;j)d[I][j]=delta[I][j]h[j]-h[I];
} for(I=1;在;I){ for(j=1;jn;j ) printf(%dt ,d[I][j]);printf( n );} return d;}
int main(){ int i,j;int * * d;结构顶点N . 1 );//为0结点的加入预留位置for(I=0;iN 1;[医]例.adj=NULL例】. key=I;}
插入(vt,N 1,1,2,3);插入(vt,N 1,1,3,8);插入(vt,N 1,1,5,-4);插入(vt,N 1,2,4,1);插入(vt,N 1,2,5,7);插入(vt,N 1,3,2,4);插入(vt,N 1,4,3,-5);插入(vt,N 1,4,1,2);插入(vt,N 1,5,4,6);d=约翰逊(vt,N ^ 1);
返回1;}