普里姆算法(Prim)

 

 

普里姆算法是归并顶点的算法,与边数无关,所以适用于稠密图

 

构造最小生成树一定有下面两个特点:

1、尽量选取最小的权值的边,并且不能有回路

2、n个顶点只选取n-1条边。

 

 

普里姆算法是由最小生成树(简称为:MST)的一条性质引出来的:

假设N=(V,E)是一个连通图,U是顶点集V的一个非空子集。若(u,v)是一条具有组小权值的边,其中u∈ U,v∈ V-U,则必存在一颗包含(u,v)的最小生成树。

 

 

 

prim算法思想:

假设N=(V,E)时连通图,TE是N上最小生成树中边的集合。

1、U={uo}(uo∈V),TE={}.

2、在所有u∈U,v∈V-U的边(u,v)∈E中找一条权值最小的边(uo,vo)并入集合TE,同时vo并入U。

3、重复2,直至U=V为止。

此时TE中必有n-1条边,则T=(V,TE )为N的最小生成树。

 

图解如下:

 

(1)图中有6个顶点v1-v6,每条边的边权值都在图上;在进行prim算法时,我先随意选择一个顶点作为起始点,当然我们一般选择v1作为起始点,好,现在我们设U集合为当前所找到最小生成树里面的顶点,TE集合为所找到的边,现在状态如下:

U={v1}; TE={};

(2)现在查找一个顶点在U集合中,另一个顶点在V-U集合中的最小权值,如下图,在红线相交的线上找最小值。

通过图中我们可以看到边v1-v3的权值最小为1,那么将v3加入到U集合,(v1,v3)加入到TE,状态如下:

U={v1,v3}; TE={(v1,v3)};

(3)继续寻找,现在状态为U={v1,v3}; TE={(v1,v3)};在与红线相交的边上查找最小值。

我们可以找到最小的权值为(v3,v6)=4,那么我们将v6加入到U集合,并将最小边加入到TE集合,那么加入后状态如下:

U={v1,v3,v6}; TE={(v1,v3),(v3,v6)}; 如此循环一下直到找到所有顶点为止。

(4)下图像我们展示了全部的查找过程:

 

现在,普里姆算法的思路已经明确了,下面需要解决的问题是,如何实现普里姆算法:

 

普里姆算法的实现:

要实现普里姆算法,需要记录各个顶点是否已经并入U,还需要记录最小边在U和V-U中的顶点和权值,所以下面设置了一个数据类型,解决了上面所有问题。

struct{
    int        adjvex;    //   最小边在U中的那个顶点
    int        lowcost;    //   最小边上的权值
}closedge[MVNum];

closedge[i-1]是V-U中的顶点vi到U中的最小权值的边,他有两个域:adjvex是该边在U中的顶点,lowcost是该边的权值。

显然,closedge[i-1].lowcost=Min{cost(u,vi)|u∈U},其中cost(u,v)表示边(u,v)的权值。

 

接下来,还需要解决的问题是,如何才能确定一个顶点是否已经并入U。这里用了一个巧妙的办法,假设接下来要让vi这个顶点并入U,则令:closedge[vi].lowcost=0;这样就可以间接的表示为将vi并入了U。

 

这样一个数据结构即存储了最小边的权值,又存储了最小边的两个顶点。

 

prim算法步骤:

1、首先将初始化顶点u加入U中,对其余的每个顶点vi,经closedge[i]初始化为到u的边的信息。

2、循环n-1次,做如下步骤:

  • 从各组边closedge中选出最小边closedge[k],输出此边
  • 将k并入U中
  • 更新剩余的每组最小边信息closedge[j],对于V-U中的边,新增加了一条从k到j的边,如果新边的权值比closedege[j].lowcost小,则将closedege[i].lowcost更新为新边的权值。

 

算法代码:

注:这里的代码并不是可以直接拷贝就能实现的,里面是算法不是代码实现。并且是根据邻接矩阵实现的。

void MiniSpanTree_Prim(Graph G,VerTexType u)
{
    int k=LocateVex(G,u);
    for(int i=0;i<G.vexnum;i++)
        if(i!=k)
        {
           closedge[i].lowcost=G.arcs[k][i];     //初始化为k到其余顶点的权值
           closedge[i].adjvex=k;            
        }

    closedge[k].lowcost=0;        //    将k点并入U中。

//----------初始化结束,进行n-1次循环,确定出n-1条边------
    for(int i=1;i<G.vexnum;i++)
    {
         int min=MaxInt;        //将选取最小边的min初始化为极大值
         for(int j=0;j<G.vexnum;j++)
            if(closedge[j].lowcost!=0&&closedge[j].lowcost<min) 
            {//顶点i没有并入U(closedge[j].lowcost!=0)并且权值小于当前最小的min
               
                 min=closedge[j].lowcost;        //更新当前最小权值
                 k=i;        //记录当前最下权值边在V-U中的顶点下标
            }

        //输出该路径
        cout<<G.vexs[closedge[k].adjvex].name<<"----->"<<G.vexs[k].name<<endl;

        closedge[k].lowcost=0;        //将k并入U中

        //跟新当前从U到V-U中顶点的路径
        for(int j=0;j<G.vexnum;j++)
        {
            if(G.arcs[k][j]<closedge[j].lowcost)
            {
                closedge[j].lowcost=G.arcs[k][j];
                closedge[j].adjvex=k;
            }
        }

        
    }
}

 

总结一下:

普里姆算法分为两部分:

1、初始化:将起始顶点u到V-U的closedge[i]初始化为u到其余顶点边的信息,并经u并入U

2、进行n-1次循环: 先找出当前从U到V-U到最小权值边,然后将该边在V-U的顶点并入U,然后更新从      U到V-U中顶点的路径

 

当这个算法结束后,closedge[]

 

                      

 

  • 39
    点赞
  • 160
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
普里姆(Prim)算法和克鲁斯卡尔(Kruskal)算法都是用于解决最小生成树问题的算法。 最小生成树问题是指在一个无向连通图中,找到一棵生成树,使得树上所有边的权值之和最小。生成树是指一个无向图的生成子图,它是一棵树,且包含图中所有顶点。 下面我们分别介绍普里姆算法和克鲁斯卡尔算法: 1. 普里姆算法 普里姆算法是一种贪心算法,它从一个任意点开始,逐步扩展生成树,每次选择当前生成树到未加入的点中距离最近的点,并将其加入生成树。 具体实现步骤如下: - 随机选择一个起始点,将其加入生成树。 - 在生成树中的所有节点中,找到到未加入生成树的节点中距离最小的节点,将其加入生成树。 - 重复以上步骤,直到生成树包含了所有节点。 2. 克鲁斯卡尔算法 克鲁斯卡尔算法也是一种贪心算法,它从边集合中选择边,逐步扩展生成树,每次选择当前边集合中权值最小的边,并将其加入生成树。 具体实现步骤如下: - 将所有边按照权值从小到大排序。 - 从权值最小的边开始,逐个加入生成树,如果加入当前边会形成环,则不加入该边。 - 重复以上步骤,直到生成树包含了所有节点。 两种算法的时间复杂度都是O(ElogE),其中E为边数。普里姆算法在处理稠密图时效率更高,而克鲁斯卡尔算法在处理稀疏图时效率更高。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值