最小生成树

最小生成树

构成生成树的准则有三条:
<1> 必须只使用该网络中的边来构造最小生成树。
<2> 必须使用且仅使用n-1条边来连接网络中的n个顶点
<3> 不能使用产生回路的边。

普里姆(Prim)算法

以某顶点为起点,逐步找各各顶点上最小权值的边来构建最小生成树

1. 代码

void MiniSpanTree_Prim(MGraph G)
{
    int min, i, j , k;
    int adjvex[MAXVEX];   //保存相关顶点下标
    int lowcost[MAXVEX];  //保存相关顶点间边的权值
    lowcost[0] = 0;       //v0作为最小生成树的根开始遍历,权值为0  凡是lowcost数组的值被设置为0则表示纳入最小生成树
    adjvex[0] = 0;        //v0第一个加入
    for(i = 1; i < G.numVertexes; i++)
    {
        lowcost[i] = G.arc[0][i]; //将邻接矩阵第0行所有权值先加入数组
        adjvex[i] = 0;            //初始化全部先为v0的下标
    }
    //真正构造最小生成树的过程
    for(i = 1; i < G.numVertexes; i++)
    {
        min = INFINITY; //初始化最小权值为65535等不可能权值
        j = 1;
        k = 0;
        //遍历全部顶点
        while(j < G.numVertexes)
        {
            //找出lowcost数组已存储的最小权值
            if(lowcost[j] != 0 && lowcost[j] < min)
            {
                min = lowcost[j];
                k = j; //将发现的最小权值的下标存入k 以便使用
            }
            j++;
        }
        //打印当前顶点边中权值最小的边
        printf("(%d, %d)", adjvex[k], k);
        lowcost[k] = 0; //将当前顶点的权值设置为0, 表示顶点已经完成任务,进行下一个顶点的遍历
        //邻接矩阵k行逐个遍历全部顶点
        for(j = 1; j < G.numVertexes; j++)
        {
            if(lowcost[j] != 0 && G.arc[k][j] < lowcost) //lowcost[j] != 0表示已经是生成树的顶点不参与最小权值的查找
            {
                lowcost[j] = G.arc[k][j];
                adjvex[j] = k;
            }
        }
    }
}

2. 解析

简单描述

<1> 设一个有n个顶点的连通网络为G(V,E),最初先构造一个只有n个顶点,没有边的非连通图T={V,空},图中每个顶点自成一格连通分量。
<2> 在E中选择一条具有最小权植的边时,若该边的两个顶点落在不同的连通分量上,则将此边加入到T中;否则,即这条边的两个顶点落到同一连通分量 上,则将此边舍去(此后永不选用这条边),重新选择一条权植最小的边。
<3> 如此重复下去,直到所有顶点在同一连通分量上为止。

详解

普里姆算法详解

克鲁斯卡尔(Kruskal)算法

从边出发,直接去找最小权值的边来构建最小生成树,构建时要考虑是否会形成环路

1. 代码

typedef struct Edge
{
    int begin;
    int end;
    int weight;
}Edge;

int Find(int *parent, int f) //查找连线顶点的尾部下标
{
    while(parent[f] > 0)
    {
        f = parent[f];
    }
    return f;
}
//生成树
void MiniSpanTree_Kruskal(MGragh G)
{
    int i, n, m;
    Edge edges[MAXEDGE];
    int parent[MAXVEX]; //定义一维数组用来判断边与边是否形成回路
    //此处省略将邻接矩阵G转化为边集数组edges并由小到大排序的代码
    for(i = 0; i < G.numVertexes; i++)
    {
        parent[i] = 0;
    }
    for(i = 0; i < G.numEdges; i++)
    {
        n = Find(parent, edges[i].begin);
        n = Find(parent, edges[i].end);
        if(n != m) //不等 则表明没有形成回路
        {
            parent[n] = m; //将此边的结尾顶点放入下标为起点的parent中,表示此顶点已经在生成树集合里
            printf("(%d, %d) %d", edges[i].begin, edges[i].end, edges[i].weight);
        }
    }
} 

2. 解析

简单概述

①记Graph中有v个顶点,e个边
②新建图Graphnew,Graphnew中拥有原图中相同的e个顶点,但没有边
③将原图Graph中所有e个边按权值从小到大排序
④环:从权值最小的边开始遍历每条边 直至图Graph中所有的节点都在同一个连通分量中 如果这条边连接的两个节点于图Graphnew中不在同一个连通分 添加这条边到图Graphnew中

详解

克鲁斯卡尔算法详解

总结

普里姆算法主要针对顶点展开,即边数非常多的情况下更好,适合稠密图
克鲁斯卡尔算法主要针对边,边数少效率更高,适合稀疏图

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值