Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >最小生成树——Prim算法与Kruskal算法

最小生成树——Prim算法与Kruskal算法

作者头像
mindtechnist
发布于 2024-08-08 09:15:32
发布于 2024-08-08 09:15:32
14300
代码可运行
举报
文章被收录于专栏:机器和智能机器和智能
运行总次数:0
代码可运行

最小生成树概念:连通图: 在一个无向图中,任意两个顶点之间都是可达的(有路径连通),则成该无向图为连通图。生成树: 一个连通图的生成树是一个极小的连通子图,它含有图中的全部顶点,但只有构成一棵树的n-1条边。也就是说,无向图中连通n个顶点n-1条边就叫做生成树。最小生成树: 构造连通图的最小代价生成树称为最小生成树,也就是说,所有的边加权后和最小的树。

Prim算法

Prim算法计算最小生成树的方法从一个结点开始使树一点点的成长。在每一步,都增加一个结点作为根,并连接这个结点作为边,也就是说每次增加一个一个结点和一条边,这样也就把相关联的顶点加到增长中的树上了。这个过程主要体现在“加点”,在算法进行的过程中,有一个已经添加到树上的顶点集,这个顶点集实际就是最小生成树的结点集合,其余顶点都作为选择,等待是否被加入集合。每次选择一个顶点,使得它和上一个顶点之间的代价最小,并把这条边加入到最小生成树中,把顶点加入到集合中。下面通过图示来描述Prim算法的思想:首先选择一个顶点作为起始,比如A,第一轮发现AC代价最小,那么就把AC边加入最小生成树,把A加入顶点集合;

后面依次寻找最小代价边,直到全部顶点都加入到顶点集合。

在程序中,通过一个最小代价标记,并一行一行的扫描来搜索最小代价边,下面来看具体代码。首先我们定义一个图的数据类型,该数据类型包含图的顶点集合、邻接矩阵,顶点数和边数。另外宏定义两个常量,通过一个不可能的数比如65535来表示两个顶点之间没有边。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define MAXVER      6       /*最大顶点个数*/
#define INFINITY    65535   /*代表无穷大*/

typedef struct _graph_type
{
    char    vertex[MAXVER]; /*存放顶点的数组*/
    int     arc[MAXVER][MAXVER]; /*邻接矩阵*/ 
    int     vertex_num; /*顶点数*/
    int     edge_num; /*边数*/
}graph_type;

Prim算法C语言实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*普利姆Prim算法求最小生成树*/
void mini_span_tree_prim(graph_type g)
{
    int min = 0; /*保存最小权值*/
    int k = 0; /*存放最小权值的顶点下标*/
    int i = 0;
    int j = 0;
    int vertex_tree[MAXVER]; /*顶点下标 - 最小生成树的结点*/
    int weight[MAXVER]; /*边的权值,置为0表示下标对应的顶点已加入最小生成树*/
    
    weight[0] = 0; /*假设图标号为0的顶点是最小生成树的第一个结点*/
    vertex_tree[0] = 0; /*加入第一个顶点*/
    
    for(i = 1; i < g.vertex_num; i++) /*遍历图的所有顶点*/
    {
        weight[i] = g.arc[0][i]; /*把和0号顶点有边的边的权值存入数组*/
        vertex_tree[i] = 0; /*初始化为0号顶点*/
    }
    
    for(i = 1; i < g.vertex_num; i++)
    {
        min = INFINITY; /*初始化最小权值为无穷大*/
        j = 1;
        k = 0;
        while(j < g.vertex_num) /*遍历所有顶点*/
        {
            if(weight[j] != 0 && weight[j] < min)
            {
                /*如果权值不为0(未加入最小生成树),且权值小于最小权值min*/
                min = weight[j]; /*更新当前最小权值*/
                k = j; /*保存最小权值边所以来的顶点,第一次循环表示 (0, k)为0开始的所有边中权值最小的边*/
            }
            j++;
        }
        weight[k] = 0; /*将k的权值置为0,表示这个结点的最小权值已经找到了,同时顶点k已被加入最小生成树中*/
        for(j = 1; j < vertex_num; j++)
        {
            if(weight[j] != 0 && g.arc[k][j] < weight[j])
            {
                /*如果j没有加入最小生成树,且邻接矩阵第k行相应权值小于weigh记录的最小权值*/
                weight[j] = g.arc[k][j]; /*更新weight*/
                vertex_tree[j] = k; /*把k加入到最小生成树*/
            }
        }
    }
}

Kruskal算法

Prim算法是以某个顶点开始,逐步寻找各个顶点上最小权值的边,这样一步步来构建最小生成树。第二种贪心策略是连续地按照最小的权选择边,并且当所选的边不产生回路时就把它作为取定的边。在形式上Kruskal算法是在处理一个森林,开始的时候,存在n棵单结点的树,每次添加一条边把两棵树合并成一棵树,当算法终止时剩下的一棵树就是最小生成树。假设图和上面一样

首先我们得到一张表,每条边按权值从小到大排序

然后开始加边,优先选择权值小的边

加最后一条边,得到最小生成树,和Prim算法得到的一样

Kruskal算法C语言实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define MAXedge_type 20 /*最大边数*/
#define MAXVEX 20  /*最大顶点数*/
#define INFINITY 65535 /*无穷大*/

typedef struct
{
    int arc[MAXVEX][MAXVEX];
    int vertex_num;
    int edge_type_num;
}graph_type;

typedef struct
{
    int begin;
    int end;
    int weight;
}edge_type;   /* 对边集数组edge_type结构的定义 */

/* 交换权值 以及头和尾 */
void Swapn(edge_type *edge_types,int i, int j)
{
    int temp;
    temp = edge_types[i].begin;
    edge_types[i].begin = edge_types[j].begin;
    edge_types[j].begin = temp;
    temp = edge_types[i].end;
    edge_types[i].end = edge_types[j].end;
    edge_types[j].end = temp;
    temp = edge_types[i].weight;
    edge_types[i].weight = edge_types[j].weight;
    edge_types[j].weight = temp;
}

/* 对权值进行排序 */
void sort(edge_type edge_types[], graph_type *G)
{
    int i, j;
    for ( i = 0; i < G->edge_type_num; i++)
    {
        for ( j = i + 1; j < G->edge_type_num; j++)
        {
            if (edge_types[i].weight > edge_types[j].weight)
            {
                Swapn(edge_types, i, j);
            }
        }
    }
    printf("权排序之后的为:\n");
    for (i = 0; i < G->edge_type_num; i++)
    {
        printf("(%d, %d) %d\n", edge_types[i].begin, edge_types[i].end, edge_types[i].weight);
    }

}

/* 查找连线顶点的尾部下标 */
int Find(int *parent, int f)
{
    while ( parent[f] > 0)
    {
        f = parent[f];
    }
    return f;
}

/* 生成最小生成树 */
void MiniSpanTree_Kruskal(graph_type G)
{
    int i, j, n, m;
    int k = 0;
    int parent[MAXVEX];/* 定义一数组用来判断边与边是否形成环路 */
    
    edge_type edge_types[MAXedge_type];/* 定义边集数组,edge_type的结构为begin,end,weight,均为整型 */

    /* 用来构建边集数组并排序 */
    for ( i = 0; i < G.vertex_num-1; i++)
    {
        for (j = i + 1; j < G.vertex_num; j++)
        {
            if (G.arc[i][j]<INFINITY)
            {
                edge_types[k].begin = i;
                edge_types[k].end = j;
                edge_types[k].weight = G.arc[i][j];
                k++;
            }
        }
    }
    sort(edge_types, &G);

    for (i = 0; i < G.vertex_num; i++)
        parent[i] = 0;  /* 初始化数组值为0 */

    for (i = 0; i < G.edge_type_num; i++)   /* 遍历每一条边 */
    {
        n = Find(parent,edge_types[i].begin);
        m = Find(parent,edge_types[i].end);
        if (n != m) /* 假如n与m不等,说明此边没有与现有的生成树形成环路 */
        {
            parent[n] = m;  /* 将此边的结尾顶点放入下标为起点的parent中。 */
                            /* 表示此顶点已经在生成树集合中 */
            printf("(%d, %d) %d\n", edge_types[i].begin, edge_types[i].end, edge_types[i].weight);
        }
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-04-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 机器和智能 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
数据结构 最小生成树之Kruskal算法
首先构造一个只含n个顶点的森林,然后依权值从小到大从连通网中选择边加入到森林中,并使森林不产生回路,直至森林变成过一棵树为止
Twcat_tree
2022/11/29
5310
数据结构 最小生成树之Kruskal算法
生成树和最小生成树prim,kruskal
普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小。该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克(英语:Vojtěch Jarník)发现;并在1957年由美国计算机科学家罗伯特·普里姆(英语:Robert C. Prim)独立发现;1959年,艾兹格·迪科斯彻再次发现了该算法。因此,在某些场合,普里姆算法又被称为DJP算法、亚尔尼克算法或普里姆-亚尔尼克算法。 中文名 普里姆算法 外文名 Prim Algorithm 别 称 最小生成树算法 提出者 沃伊捷赫·亚尔尼克(Vojtěch Jarník) 提出时间 1930年 应用学科 计算机,数据结构,数学(图论) 适用领域范围 应用图论知识的实际问题 算 法 贪心 目录 1 算法描述 2 时间复杂度 3 图例描述 4 代码 ▪ PASCAL代码 ▪ c代码 ▪ C++代码 5 时间复杂度 算法描述编辑 1).输入:一个加权连通图,其中顶点集合为V,边集合为E; 2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空; 3).重复下列操作,直到Vnew = V: a.在集合E中选取权值最小的边,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一); b.将v加入集合Vnew中,将边加入集合Enew中; 4).输出:使用集合Vnew和Enew来描述所得到的最小生成树。
废江_小江
2022/09/05
9690
最小生成树-Prim算法和Kruskal算法
Prim算法 1.概览 普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小。该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克(英语:Vojtěch Jarník)发现;并在1957年由美国计算机科学家罗伯特·普里姆(英语:Robert C. Prim)独立发现;1959年,艾兹格·迪科斯彻再次发现了该算法。因此,在某些场合,普里姆算
用户1215536
2018/02/05
3.8K0
最小生成树-Prim算法和Kruskal算法
数据结构–最小生成树详解[通俗易懂]
前言 A wise man changes his mind,a fool never. Name:Willam Time:2017/3/1
全栈程序员站长
2022/11/17
1.1K0
数据结构–最小生成树详解[通俗易懂]
最小生成树算法(上)——Prim(普里姆)算法
最小生成树:一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。根据定义可知对于一个有V个顶点的图来说,其最小生成树定包含V个顶点与V-1条边。反过来如果一个图的最小生成树存在,那么图一定是连通图。 对于最小生成树算法最著名的有两种:Prim算法与Kruskal算法。
AI那点小事
2020/04/20
9230
最小生成树算法:Kruskal 与 Prim算法
连通图中的每一棵生成树,都是原图的一个极大无环子图,即:从其中删去任何一条边,生成树就不再连通;反之,在其中引入任何一条新边,都会形成一条回路。
利刃大大
2023/04/12
2.1K0
最小生成树算法:Kruskal 与 Prim算法
最小生成树的两种方法(Kruskal算法和Prim算法)
此算法可以称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。
233333
2020/01/09
2.1K0
最小生成树之Prim算法和Kruskal算法
一个连通图可能有多棵生成树,而最小生成树是一副连通加权无向图中一颗权值最小的生成树,它可以根据Prim算法和Kruskal算法得出,这两个算法分别从点和边的角度来解决。
业余草
2019/01/21
1.9K0
最小生成树之Prim算法和Kruskal算法
图详解第三篇:最小生成树(Kruskal算法+Prim算法)
连通图中的每一棵生成树,都是原图的一个极大无环子图,即:从其中删去任何一条边,生成树就不在连通;反之,在其中引入任何一条新边,都会形成一条回路。
YIN_尹
2024/01/23
3.5K0
图详解第三篇:最小生成树(Kruskal算法+Prim算法)
复杂网络(2)--图论的基本理论-最小生成树问题
该文章是一篇技术文章,主要介绍了如何通过编辑距离算法实现文本相似度的计算。文章首先介绍了编辑距离算法的原理,然后详细讲解了基于编辑距离的文本相似度计算方法,并给出了具体的实现代码。最后,文章还探讨了编辑距离算法在技术社区中的应用,包括相似度计算和相似问答系统。
锦小年
2018/01/02
1.6K0
复杂网络(2)--图论的基本理论-最小生成树问题
数据结构基础温故-5.图(中):最小生成树算法
图的“多对多”特性使得图在结构设计和算法实现上较为困难,这时就需要根据具体应用将图转换为不同的树来简化问题的求解。
Edison Zhou
2018/08/20
1.2K0
数据结构基础温故-5.图(中):最小生成树算法
加权无向图----Prim算法实现最小生成树
上一篇:加权无向图的实现 加权无向图----Kruskal算法实现最小生成树 图的生成树是它的一棵含有其所有顶点的无环连通子图,加权图的最小生成树(MST)是它的一棵权值最小的生成树。 切分:图的一种切分是将图的所有顶点分为两个非空且不重合的两个集合。横切边是一条连接两个属于不同集合的顶点的边。 切分定理:在一幅加权图中,给定任意的切分,它横切边中权重最小者必然属于图的最小生成树。 切分定理是解决最小生成树问题的所有算法的基础。  Prim算法能够得到任意加权连通无向图的最小生成树。 数据结构设计: 采用一
SuperHeroes
2018/05/30
1.6K0
图Graph--最小生成树
练习题: LeetCode 1135. 最低成本联通所有城市(最小生成树+排序+并查集) LeetCode 1489. 找到最小生成树里的关键边和伪关键边(并查集+kruskal最小生成树)
Michael阿明
2021/02/20
5070
图Graph--最小生成树
算法:图解最小生成树之普里姆(Prim)算法
我们在图的定义中说过,带有权值的图就是网结构。一个连通图的生成树是一个极小的连通子图,它含有图中全部的顶点,但只有足以构成一棵树的n-1条边。所谓的最小成本,就是n个顶点,用n-1条边把一个连通图连接起来,并且使得权值的和最小。综合以上两个概念,我们可以得出:构造连通网的最小代价生成树,即最小生成树(Minimum Cost Spanning Tree)。 找连通图的最小生成树,经典的有两种算法,普里姆算法和克鲁斯卡尔算法,这里介绍普里姆算法。 为了能够讲明白这个算法,我们先构造网图的邻接矩阵,如图7-6
s1mba
2018/01/12
2.6K0
算法:图解最小生成树之普里姆(Prim)算法
最小生成树(Prim算法和Kruskal算法算法详解)
通俗易懂的讲就是最小生成树包含原图的所有节点而只用最少的边和最小的权值距离。因为n个节点最少需要n-1个边联通,而距离就需要采取某种策略选择恰当的边。
bigsai
2019/10/21
3.9K0
最小生成树(Prim算法和Kruskal算法算法详解)
最小生成树(Kruskal算法和Prim算法)
上一篇文章,我们讲了图的创建和遍历,其中遍历的算法主要有BFS(广度优先算法)和DFS(深度优先算法)两种,并且DFS算法对很多问题都有很好的启示!而今天我们要说一个非常实用的算法——最小生成树的建立!这是图论中一个经典问题,可以使用Kruskal和Prim两种算法来进行实现!
算法工程师之路
2019/08/05
5.9K0
数据结构01-最小生成树-Prim算法
给定一个带权的无向连通图,能够连通该图的全部顶点且不产生回路的子图即为该图的生成树;
yangjiao
2021/03/04
5680
最小生成树----prim算法----普利姆算法
生成树的概念 最小生成树的定义 生成树的代价和最小生成树 MST性质 普利姆(prim)算法 图解: 使用哪一种结构进行存储? 数据结构设计 伪代码 实例 #include<iostream>
大忽悠爱学习
2021/03/23
2.3K0
最小生成树----prim算法----普利姆算法
数据结构 最小生成树之Prim算法
求无向网的最小生成树的算法有两种:Prim和Kruskal,它们都是利用最小生成树的MST性质得到的。
Twcat_tree
2022/11/29
4990
数据结构 最小生成树之Prim算法
为实习准备的数据结构(11)-- 图论算法 集锦
又要画图了。一到这里就莫名其妙的烦,之前写过的图相关博客已经让我都删了,讲的语无伦次。 希望这篇能写好点。
看、未来
2021/09/18
5930
推荐阅读
相关推荐
数据结构 最小生成树之Kruskal算法
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验