一、Prim算法
1.算法原理:
和Dijkstra算法非常相似,都是从某个顶点开始,通过将边添加到新的集合中去。此算法添加边的原则描述如下:
设新的节点集合为V,由新的节点集合确定的边集为E = { (i, j) | i, j ∈V } (当然这些边构成的图是一棵树),每次将与V集合相连的点的不在E集合的最小边添加到E中去,同时将相连的点添加到V中去,在这个过程中,从原来的图中删除点和边。在添加的过程中还要注意不能有环的产生
2.难点:
这个算法主要的难点在于如何高效的确定我们需要添加的边,如果每次都遍历V集合的所有相关边,那么算法的复杂度比较高,O(n^2);但是我们可以借鉴在Dijkstra中的处理方法,通过队列来管理我们需要使用的边。优化以后算法复杂度变为O(elogv)
int cost[maxn][maxn]; //表示边权值
int mincost[maxn]; //从集合V出发的边每个顶点的最小权值
bool used[maxn]; //表示顶点i是否被取出,包含在V中
int vertex_num; //顶点数
int Prim() {
//未加入的点的边权值为INF
fill(mincost, mincost + vertex_num, INF);
fill(used, used + vertex_num, false);
mincost[0] = 0;
int res = 0;
while (true) {
int v = -1;
for (int u = 0; u < vertex_num; u++) {
if (!used[u] && (v == -1 || mincost[u] < mincost[v])) v = u;
}
if (v == -1) break;
used[v] = true;
res += mincost[v];
for (int u = 0; u < vertex_num; u++) {
if (!used[u]) mincost[u] = min(mincost[u], cost[v][u]);
}
}
return res;
}
关于如下语句的解释为:对于没有加入V的点,如果该点与已经在V中的点有连接,那么使用边权值进行最小值的更新(已经在V中的点就没有必要更新了,因为更新也没有意义)
if (!used[u]) mincost[u] = min(mincost[u], cost[v][u]);
Prim算法的队列维护方式:
typedef pair<int, int> P; //first: mincost, second: vertex
int cost[maxn][maxn];
bool used[maxn];
int vertex_num;
int Prime() {
priority_queue<P, vector<P>, greater<P> > que;
que.push(P(0, 0));
int v = -1, ans;
while (true) {
if (!que.empty()) {
v = que.top().second;
ans += que.top().first;
que.pop();
}
if (v == -1) break;
used[v] = true;
for (int u = 0; u < vertex_num; u++) {
if (!used[u]) que.push(P(cost[v][u], u));
}
}
return ans;
}
1.算法原理:
Kruskal算法按照边的权值顺序从小到大查看一遍,如果没有圈产生,就把当前边加入到生成树中
2.算法实现难点:
主要是判断是否产生圈,在prim算法中,我们是对节点数进行考虑,有一个关于节点状态的数组used[maxn]为我们排除那些已经被选取的点,然而这里只用到边,所以没有直接快速的方法去确定,但是我们可以利用并查集来排除产生环的情况,算法复杂度为O(elogv)
struct Edge {
int u, v, cost;
};
int vertex_num, edge_num; //顶点数,边数
int pre_V[maxn_V]; //记录顶点并查集
int rank[maxn_V]; //记录阶数
Edge edge[maxn_E]; //记录边
bool comp(const Edge& lhs, const Edge& rhs) {
return lhs.cost < rhs.cost;
}
void init_union_find(int n) {
for (int i = 0; i < vertex_num; i++) pre_V[i] = i;
}
int find(int value) {
if (pre_V[value] == value) return value;
return pre_V[value] = find(pre_V[value]);
}
void unite(int lhs, int rhs) {
lhs = find(lhs);
rhs = find(rhs);
if (lhs == rhs) return ;
if (rank[lhs] < rank[rhs]) {
pre_V[lhs] = rhs;
}
else {
pre_V[rhs] = lhs;
if (rank[rhs] == rank[lhs]) rank[lhs]++;
}
}
bool same(int lhs, int rhs) {
return find(lhs) == find(rhs);
}
int Kruskal() {
sort(edge, edge + edge_num, comp);
init_union_find(vertex_num);
int ans = 0;
for (int i = 0; i < edge_num; i++) {
Edge e = edge[i];
if (!same(e.u, e.v)) {
unite(e.u, e.v);
ans += e.cost;
}
}
return ans;
}