最小生成树算法详解(prim+kruskal)

最小生成树概念:

一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。 最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。最小生成树其实是最小权重生成树的简称。

prim:

普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小。

prim算法求最小生成树的时候和边数无关,和顶点树有关,所以适合求解稠密网的最小生成树。

prim算法的步骤包括:

1. 将一个图分为两部分,一部分归为点集U,一部分归为点集V,U的初始集合为{V1},V的初始集合为{ALL-V1}。

2. 针对U开始找U中各节点的所有关联的边的权值最小的那个,然后将关联的节点Vi加入到U中,并且从V中删除(注意不能形成环)。

3. 递归执行步骤2,直到V中的集合为空。

4. U中所有节点构成的树就是最小生成树。

实现图解:

图例 说明 不可选 可选 已选(Vnew

此为原始的加权连通图。每条边一侧的数字代表其权值。 - - -

顶点D被任意选为起始点。顶点ABEF通过单条边与D相连。A是距离D最近的顶点,因此将A及对应边AD以高亮表示。 C, G A, B, E, F D

下一个顶点为距离DA最近的顶点。BD为9,距A为7,E为15,F为6。因此,FDA最近,因此将顶点F与相应边DF以高亮表示。 C, G B, E, F A, D
算法继续重复上面的步骤。距离A为7的顶点B被高亮表示。 C B, E, G A, D, F

在当前情况下,可以在CEG间进行选择。CB为8,EB为7,GF为11。E最近,因此将顶点E与相应边BE高亮表示。 C, E, G A, D, F, B

这里,可供选择的顶点只有CGCE为5,GE为9,故选取C,并与边EC一同高亮表示。 C, G A, D, F, B, E

顶点G是唯一剩下的顶点,它距F为11,距E为9,E最近,故高亮表示G及相应边EG G A, D, F, B, E, C

现在,所有顶点均已被选取,图中绿色部分即为连通图的最小生成树。在此例中,最小生成树的权值之和为39。 A, D, F, B, E, C, G

再来一张比较容易懂的图片:

(a):一个无向图,记录了各点之间的权值关系

(b):在图中选择一个与{v1}连接最小的点v3

(c):选择一个与{v1,v3}连接最小的点v6

(d):选择一个与{v1,v3,v6}连接最小的点v4

(e):选择一个与{v1,v3,v6,v4}连接最小的点v2

(f):选择一个与{v1,v3,v6,v4,v2}连接最小的点v5

生成完毕。

Kruskal算法(并查集实现)

Kruskal是一种用来寻找最小生成树的算法,在剩下的所有未选取的边中,找最小边,如果和已选取的边构成回路,则放弃,选取次小边。

实现过程

1).记Graph中有v个顶点,e个边

2).新建图Graphnew,Graphnew中拥有原图中相同的e个顶点,但没有边

3).将原图Graph中所有e个边按权值从小到大排序

4).循环:从权值最小的边开始遍历每条边 直至图Graph中所有的节点都在同一个连通分量中  if 这条边连接的两个节点于图Graphnew中不在同一个连通分量中   添加这条边到图Graphnew

  图例描述:

首先第一步,我们有一张图Graph,有若干点和边

将所有的边的长度排序,用排序的结果作为我们选择边的依据。这里再次体现了贪心算法的思想。资源排序,对局部最优的资源进行选择,排序完成后,我们率先选择了边AD。这样我们的图就变成了下图

在剩下的变中寻找。我们找到了CE。这里边的权重也是5

依次类推我们找到了6,7,7,即DF,AB,BE。

下面继续选择, BC或者EF尽管现在长度为8的边是最小的未选择的边。但是现在他们已经连通了(对于BC可以通过CE,EB来连接,类似的EF可以通过EB,BA,AD,DF来接连)。所以不需要选择他们。类似的BD也已经连通了(这里上图的连通线用红色表示了)。最后就剩下EG和FG了。当然我们选择了EG。

代码:

prim;

#include<stdio.h>
#include<string.h>
#include <iostream>
#include <bits/stdc++.h>
#define IO ios::sync_with_stdio(false);\
    cin.tie(0);    cout.tie(0);
#define MAX 0x3f3f3f3f
using namespace std;
int logo[1010];//用来标记0和1  表示这个点是否被选择过
int map1[1010][1010];//邻接矩阵用来存储图的信息
int dis[1010];//记录任意一点到这个点的最近距离
int n;//点个数
int prim()
{
    int i,j,now;
    int sum=0;
    /*初始化*/
    for(i=1; i<=n; i++)
    {
        dis[i]=MAX;
        logo[i]=0;
    }
    /*选定1为起始点,初始化*/
    for(i=1; i<=n; i++)
    {
        dis[i]=map1[1][i];
    }
    dis[1]=0;
    logo[1]=1;
    /*循环找最小边,循环n-1次*/
    for(i=1; i<n; i++)
    {
        now=MAX;
        int min1=MAX;
        for(j=1; j<=n; j++)
        {
            if(logo[j]==0&&dis[j]<min1)
            {
                now=j;
                min1=dis[j];
            }
        }
        if(now==MAX)
            break;//防止不成图
        logo[now]=1;
        sum+=min1;
        for(j=1; j<=n; j++)//添入新点后更新最小距离
        {
            if(logo[j]==0&&dis[j]>map1[now][j])
                dis[j]=map1[now][j];
        }
    }
    if(i<n)
        printf("?\n");
    else
        printf("%d\n",sum);
}
int main()
{
    while(scanf("%d",&n),n)//n是点数
    {
        int m=n*(n-1)/2;//m是边数
        memset(map1,0x3f3f3f3f,sizeof(map1));//map是邻接矩阵存储图的信息
        for(int i=0; i<m; i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            if(c<map1[a][b])//防止重边
                map1[a][b]=map1[b][a]=c;
        }
        prim();
    }
}

kruskal:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n, m,sum;
struct node
{
    int start,end,power;//start为起始点,end为终止点,power为权值
} edge[5050];
int pre[5050];

int cmp(node a, node b)
{
    return a.power<b.power;//按照权值排序
}

int find(int x)//并查集找祖先
{
    if(x!=pre[x])
    {
        pre[x]=find(pre[x]);
    }
    return pre[x];
}

void merge(int x,int y,int n)//并查集合并函数,n是用来记录最短路中应该加入哪个点
{
    int fx=find(x);
    int fy=find(y);
    if(fx!=fy)
    {
        pre[fx]=fy;
        sum+=edge[n].power;
    }
}
int main()
{
    while(~scanf("%d", &n), n)//n是点数
    {
        sum=0;
        m=n*(n-1)/2;//m是边数,可以输入
        int i;
        int start,end,power;
        for(i=1; i<=m; i++)
        {
            scanf("%d %d %d", &start, &end, &power);
            edge[i].start=start,edge[i].end=end,edge[i].power=power;
        }
        for(i=1; i<=m; i++)
        {
            pre[i]=i;
        }//并查集初始化
        sort(edge+1, edge+m+1,cmp);
        for(i=1; i <= m; i++)
        {
            merge(edge[i].start,edge[i].end,i);
        }
        printf("%d\n",sum);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/GHzz/p/9148279.html

时间: 2024-11-29 07:22:35

最小生成树算法详解(prim+kruskal)的相关文章

最小生成树详解 prim+ kruskal代码模板

最小生成树概念: 一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边. 最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出.最小生成树其实是最小权重生成树的简称. prim: 概念:普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点,且其所有边的权值之和亦为最小. 实现过程: 图例 说明 不可选 可选 已选(Vn

EM算法(3):EM算法详解

目录 EM算法(1):K-means 算法 EM算法(2):GMM训练算法 EM算法(3):EM算法详解

[转] KMP算法详解

转载自:http://www.matrix67.com/blog/archives/115 KMP算法详解 如果机房马上要关门了,或者你急着要和MM约会,请直接跳到第六个自然段.    我们这里说的KMP不是拿来放电影的(虽然我很喜欢这个软件),而是一种算法.KMP算法是拿来处理字符串匹配的.换句话说,给你两个字符串,你需要回答,B串是否是A串的子串(A串是否包含B串).比如,字符串A="I'm matrix67",字符串B="matrix",我们就说B是A的子串.

[搜索]波特词干(Porter Streamming)提取算法详解(3)

 接上 [搜索]波特词干(Porter Streamming)提取算法详解(2) 下面分为5大步骤来使用前面提到的替换条件来进行词干提取. 左边是规则,右边是提取成功或者失败的例子(用小写字母表示). 步骤1 SSES -> SS                   caresses  ->  caress IES  -> I                          ponies    ->  poni ties      ->  ti SS   -> S

KMP算法详解(图示+代码)

算法过程非常绕,不要企图一次就能看明白,多尝试就会明白一些.下面试图用比较直观的方法解释这个算法,对KMP算法的解释如下: 1. 首先,字符串"BBC ABCDAB ABCDABCDABDE"的第一个字符与搜索词"ABCDABD"的第一个字符,进行比较.因为B与A不匹配,所以搜索词后移一位. 2. 因为B与A不匹配,搜索词再往后移. 3. 就这样,直到字符串有一个字符,与搜索词的第一个字符相同为止. 4. 接着比较字符串和搜索词的下一个字符,还是相同. 5. 直到字

安全体系(三)——SHA1算法详解

本文主要讲述使用SHA1算法计算信息摘要的过程. 安全体系(零)—— 加解密算法.消息摘要.消息认证技术.数字签名与公钥证书 安全体系(一)—— DES算法详解 安全体系(二)——RSA算法详解 为保证传输信息的安全,除了对信息加密外,还需要对信息进行认证.认证的目的有两:一是验证信息的发送者是合法的,二是验证信息的完整性.Hash函数就是进行信息认证的一种有效手段. 1.Hash函数和消息完整性 Hash函数也称为杂凑函数或散列函数,函数输入为一可变长度x,输出为一固定长度串,该串被称为输入x

php 二分查找法算法详解

一.概念:二分查找又称折半查找,优点是比较次数少,查找速度快,平均性能好:其缺点是要求待查表为有序表,且插入删除困难.因此,折半查找方法适用于不经常变动而查找频繁的有序列表.首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功:否则利用中间位置记录将表分成前.后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表.重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功. 二.代

【转】AC算法详解

原文转自:http://blog.csdn.net/joylnwang/article/details/6793192 AC算法是Alfred V.Aho(<编译原理>(龙书)的作者),和Margaret J.Corasick于1974年提出(与KMP算法同年)的一个经典的多模式匹配算法,可以保证对于给定的长度为n的文本,和模式集合P{p1,p2,...pm},在O(n)时间复杂度内,找到文本中的所有目标模式,而与模式集合的规模m无关.正如KMP算法在单模式匹配方面的突出贡献一样,AC算法对于

支持向量机(SVM)(五)-- SMO算法详解

一.我们先回顾下SVM问题. A.线性可分问题 1.SVM基本原理: SVM使用一种非线性映射,把原训练            数据映射到较高的维.在新的维上,搜索最佳分离超平面,两个类的数据总可以被超平面分开. 2.问题的提出: 3.如何选取最优的划分直线f(x)呢? 4.求解:凸二次规划 建立拉格朗日函数: 求偏导数: B.线性不可分问题 1.核函数 如下图:横轴上端点a和b之间红色部分里的所有点定为正类,两边的黑色部分里的点定为负类. 设: g(x)转化为f(y)=<a,y> g(x)=