优先队列:二叉堆优化的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());
    }
}

posted on 2011-07-25 09:50  oa414  阅读(4481)  评论(0编辑  收藏  举报

导航