基本思路:
定义结点集合U, V (U表示已经选择加入MST的结点集合,V表示未选)
1. 任选一个结点加入U
2. 选择一条边权最小的边,他的两个结点分别属于U, V,并把属于V的那个结点加入U
3. 重复执行2直到V空
伪代码:
C++代码:
int g[mnx][mnx]; int n, m; int d[mnx]; // 朴素 prim, 复杂度O(|V|^2) |V|:点数, |E|:边数 int prim() { memset(d, 0x3f, sizeof d); //初始化 int ret = d[1] = 0; // 先把d[1]弄成0 for(int i = 1; i <= n; ++i) { int u = -1; for(int j = 1; j <= n; ++j) //找到d[u]最小的一个u if((u == -1 || d[u] > d[j]) && d[j] != -1) u = j; ret += d[u]; d[u] = -1; for(int j = 1; j <= n; ++j) // 更新和u邻接的节点的d[j]值 d[j] = min(d[j], g[u][j]); } return ret; }
算法分析:
主要耗费在查找边权最小的边,这一步的二重循环耗费Θ(n2),所以算法的时间复杂度为Θ(n2)。
堆优化改进:
我们用小顶堆来完成查找最小边,和Dijkstra算法一样,算法共进行了n-1次插入、n-1次删除、m-n+1次Siftup运算。总的时间复杂度为O(mlogn)。
伪代码:
C++代码:
int fst[mnx], nxt[mxe], cost[mxe], to[mxe], e; void init() { memset(fst, -1, sizeof fst); e = 0; } void add(int u, int v, int c) { to[e] = v, nxt[e] = fst[u], cost[e] = c, fst[u] = e++; } struct node { int u, dis; node(int u, int dis):u(u), dis(dis) {} bool operator < (const node &b) const { return dis > b.dis; } }; //堆优化, 复杂度O(|E|log|V|), 稠密图时比较慢 int primHeap() { memset(d, 0x3f, sizeof d); d[1] = 0; priority_queue<node> q; q.push(node(1,0)); // 先选定第一个节点 int ret = 0; while(!q.empty()) { int u = q.top().u; int dd = q.top().dis; q.pop(); if(d[u] != dd) continue; // 如果是被更新之前的值的话就不取, continue掉 ret += dd; d[u] = -1; for(int j = fst[u]; ~j; j = nxt[j]) { int v = to[j], c = cost[j]; // 更新 if(d[v] > c && d[v] != -1) { d[v] = c; q.push(node(v, c)); } } } return ret; }
时间: 2024-11-12 08:56:58