MST算法

无向图的最小生成树是连接所有顶点的生成树中边权和最小的一棵。本文介绍了Prim算法和使用并查集实现的Kruscal算法求解最小生成树,并详细讨论了并查集的概念、操作及其在Kruscal算法中的应用,包括按高度合并和路径压缩的改进策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

无向图中,如果它的子图是连通图,并且是一颗树,则称其为生成树。

如果生成树的各边权值之和最小,则称其为最小生成树(MST)。

Prim算法求最小生成树:

int cost[MAX_V][MAX_V];    //边
int mincost[MAX_V];      //当前集合到该点的最短距离
bool used[MAX_V];       //标记该边是否纳入集合中
int V;       //节点数

int prim()
{
    for(int i=0;i<V;i++) //初始化
    {
        mincost[i]=INF;
        used[i]=flase;
    }
    mincost[0]=0;//一开始只有这一个是0,其余都是INF,所以这个是最小值
    
    int res=0;
    while(true)
    {
        int vx=-1; //每次找边开始时先对vx初始化,使得第一次时vx就等于数组中第一个未纳入集合中的节点
        for(int i=0;i<V;i++)
        {
            //在未纳入集合中的点中找最小边权的节点。分第一次赋值和之后赋值的情况
            if(!used[i]&&(vx==-1||mincost[i]<mincost[vx]))  vx=i;
        }
        if(vx==-1) break;  //所有节点全找完
        used[vx]=true;
        res+=mincost[vx];
        
        for(int i=0;i<V;i++)
        {
            mincost[i]=min(mincost[i],cost[vx][i]); //加入新点vx后更新最小边mincost[]
        }
    }
}

Kruscal求最小生成树(并查集实现):

struct edge {int u,v,cost;};   //定义边

bool cmp(const edge& e1, const edge& e2)  // 定义sort的规则
{
    return e1.cost<e2.cost;
}

edge es[MAX_E];   //保存边信息
int V,E;  //节点数和边数

int kruscal()
{
    sort(es,es+E,cmp);  //首先对所有边排序
    int_union_find(V);   //对并查集初始化,其实就是初始化V个孤立的节点
    int res=0;
    for(int i=0;i<E;i++)//按顺序遍历各节点
    {
        edge e=es[i];
        if(!same(e.u,e.v)) //same函数时判断两个节点是否在一棵树(一个并查集集合)中
        {
            merge(e.u,e.v);  //并查集的合并操作
            res+=e.cost;
        }
    }
    return res;
}
//Kruscal复杂度为O( Elog(V) )

并查集

英文 disjoint tree ,即不相交的集合,将编号为1~N的N个对象划分为不相交的集合,在集合中选择某一个代表性元素代表该集合。

在并查集创建时要初始化:用n个节点表示n个元素,最开始没有边

常见的两种操作:1.合两个集合 2.找某元素属于哪个集合

实现方法(from hdu lcy课件):

1551580680917

合并的改进(按高度合并: 为了避免树的退化,所以在合并树的时候不是简单的将编号大的合并到编号小的节点上,二十要比较两颗树的高度,将更矮的树合并到较高的中去。

void merge(a,b)  //伪代码
{
    fx=find(a);  //对于a,b若不能确定他们是否在同一颗树,要先验证
    fy=find(b);
    if(fx==fy)
        return ;
    
    if(height(fx)==height(fy))  //相等时树的高度还会增加一层
    {
        height(fx)=height(fy)+1;
        set[fy]=fx;
    }
    else if(height(fx)<height(fy))
        set[fx]=fy;
    else 
        set[fy]=fx;
}
//注意merge合并是 树 的合并,所以要找到 根节点 去合并

查找的改进(找到后路径压缩: 并查集中每个节点的set(i)是父节点,而查找最终结果是返回这棵树的根节点。

int find(x)   //伪代码
{
    r=x;
    //第一步要找到x节点所属集合的根
    while(set[r]!=r)
        r=set[r];
    i=x;
    //找到后r为根节点,第二步要将上步中寻找路径上的所有节点都变成根的直接子代,实现路径压缩
    while(i!=r)
    {
        j=set[i];
        set[i]=r;
        i=j;
    }
    return r;
}

简单起见,即使在路劲压缩后树的高度改变,也不对这棵树的高度height[i]做修改。

并查集初始化时的点都是离散的,即每一个都是set[i]=i。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值