最小生成树(Prim,Kruskal)

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;
}


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值