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