1. 图的基本概念:
- 连通图:在无向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该无向图为连通图。
- 强连通图:在有向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该有向图为强连通图。
- 连通网:在连通图中,若图的边具有一定的意义,每一条边都对应着一个数,称为权;权代表着连接连个顶点的代价,称这种连通图叫做连通网。
- 生成树:一个连通图的生成树是指一个连通子图,它含有图中全部n个顶点,但只有足以构成一棵树的n-1条边。一颗有n个顶点的生成树有且仅有n-1条边,如果生成树中再添加一条边,则必定成环。
- 最小生成树:在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树。
2.Prim算法:
此算法可以称为“从点找边”,每次迭代选择代价最小的边对应的点,加入到最小生成树中。算法从某一个顶点s开始,逐渐长大覆盖整个连通网的所有顶点。(通俗点说就是把点集V分为两部分,加入到生成树的A集合,还有剩下的B集合,每次循环找与B集合中与A中的点相连且边权值最小的点加入到A集合,直至B集合为空集合,此时A集合即为所求最小生成树。)时间复杂度为O(v2)。具体做法:
- 维护两个数组adjvex[],lowcost[].两个数组相互对应。lowcost代表与A集合中的点相连的最近的边,lowcost[3] = 12 代表A集合与V3最近的权值为12,lowcost[5] = 0代表V5已经是A点集中的了。而adjvex[3] = 2,代表A集合与V3相连最近的是V2.
- 不断循环找lowcost中的最小值,即与A中的点相连且边权值最小的点k加入到A集合
- 找到每一个点k时,更新adjvex,lowcost数组.
- 直至找到全部的点
代码:
void MiniSpanTree_Prim (Graph G) //默认邻接矩阵
{
int min, i, j, k;
int adjvex[MAXVEX]; //MAXVEX为顶点个数
int lowcost[MAXVEX];
lowcost[0] = 0; //初始化,即将V0加入到生成树
adjvex[0] = 0;
for (i = 1; i < MAXVEX; i++){
lowcost[i] = G.arc[0][i]; //将与V0相连的权值加入到lowcost数组
adjvex[i] = 0;
}
for (i = 1; i < MAXVEX; i++){
min = INF;
j = 1;
k = 0;
for (j = 1; j < MAXVEX; j++){
if (lowcost[j] != 0 && lowcost[j] < min){
min = lowcost[j];
k = j;
}
}
printf("(%d,%d)", adjvex[k], k); //打印新加入的边
lowcost[k] = 0; //当前顶点权值为0,即此顶点完成任务
for (j = 1; j < MAXVEX; j++){ //以点k更新两个数组
if (lowcost[j] != 0 && G.arc[k][j] < lowcost[i]){
lowcost[j] = G.arc[k][j];
adjvex[j] = k;
}
}
}
}
3. Kruskal算法:
此算法可以称为“以边找点”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。(通俗点说,就是不断的找到权值最小的边,如果符合加入此边不会形成闭环,那么就加入。判断闭环可以用并查集。)判断闭环的方法:随着边加入到生成树,会形成多个连通子图,也就是各个点集,再加入新的边时,只需要判断两个点是否在一个连通子图里即可代码:
// poj--1233
#include <bits/stdc++.h>
using namespace std;
const int N = 100 + 10;
int n, a, b, c;
int pre[N];
struct node
{
int x, y, len;
node(int x, int y, int len):x(x),y(y),len(len){}
bool operator < (const node &a) const{
return a.len < len;
}
};
int find(int x){
int r=x;
while(pre[r]!=r){
r=pre[r];
}
int i=x,j;
while(i!=r){
j=pre[i];
pre[i]=r;
i=j;
}
return r;
}
void join(int x,int y){
int f1=find(x);
int f2=find(y);
if(f1!=f2){
pre[f1]=f2;
}
}
priority_queue<node> s; //利用优先队列从小到大找边
int main()
{
while(~scanf("%d", &n) && n){
int cnt = (n * (n - 1)) / 2;
for(int i = 0; i <= N; i++){
pre[i] = i;
}
while(cnt--){
scanf("%d%d%d", &a, &b, &c);
s.push(node(a, b, c));
}
int ans = 0;
while(!s.empty()){
node temp = s.top();
s.pop();
int X = find(temp.x);
int Y = find(temp.y);
if (X != Y){
ans += temp.len;
join(temp.x, temp.y);
}
}
printf("%d\n", ans);
}
return 0;
}