kruskal算法的链接 http://blog.csdn.net/winoros/article/details/21279967
接下来是prim算法。prim算法和kruskal算法同样是贪心,不过贪心的思路不同。
prim算法的步骤如下
1)标记任意一个点。
2)然后找一条边权最小的而且是一端被标记,另一端未被标记的边加进来。也就加入合法的最短边,然后将该边为被标记的顶点标记
3)这样进行到所有点都被标记即为结束。
算法的正确性个人感觉可以利用数学归纳法证明。证明如下
1)只有两个点标记时,明显该边边权必为最小。
2)假设有k个点被标记时,所加入的边了这k个点的最小生成树。
3)由于再加入的时候选取的是与现有生成树相连的边权最小的边,所以这时的k个边构成了这k+1个的最小生成树。
4)从而当所有点都被标记时所得边即构成了这张图的最小生成树。
具体的算法实现中,我们用一个bool数组marked[i]标记点i是否已计入最小生成树中,一个int数组dis[i]来表示未标记的点i加入最小生成树的最小权值(也就是点i到已标记的点最小的距离)
prim算法可以用堆优化,这里没有使用,只用邻接表实现。这里复杂度为O(N^2)(N为顶点的个数),相对于kruskal,prim算法更适合求稠密图,因为prim的复杂度与边的个数没有关系。
下面是代码
//made by winoros
vector<pair<int, int> > v[MAX_N];
bool marked[MAX_N];
int dis[MAX_N];
int prim(int n) {
memset(marked, 0, sizeof(marked));
fill(dis, dis + n, INT_MAX);//这里顶点的标记总0开始
dis[0] = 0;
int ans = 0;
for(int i = 0; i <= n; i++) {
int mark = -1;
for(int j = 0; j <= n; j++) {
if(!marked[j]) {//这个if是在找最小的dis[j]
if(mark == -1) mark = j;
else if(dis[j] < dis[mark]) mark = j;
}
}
if(mark == -1) break; //如果已经无点可找(也就是所有的点都已进入最小生成树中),跳出循环
marked[mark] = true;
ans += dis[mark];
for(int j = 0; j < v[mark].size(); j++) {//这个for是更新剩余点到最小生成树的距离
if(!marked[v[mark][j].first]) {
int x = v[mark][j].first;
dis[x] = min(dis[x],v[mark][j].second);
}
}
}
return ans;
}