优先队列:二叉堆优化的Prim与Dijkstra算法

终于调出来了,理解之后其实很简单,但是一些细节总是没处理好。直到现在还有一点疑问在。

两者的渐进复杂度都是O(ELogV+VLogV),因为边调整了E次,删除最小元V次,二叉堆的操作都是LogV级别的。

事实上两个算法很相似,Prim只是松弛操作和Dijksrra还有记录ans的方式不同。

在算法导论上看到,Kruskal算法是ELogE的即可以约成E*2LogV即ELogV,渐进意义上是和Prim一样的。

平时很少看见用heap优化的Prim和Dijkstra,可能是因为Kruskal和SPFA有更好的表现或编程复杂度小点吧,当然也看到不少C++用STL的优先队列写的Prim/Dijkstra。

下面是POJ 2387和1287的代码,分别对应Dijkstra和Prim算法。

/*

    很裸很水的最短路,练习二叉堆优化的Dijstra~



    之前二叉堆优化的Prim敲了好几遍前后花了不下八个小时调试还是没有调试成功,

    但是还好,熟悉了优先队列的操作。



    十几天后的今天重新想起这个,终于调出来了堆优化的Dijstra。理解之后还是蛮简单的。



    一些写法并不是最优的,例如heap的实现中可以减少交换元素等。但是有这个自己写的AC

    过的Dijkstra在,以后写二叉堆优化的Prim/Dijkstra和其它优先队列的题目就可以拿它对照着Debug了。



    2011-07-24 23:00

*/





#include <stdio.h>



#define MAXN 1200

#define MAXM 1200000

#define INF 19930317



struct node

{

    int d, v, p;

}heap[MAXN];

int pos[MAXN], hl;



int e[MAXM], cost[MAXM], next[MAXM], g[MAXN], size;



int m, n, s, t;



void insert(int u, int v, int w)

{

    e[++size] = v;

    next[size] = g[u];

    cost[size] = w;

    g[u] = size;

}





void swap(int a, int b)

{

    heap[0] = heap[a];

    heap[a] = heap[b];

    heap[b] = heap[0];

    pos[heap[a].v] = a;

    pos[heap[b].v] = b;

}



void heapfy()

{

    int i = 2;

    while (i <= hl)

    {

        if ((i < hl) && (heap[i + 1].d < heap[i].d))

            i++;

        if (heap[i].d < heap[i >> 1].d)

        {

            swap(i, i >> 1);

            i <<= 1;

        }

        else

            break;

    }

}







void decrease(int i)

{

    while ((i != 1) && (heap[i].d < heap[i >> 1].d))

    {

        swap(i, i >> 1);

        i >>= 1;

    }

}



void relax(int u ,int v, int w)

{

    if (w + heap[pos[u]].d < heap[pos[v]].d)

    {

        heap[pos[v]].p = u;

        heap[pos[v]].d = w + heap[pos[u]].d;

        decrease(pos[v]);

    }

}



void delete_min()

{

    swap(1, hl);

    hl--;

    heapfy();

}



void init()

{

    int u ,v ,w, i;



    scanf("%d%d", &m, &n);

    for (i = 1; i <= m; i++)

    {

        scanf("%d%d%d", &u, &v, &w);

        insert(u, v, w);

        insert(v, u, w);

    }

    s = 1;

    t = n;

}







int dijkstra()

{

    int u, p, i;



    for (i = 1; i <= n; i++)

    {

        heap[i].v = pos[i] = i;

        heap[i].d = INF;

    }

    heap[s].p = s;

    heap[s].d = 0;

    swap(1, s);

    hl = n;

    while (hl)

    {

        u = heap[1].v;

        delete_min();

        p = g[u];

        while (p)

        {

            if (pos[e[p]] <= hl)

                relax(u, e[p], cost[p]);

            p = next[p];

        }



    }

}

int main()

{

    init();

    dijkstra();

    printf("%d\n", heap[pos[t]].d);

    return 0;

}

 

 

 

/*

    二叉堆优化的Prim,相比Dijkstra只有松弛操作不同



    很郁闷的是heapfy函数,就是从堆顶往下筛,仿照别人的写法AC了,自己写的迭代以减少

    交换次数的在样例的一个数据陷入死循环了,而仿照数据结构与算法分析写的WA了。



    以前没写过带有pos作为优先队列的heap这样调整,所以也不知道哪错了。



    下面是强大的样例:

5 7

1 2 5

2 3 7

2 4 8

4 5 11

3 5 10

1 5 6

4 2 12

    有空调调吧。



    2011-07-25 09:32

*/







#include <stdio.h>



#define MAXN 1200

#define MAXM 120000

#define INF 19930317



int e[MAXM], cost[MAXM], next[MAXM], g[MAXN], size;



struct node

{

    int d, v;

}heap[MAXN];

int pos[MAXN], hl;



int m, n;





void swap(int a, int b)

{

    heap[0] = heap[a];

    heap[a] = heap[b];

    heap[b] = heap[0];

    pos[heap[a].v] = a;

    pos[heap[b].v] = b;

}





void heapfy()

{

    int i = 2;

    while (i <= hl)

    {

        if ((i < hl) && (heap[i + 1].d < heap[i].d))

            i++;

        if (heap[i].d < heap[i >> 1].d)

        {

            swap(i, i >> 1);

            i <<= 1;

        }

        else

            break;

    }

}







/*

void heapfy()

{

    int i = 1, j = 2;



    heap[0] = heap[1];

    while (j <= hl)

    {

        if ((j < hl) && (heap[j].d > heap[j+1].d))

            j++;

        if (heap[j].d < heap[0].d)

        {

            heap[i] = heap[j];

            pos[heap[i].v] = i;

            i = j;

            j >>= 1;

        }

        else

            break;

    }

    heap[i] = heap[0];

    pos[heap[i].v] = i;

}





void heapfy()

{

    int i, j;



    heap[0] = heap[1];

    for (i = 1; i * 2 <= hl; i = j)

    {

        j = i * 2;

        if (j < hl && heap[j+1].d < heap[j].d)

            j++;

        if (heap[0].d > heap[j].d)

        {

            heap[i] = heap[j];

            pos[heap[i].v] = i;

        }

    }

    heap[i] = heap[0];

    pos[heap[i].v] = i;

}

*/



void decrease(int i)

{

    heap[0] = heap[i];

    while ((i > 1) && (heap[0].d < heap[i >> 1].d))

    {

        heap[i] = heap[i >> 1];

        pos[heap[i].v] = i;

        i >>= 1;

    }

    heap[i] = heap[0];

    pos[heap[i].v] = i;

}













void delete_min()

{

    swap(1, hl);

    hl--;

    heapfy();

}









void relax(int v, int w)

{

    if (w < heap[pos[v]].d)

    {

        heap[pos[v]].d = w;

        decrease(pos[v]);

    }

}



void insert(int u, int v, int w)

{

    e[++size] = v;

    cost[size] = w;

    next[size] = g[u];

    g[u] = size;

}



void init()

{

    int i, u, v, w;



    size = 0;

    memset(g, 0, sizeof(g));

    for (i = 1; i <= m; i++)

    {

        scanf("%d%d%d", &u, &v, &w);

        insert(u, v, w);

        insert(v, u, w);

    }

}



int prim()

{

    int mst = 0, u , p, i;



    for (i = 1; i <= n; i++)

    {

        pos[i] = heap[i].v = i;

        heap[i].d = INF;

    }

    heap[1].d = 0;

    hl = n;

    while (hl)

    {

        u = heap[1].v;

        mst += heap[1].d;

        delete_min();

        for (p = g[u]; p; p = next[p])

            if (pos[e[p]] <= hl)

                relax(e[p], cost[p]);

    }

    return mst;

}



int main()

{

    while (scanf("%d%d", &n, &m) == 2 && n)

    {

        init();

        printf("%d\n", prim());

    }

}

你可能感兴趣的:(dijkstra)