• 稀疏图上的Johnson算法


      距离上一篇中间时间比较长,按照《算法导论》写了一些C语言实现,不过并没有一一贴上来的打算。这个算法融合了Bellman-Ford算法和Dijkstra算法,并且Dijkstra算法本身还使用了优先级数组(可用二项堆或斐波那契堆实现,这里用的是二项堆实现),性能比较好,达到了O(V2lgV+VE)的时间复杂度,在无负权回路图中是最快的,比较有代表性,因此把我参考自《算法导论》写成的C代码放在这里留档。

      这个算法不是对二者的简单融合,其中的重赋权值有理论依据,此处略去,可以参考原书。

    算法步骤简述:

    1.计算图G加入新结点后的图G’,加入的新结点0到所有原结点之间距离为0,同时形成新的边集E';

    2.使用Bellman-Ford算法处理G',并形成0结点到各结点的最小距离d。

    3.如果Bellman-Ford算法检测出有负权回路则提示FALSE并退出,否则继续。

    4.对所有G'中的顶点v,根据0结点到v的最小距离,将h(v)设置为这个值。

    5.对所有的边w(u,v),权值更新为w(u,v)+h(u)-h(v)

    6.对图G中所有结点运行Dijkstra算法计算与其他顶点最短距离d'[u][v]

    (此处假定G和w集合是分开存储的。直接使用G'也可以,因为0结点对其他结点是不可达的,但这显然浪费了计算时间。如果权值信息存在G'中,可以对G'进行操作,只不过跳过了0结点的处理)

    7.原图G中最短距离d[u][v] = d'[u][v] +h(v)-h(u)

      代码中有的地方没有优化,比如辅助结构vassist其实在Bellman-Ford算法和Dijkstra算法两个函数中用法稍微有所不同,而且成员变量在前者中只用了2个;同时松弛算法relax也有类似的情况。前者是简单的复用,后者直接用名字区分。

      代码包含三部分:Bellman-Ford算法、Dijkstra算法、用二项堆实现的优先级数组(Dijkstra算法要用到)。以下是算法的C语言版本,测试实例同《算法导论》图25-1。

    #include <stdio.h>
    #include <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
    
    struct vertex {
        int key;
        struct vtable *adj;
    };
    
    struct vtable {
        int key;//这个key是在vertex数组的序号
        //struct vertext *v;
        int w;
        struct vtable *next;
    };
    
    struct vassist {
        int d;
        int p;
        int key;
    };
    
    
    
    
    int insert(struct vertex *,int,int,int,int);
    int walk(struct vertex *,int,int);
    struct vassist *initialize_ss(int,int);
    int relaxd(int *,int ,int ,int);
    int relaxb(struct vassist *,int ,int ,int);
    int build_min_heap(struct vassist *,int);
    int min_heapify(struct vassist *, int ,int);
    int heap_extract_min(struct vassist *,int);
    int inheap(struct vassist *,int ,int );
    int heap_decrease(struct vassist *,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,*prev;
        q = p[i].adj;
        printf("key:%d\n",p[i].key);
        prev = NULL;
        while(q!=NULL) {
            if (q->key == j) {
                printf("error: v %d to %d already exist.\n",i,j);
                return 0;
            }
            else {
                prev = q;
                q=q->next;
            }
        }
        q = (struct vtable*)malloc(sizeof(struct vtable));
        q->key = j;
        q->w = w;
        q->next = NULL;
        if(prev!=NULL)
            prev->next = q;
        else 
            p[i].adj = q;
        return 1;
    }
    
    int walk(struct vertex *p,int len,int i) {
        struct vtable *q = p[i].adj;    
        while(q!=NULL) {
            printf(" %d,w is %d\n",q->key,q->w);
            q=q->next;
        }
        printf("\n");
    }
    
    struct vassist *initialize_ss(int size,int s) {
        int i;
        struct vassist *va;
        va = (struct vassist *)malloc(size*sizeof(struct vassist));
        for(i=0;i<size;i++) {
            va[i].key = i;//建堆后i!=key
            va[i].d = U;
            va[i].p = -1;
        }
        va[s].d = 0;
        return va;
    }
    
    //relax for dijkstra
    int relaxd(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改为一个自定义的结构体
        }
        return 1;
    }
    
    //relax for beltman_ford
    int relaxb(struct vassist *va,int u,int v,int w) {//w=w(u,v)
        if(va[v].d>va[u].d+w) {
            va[v].d = va[u].d+w;
            va[v].p = u;
        }
        return 1;
    }
    
    
    int bellman_ford(struct vertex *graph,int *h,int size,int s) {//算法要求不含源点可达的负权回路
        int i,j;
        struct vtable *p;
        struct vassist *va;
        va = initialize_ss(size,s);
        for(i=1;i<size;i++)
            for(j=0;j<size-1;j++) {
                p = graph[j].adj;
                while(p!=NULL) {
                    relaxb(va,j,p->key,p->w);
                    p=p->next;
                }
            }
    
        printf("from %d,\n",s);
        for(j=0;j<size;j++)
            printf("to %d: %d\n",j,va[j].d);
            
    
        for(j=0;j<size;j++) {//对0结点不必要
            p = graph[j].adj;
            while(p!=NULL) {
                if(va[p->key].d>va[j].d+p->w)
                    return 0;
                p = p->next;
            }
        }
        for(j=1;j<=size;j++)
            h[j] = va[j].d;
        free(va);
        h[0] = 0;
        return 1;
    }
    
    int build_min_heap(struct vassist *va,int size) {//建堆
        int i;
        for (i =size/2-1; i>=0; i--)
            min_heapify(va,i,size);
    
        return 1;
    }
    
    
    int min_heapify(struct vassist *va, int i,int heap_size) {
        int l,r,min;
        struct vassist temp;
        int tmin = U;
        l = LEFT(i);
        r = RIGHT(i);
        if ((l < heap_size) &&(va[l].d<va[i].d)) {
            min = l;
            tmin = va[l].d;
        }
        else {
            min = i;
            tmin = va[i].d;
        }
        if ((r < heap_size) &&(va[r].d<va[min].d)) {
            min = r;
            tmin = va[r].d;
        }
        if (!(min == i)) {
            temp.d = va[min].d;
            temp.p = va[min].p;
            temp.key = va[min].key;
    
            va[min].d = va[i].d;
            va[min].p = va[i].p;
            va[min].key = va[i].key;
    
            va[i].d = temp.d;
            va[i].p = temp.p;
            va[i].key = temp.key;
    
            min_heapify(va,min,heap_size);
        }
        return 1;
    }
    
    int heap_extract_min(struct vassist *va,int heap_size) {
        int min;    
        if ( heap_size<1 )
            return -1;
        min = va[0].key;
        va[0].p = va[heap_size -1].p;
        va[0].d = va[heap_size -1].d;
        va[0].key = va[heap_size -1].key;
        heap_size = heap_size -1;
        min_heapify(va,0,heap_size);
        return min;
    }
    
    int inheap(struct vassist *va,int heap_size,int j) {
        int i;
        for(i=0;i<heap_size;i++)
            if(va[i].key == j)
                return i;
        return -1;
    }
    
    int heap_decrease(struct vassist *va,int i,int key_new) {
        struct vassist temp;    
        if(key_new>va[i].d)
            return 0;
        va[i].d = key_new;
        while((i>0)&&(va[PARENT(i)].d > va[i].d)) {
            temp.d = va[i].d;
            temp.p = va[i].p;
            temp.key = va[i].key;
            va[i].d = va[PARENT(i)].d;
            va[i].p = va[PARENT(i)].p;
            va[i].key = va[PARENT(i)].key;
            va[PARENT(i)].d = temp.d;
            va[PARENT(i)].p = temp.p;
            va[PARENT(i)].key = temp.key;
            i = PARENT(i);
        }
        return 1;        
    }
    
    int dijkstra(struct vertex *graph,int len,int s,int **delta) {
        int i,j,heap_size;
        struct vtable *q;
        struct vassist *va;
        int *p;
        p = (int *)malloc(len * sizeof(int));
        for(i=0;i<len;i++)
            p[i] = U;
        p[s] = 0;
        heap_size = len;
    
        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:%d\n",i);
            heap_size--;
            for(j=0;j<heap_size;j++)
                printf("key:%d,d:%d, in array:%d\n",va[j].key,va[j].d,p[va[j].key]);
            q = graph[i].adj;
            while(q!=NULL) {
                j=inheap(va,heap_size,q->key);
                if(j>=0) 
                    if(va[j].d>p[i]+q->w)    
                        heap_decrease(va,j,p[i]+q->w);
                relaxd(p,i,q->key,q->w);//其实可以合并heap_decreas和relax,不过为了接口简单没有这样做
                printf("relax %d to %d ,w is %d\n",i,q->key,q->w);
                q = q->next;
            }
            for(j=0;j<heap_size;j++)
                printf("key:%d,d:%d, in array:%d\n",va[j].key,va[j].d,p[va[j].key]);
        }
        for(i=0;i<len;i++)
            printf("from %d to %d, distance is %d\n",s,i,p[i]);
        
        free(va);
    
        for(i=0;i<len;i++) {
            delta[s][i] = p[i];
        }
        free(p);    
    
    }
    
    int **johnson(struct vertex *g, int n) {
        int i,j;
        int *h,**delta,**d;
        struct vertex *gn;
        struct vtable *p;
        gn = (struct vertex *)malloc(n*sizeof(struct vertex));
        h = (int *)malloc(n*sizeof(int));
        delta = (int**)malloc(n*sizeof(int *));
        d = (int**)malloc(n*sizeof(int *));
        for(i=0;i<n;i++) {
            delta[i]=(int*)malloc(n*sizeof(int));
            d[i]=(int*)malloc(n*sizeof(int));
        }
        for(i=0;i<n;i++) 
            gn[i] = g[i];
        
        for(i=1;i<n;i++) 
                insert(gn,n,0,i,0);
        if(!bellman_ford(gn,h,n,0)) {
            printf("the input graph contains a negative-weight cycle.\n");
            return NULL;
        }
    
        for(i=0;i<n;i++) {
            p = gn[i].adj;
            while(p!=NULL) {
                p->w = p->w+h[i]-h[p->key];
                p=p->next;
            }
        }
        for(i=0;i<n;i++)
            walk(gn,n,i);
        
        printf("before dijkstra\n");
        for(i=1;i<n;i++) {
            dijkstra(gn,n,i,delta);
            for(j=1;j<n;j++)
                d[i][j] = delta[i][j] + h[j] - h[i];
        
        }
        for(i=1;i<n;i++) {
            for(j=1;j<n;j++)
                printf("%d\t",d[i][j]);
            printf("\n");
        }
        return d;
    }
        
    int main(){
        int i,j;
        int **d;
        struct vertex vt[N+1];//为0结点的加入预留位置
        for(i=0;i<N+1;i++) {
            vt[i].adj = NULL;
            vt[i].key = i;
        }
    
        insert(vt,N+1,1,2,3);
        insert(vt,N+1,1,3,8);
        insert(vt,N+1,1,5,-4);
        insert(vt,N+1,2,4,1);
        insert(vt,N+1,2,5,7);
        insert(vt,N+1,3,2,4);
        insert(vt,N+1,4,3,-5);
        insert(vt,N+1,4,1,2);
        insert(vt,N+1,5,4,6);
        d = johnson(vt,N+1);
    
        return 1;
    }
    原博文代码

    (2014.1.9更新)

      不要急着打开上面的代码。正如@DingHy在评论里提到的,它的效率并不能达到预期,而这是由于堆的操作不规范导致的。

      除此以外,回顾下这段一年前写的代码,还有不少问题:各个数据结构和算法耦合性过高,难以复用;辅助数据结构的描述过于抽象;即使对照《算法导论》的伪代码,代码理解起来并不容易。事实上作为作者本人回顾起来都不是很舒服,于是花了不少时间重构了这个算法的代码,尽量降低了耦合性,提高复用性。

      要注意的是,Dijkstra算法使用的是二项堆,这部分需要一个特殊的bheap_decrease()以便于保持辅助数据结构中与二项堆的指针一致,这可能是最不好理解的地方。

      重构的代码包括:

        graph.c  通用的图,使用邻接链表表示

              含有用于保存单源最短路径的辅助数据结构

        bheap.c  二项堆以及相关操作

        Bellman-Ford.c

              Bellman-Ford算法

        Dijkstra.c Dijkstra算法,使用二项堆,实现了特殊的bheap_decrease_special()代替bheap_decrease()

        Johnson.c 利用Bellman-Ford算法和Dijkstra算法实现的Johnson算法,包含一个短小的测试程序,测试用例同《算法导论》图25-1。

      代码比较长,有近千行,因此不再贴于博文正文,而是上传至github:https://github.com/vvy/Johnson-s-algorithm

      虽然经过了原书例子的测试,但是可能仍存在bug,请及时反馈,不过最近比较忙,可能不能及时修正。


    作者:五岳
    出处:http://www.cnblogs.com/wuyuegb2312
    对于标题未标注为“转载”的文章均为原创,其版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

     
  • 相关阅读:
    Ubuntu 14.10 下设置时间同步
    Ubuntu 14.10 下Hadoop FTP文件上传配置
    Hadoop 管理工具HUE配置-集成Unix用户和用户组
    Ubuntu 14.10 下SSH执行远程命令
    Hadoop 管理工具HUE配置-hdfs_clusters配置
    在注册表中无Python3.5安装路径的情况下安装pywin32-
    《BuildingMachineLearningSystemsWithPython》学习笔记
    《利用Python进行数据分析: Python for Data Analysis 》学习随笔
    电脑莫名其妙的被装上了流氓软件DNSunlocker的解决办法
    Python for Data Analysis
  • 原文地址:https://www.cnblogs.com/wuyuegb2312/p/2774115.html
Copyright © 2020-2023  润新知