2.17总结

今天结合《大话数据结构》做一下最小生成树的知识总结。

最小生成树含义:一个图可能存在多条边,我们一定可以从图中挑出一些边连接上所以的点生成一棵树。如果我们挑选n-1条最小的边连接上n个点,付出最小的代价,从而得到最小生成树。

怎么去找边呢?那就要看下边的两种算法了。

克鲁斯卡尔(Kruskal)算法

思路:直接以边为目标去构建最小生成树,因为权值在边上,直接去找最小权值的边,但是找的过程中要注意新增加的边是否构成了环

用到了图的存储结构中的边集数组结构

 

模板的话前面的总结有写,今天的稍稍有点不同。

/*对边集数组Edge结构的定义*/
/*具体的数据类型根据题目来定,这里就用整型*/
typedef struct
{
    int begin;//边的起点
    int end;//边的终点
    int weight;//边权,或者说边的长度
};
使得sort函数按从小到大排序
bool cmp(Edge a,Edge b)
{
    return a.w<b.w;
}
/*Find函数,找连线顶点的尾部下标*/
int Find(*parent,int x)
{
    while(parent[x]>0)
        x=parent[x];
    return x;
}
/*Kruskal算法*/
void Kruskal_tree(MGraph G)
{
    int i,n,m;//n为顶点数,m为边数
    int parent[maxsize];//存储每个点的根结点,maxsize根据题目的边数来定
    Edge edge[maxsize];//边集数组
    sort(edge+1,edge+1+m,cmp);//将边按从小到大排序,规定边集数组是从i=1开始存储
    for(int i=1;i<=n;i++)
        parent[i]=i;//初始化每个结点的根结点为本身
    for(int i=1;i<=m;i++)
    {
        int begin=Find(parent,edge[i].begin);
        int end=Find(parent,edge[i].end);
        if(begin!=end)//假如begin和end不相等,则证明没有此边没有和现有生成树形成环
        {
            parent[end]=begin;//将此边的结尾顶点end放入以下标为起点的parent中
            printf("(%d,%d),%d",edge[i].begin,edge[i].end,edge[i].weight);
            sum++;//已经加入到生成树的边的数量
        }
        if(sum==n-1)//最小生成树只有n-1条边
            break;
    }
}

复杂度:O(elog e)(e为边数)Find函数的复杂度是O(log e),它外面还有一个for循环。

适用的地方:边数小于点的平方较多时(稀疏图),Kruskal算法有优势。

普里姆(Prim)算法

思路:使用lowcost记录当前已遍历的顶点的权值,每一位对应该顶点在已遍历过程中出现的最小权值。lowcost初始化即任意选一顶点(一般0号顶点)作起始,将邻接矩阵图中该顶点与其它顶点的权值按序填入,该顶点本身权值为0。遍历初始化后的lowcost,找到最小权值min和对应顶点序号k。在lowcost中将已遍历的顶点对应位置0,用于过滤已遍历的顶点(起始点本身权值为0)。在邻接矩阵图中,k位顶点(第k行)按对应位将较小的权值填入lowcost。 遍历lowcost找到最小权值min和对应位置k,然后将k位置0。依此类推每次循环遍历一个顶点,直到遍历全部顶点。adjvex用于记录lowcost所记录权值的对应连线关系:顶点i与顶点adjvex[i]的权值为lowcost[i]。每次替换lowcost中较小的权值时,对应adjvex位也要变更,从而在找到lowcost最小权值时知道连线信息。通过每次(n-1次)循环打印一次连线信息,构建一个完整的最小生成树

用到了图的存储结构中的邻接矩阵

 

/*Prim算法*/
void Prim(MGraph G)
{
    int min,i,j,k;
    int adjvex[maxvex];//保存相关顶点的下标,maxvex是最大的顶点数
    int lowcost[maxvex];//保存相关顶点之间的边的权值
    low[0]=0;//初始化第一个顶点的到自己自身的权值,表示第一个顶点已经加入到树中
    adjvex[0]=0;//初始化第一个顶点的下标为0
    for(i=0;i<G.numvertexes;i++)
    {
        lowcost[i]=G.arc[0][i];//将与第一个顶点想连接的顶点之间的边的权值存入数组
        adjvex[i]=0;//都初始化为第一个顶点的下标
    }
    for(i=0;i<G.numvertexes;i++)//开始最小生成树构建
    {
        min=inf;//inf可以初始化为不可能的大数字,表示不连通,如32767、65535
        j=1;
        k=0;
        while(j<G.numvertexes)将全部顶点过一遍
        {
            if(lowcost[j]!=0&&lowcost[j]<min)//权值不为0且小于min
            {
                min=lowcost[j];//更行最小值
                k=j;//将当前最小值的下标存入k
            }
            j++;
        }
        printf("%d %d",adjvex[k],k);当前顶点的权值最小的边
        lowcost[k]=0;//表示这个点已经找到这个点连接到的边中最小的边
        for(j=1;j<G.numvertexes;j++)//将全部顶点过一遍
        {
           if(lowcost[j]!=0&&lowcost[j]>G.arc[k][j])//如果下标为k为顶点的各边的权值小于之前这些顶点未被加入生成树的权值
           {
               lowcost[j]=G.arc[k][j];//存入较小权值
               adjvex[j]=k;//存入下标为k的顶点
           }
        }
    }
}

复杂度:O(n^2)

适用的地方:边数接近点的平方时(稠密图),Prim算法有优势。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值