MST 最小生成树

最小生成树的定义:

在一给定的无向图G=(U,V)中,(U,V)代表链接顶点U,V的边,而W(U,V)代表此边的权重,若存在T为E(无向图边的集合)的子集()且为无循环图,使得:


的W(T)最小,则此T为G的最小生成树(最小权重生成树的简称)。

性质:

  • 最小生成树的边数必然是顶点数减一,|E| = |V| - 1。
  • 最小生成树不可以有循环。
  • 最小生成树不必是唯一的。

Prim算法实现:(适合求稠密图的最小生成树问题)

伪代码:
PrimMST(G,T){
    //T是边的集合,U是顶点的集合;
    //u,v是顶点,设任意顶点为v0
    T=空;U={v0};
    while(U!=V){
        找出一条满足u属于U且v属于V-U的权值最小的边(u,v);
        T=T并{(u,v)};  //集合的并运算
        U=U并{v};
    }
}

/*==================================================*\
| Prim 求MST
| INIT: cost[][]耗费矩阵(inf为无穷大,表示两个顶点之间不连通,(u,v)不属于E);
| CALL: prim(cost, n); 返回-1代表原图不连通;
\*==================================================*/
#define typec int // type of cost
const typec inf = 0x3f3f3f3f; // max of cost
int vis[V]; typec lowc[V];
typec prim(typec cost[][V], int n){ // vertex: 0 ~ n-1
    int i, j, p;
    typec minc, res = 0;
    memset(vis, 0, sizeof(vis));
    vis[0] = 1;
    for (i=1; i<n; i++) lowc[i] = cost[0][i];
    for (i=1; i<n; i++) {
    minc = inf; p = -1;
    for (j=0; j<n; j++)
        if (0 == vis[j] && minc > lowc[j]) {
        minc = lowc[j]; p = j;
    }
    if (inf == minc) return -1; // 原图不连通
    res += minc; vis[p] = 1;
    for (j=0; j<n; j++)
        if (0 == vis[j] && lowc[j] > cost[p][j])
            lowc[j] = cost[p][j];
    }
    return res;   //返回最小权值和
}

Kruskal算法:(适合求稀疏图的最小生成树的问题)

将所有边排序,记第i小的边为e[i](1<=i<m)
初始化MST为空
初始化连通分量,让每个点成为一个独立的连通分量
for(int i=0;i<m;i++)
  if(e[i].u和e[i].v不在同一连通分量中)
    {
    把边e[i]加入MST
    合并e[i].u和e[i].v所在的连通分量
  }

struct edge{
    int u, v, w;
};

const int NODE_NUM = 102;
edge e[NODE_NUM*NODE_NUM];
int father[NODE_NUM];
int n, ne;    //n是顶点的个数,ne是边的个数

bool cmp(const edge& a, const edge& b)
{
    return a.w < b.w;   //return a.w > b.w; ---是求最大生成树
}

void make_set()
{
    for (int i = 1; i <= n; ++i)
        father[i] = i;
}

int find_set(int i)
{
    if (father[i] != i){
        father[i] = find_set(father[i]);
    }
    return father[i];
}

bool union_set(int a, int b) //a --> b
{
    a = find_set(a);
    b = find_set(b);
    if (a != b){  //没有共同祖先,说明没有形成回路
        father[a] = b; //将节点纳入最小生成树集合
        return true;
    }
    else{
        return false;
    }
}

int kruskal()
{
    int i, mst_edge = 0, sum = 0;
    make_set();
    sort(e, e+ne, cmp);  //将边按升序排序
    for (i = 0; i < ne; ++i){
        //如果加入的边不会使树形成回路
        if (union_set(e[i].u, e[i].v)){
            sum += e[i].w;
            //如果纳入的边数等于顶点数-1,则说明最小生成树形成
            if (++mst_edge == n - 1){
                return sum;    //如果图是连通图,返回权值
            }
        }
    }
    return mst_edge;   //如果不是连通图,则返回最大的连通(可以是多个生成树)的边的个数,
}

//边的信息的输入
int main(){
    //代码 
    scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);  //有向边的输入
    //代码
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值