用Prim算法构造最小生成树

合集下载

PRIM算法求最小生成树

PRIM算法求最小生成树

xx学院《数据结构与算法》课程设计报告书课程设计题目 PRIM算法求最小生成树院系名称计算机科学与技术系专业(班级)姓名(学号)指导教师完成时间一、问题分析和任务定义在该部分中主要包括两个方面:问题分析和任务定义;1 问题分析本次课程设计是通过PRIM(普里姆)算法,实现通过任意给定网和起点,将该网所对应的所有生成树求解出来。

在实现该本设计功能之前,必须弄清以下三个问题:1.1 关于图、网的一些基本概念1.1.1 图图G由两个集合V和E组成,记为G=(V,E),其中V是顶点的有穷非空集合,E是V中顶点偶对的有穷集,这些顶点偶对称为边。

通常,V(G)和E(G)分别表示图G的顶点集合和边集合。

E(G)也可以为空集。

则图G只有顶点而没有边。

1.1.2 无向图对于一个图G,若边集E(G)为无向边的集合,则称该图为无向图。

1.1.3 子图设有两个图G=(V,E)G’=(V’,),若V’是V的子集,即V’⊆V ,且E’是E的子集,即E’⊆E,称G’是G的子图。

1.1.4 连通图若图G中任意两个顶点都连通,则称G为连通图。

1.1.5 权和网在一个图中,每条边可以标上具有某种含义的数值,该数值称为该边的权。

把边上带权的图称为网。

如图1所示。

1.2 理解生成树和最小生成树之间的区别和联系1.2.1 生成树在一个连通图G中,如果取它的全部顶点和一部分边构成一个子图G’,即:V(G’)= V(G)和E(G’)⊆E(G),若边集E(G’)中的边既将图中的所有顶点连通又不形成回路,则称子图G’是原图G的一棵生成树。

1.2.2 最小生成树图的生成树不是唯一的,把具有权最小的生成树称为图G的最小生成树,即生成树中每条边上的权值之和达到最小。

如图1所示。

图1.网转化为最小生成树1.3 理解PRIM(普里姆)算法的基本思想1.3.1 PRIM算法(普里姆算法)的基本思想假设G =(V,E)是一个具有n个顶点的连通网,T=(U,TE)是G的最小生成树,其中U是T的顶点集,TE是T的边集,U和TE的初值均为空集。

最小生成树---普里姆算法(Prim算法)和克鲁斯卡尔算法(Kruskal算法)

最小生成树---普里姆算法(Prim算法)和克鲁斯卡尔算法(Kruskal算法)

最⼩⽣成树---普⾥姆算法(Prim算法)和克鲁斯卡尔算法(Kruskal算法)最⼩⽣成树的性质:MST性质(假设N=(V,{E})是⼀个连通⽹,U是顶点集V的⼀个⾮空⼦集,如果(u,v)是⼀条具有最⼩权值的边,其中u属于U,v属于V-U,则必定存在⼀颗包含边(u,v)的最⼩⽣成树)普⾥姆算法(Prim算法)思路:以点为⽬标构建最⼩⽣成树1.将初始点顶点u加⼊U中,初始化集合V-U中各顶点到初始顶点u的权值;2.根据最⼩⽣成树的定义:从n个顶点中,找出 n - 1条连线,使得各边权值最⼩。

循环n-1次如下操作:(1)从数组lowcost[k]中找到vk到集合U的最⼩权值边,并从数组arjvex[k] = j中找到该边在集合U中的顶点下标(2)打印此边,并将vk加⼊U中。

(3)通过查找邻接矩阵Vk⾏的各个权值,即vk点到V-U中各顶点的权值,与lowcost的对应值进⾏⽐较,若更⼩则更新lowcost,并将k存⼊arjvex数组中以下图为例#include<bits/stdc++.h>using namespace std;#define MAXVEX 100#define INF 65535typedef char VertexType;typedef int EdgeType;typedef struct {VertexType vexs[MAXVEX];EdgeType arc[MAXVEX][MAXVEX];int numVertexes, numEdges;}MGraph;void CreateMGraph(MGraph *G) {int m, n, w; //vm-vn的权重wscanf("%d %d", &G->numVertexes, &G->numEdges);for(int i = 0; i < G->numVertexes; i++) {getchar();scanf("%c", &G->vexs[i]);}for(int i = 0; i < G->numVertexes; i++) {for(int j = 0; j < G->numVertexes; j++) {if(i == j) G->arc[i][j] = 0;else G->arc[i][j] = INF;}}for(int k = 0; k < G->numEdges; k++) {scanf("%d %d %d", &m, &n, &w);G->arc[m][n] = w;G->arc[n][m] = G->arc[m][n];}}void MiniSpanTree_Prim(MGraph G) {int min, j, k;int arjvex[MAXVEX]; //最⼩边在 U集合中的那个顶点的下标int lowcost[MAXVEX]; // 最⼩边上的权值//初始化,从点 V0开始找最⼩⽣成树Tarjvex[0] = 0; //arjvex[i] = j表⽰ V-U中集合中的 Vi点的最⼩边在U集合中的点为 Vjlowcost[0] = 0; //lowcost[i] = 0表⽰将点Vi纳⼊集合 U ,lowcost[i] = w表⽰ V-U中 Vi点到 U的最⼩权值for(int i = 1; i < G.numVertexes; i++) {lowcost[i] = G.arc[0][i];arjvex[i] = 0;}//根据最⼩⽣成树的定义:从n个顶点中,找出 n - 1条连线,使得各边权值最⼩for(int i = 1; i < G.numVertexes; i++) {min = INF, j = 1, k = 0;//寻找 V-U到 U的最⼩权值minfor(j; j < G.numVertexes; j++) {// lowcost[j] != 0保证顶点在 V-U中,⽤k记录此时的最⼩权值边在 V-U中顶点的下标if(lowcost[j] != 0 && lowcost[j] < min) {min = lowcost[j];k = j;}}}printf("V[%d]-V[%d] weight = %d\n", arjvex[k], k, min);lowcost[k] = 0; //表⽰将Vk纳⼊ U//查找邻接矩阵Vk⾏的各个权值,与lowcost的对应值进⾏⽐较,若更⼩则更新lowcost,并将k存⼊arjvex数组中for(int i = 1; i < G.numVertexes; i++) {if(lowcost[i] != 0 && G.arc[k][i] < lowcost[i]) {lowcost[i] = G.arc[k][i];arjvex[i] = k;}}}int main() {MGraph *G = (MGraph *)malloc(sizeof(MGraph));CreateMGraph(G);MiniSpanTree_Prim(*G);}/*input:4 5abcd0 1 20 2 20 3 71 2 42 3 8output:V[0]-V[1] weight = 2V[0]-V[2] weight = 2V[0]-V[3] weight = 7最⼩总权值: 11*/时间复杂度O(n^2)克鲁斯卡尔算法(Kruskal算法)思路:以边为⽬标进⾏构建最⼩⽣成树在边集中依次寻找最⼩权值边,若构建是不形成环路(利⽤parent数组记录各点的连通分量),则将其添加到最⼩⽣成树中。

最小生成树问题例题

最小生成树问题例题

最小生成树问题例题最小生成树(Minimum Spanning Tree)是图论中的一个经典问题,它是指在一个带权无向图中找到一棵生成树,使得树上所有边的权值之和最小。

最小生成树问题在实际生活中有着广泛的应用,比如电力输送、通信网络等领域。

下面我们以一个具体的例子来说明最小生成树问题的求解过程。

假设有一个无向图,图中包含了6个节点(A、B、C、D、E、F)和9条边。

每条边都有一个权值,表示连接两个节点的成本。

我们的目标是找到一棵最小生成树。

首先,我们可以使用 Prim 算法来求解最小生成树。

Prim 算法的基本思想是从一个起始节点开始,逐步扩展生成树,直到包含所有节点为止。

具体步骤如下:1. 选择一个起始节点,将其标记为已访问。

2. 从已访问的节点中,选择一条连接到未访问节点的最短边。

3. 将这条边加入到最小生成树中,并将连接的节点标记为已访问。

4. 重复步骤2和步骤3,直到所有节点都被访问过。

根据上述算法,我们可以依次选取边 AB、CD、BC、EF、DE 来构建最小生成树。

最终的最小生成树是:A-B、C-D、B-C、E-F 和 D-E 这五条边,它们的权值之和为12。

另外一个常用的求解最小生成树问题的算法是 Kruskal 算法。

Kruskal 算法的基本思想是将图中的边按照权值从小到大进行排序,然后依次选取边,如果这条边连接的两个节点不在同一个连通分量中,就将这条边加入到最小生成树中。

具体步骤如下:1. 对图中的边按照权值进行排序。

2. 从权值最小的边开始,依次选取边。

3. 如果选取的边连接的两个节点不在同一个连通分量中,就将这条边加入到最小生成树中,并将连接的节点合并为一个连通分量。

4. 重复步骤2和步骤3,直到最小生成树中包含了所有的节点。

使用 Kruskal 算法求解上述例子,我们可以依次选取边 AB、BC、CD、DE、EF 来构建最小生成树。

最终的最小生成树是:A-B、B-C、C-D、D-E 和 E-F 这五条边,它们的权值之和也是12。

普里姆算法

普里姆算法
最小生成树 ( minimum cost spanning tree )
使用不同的遍历图的方法,可以得到不同 的生成树;从不同的顶点出发,也可能得 到不同的生成树。
按照生成树的定义,n 个顶点的连通网络 的生成树有 n 个顶点、n-1 条边。
构造最小生成树的准则 必须使用且仅使用该网络中的n-1 条边 来联结网络中的 n 个顶点; 不能使用产生回路的边; 各边上的权值的总和达到最小。
采用邻接矩阵作为图的存储表示。
28
01
01
01
10 14 16 10
10
5 6 25 6 25 6 2
25 24 18 12
25
4 22 3
43
43
原图
(a)
(b)
01
01
01
10
10
10 14 16
5 6 25 6 25 6 2
25
4 22 3
(c)
25
12 25
12
4 22 3
4 22 3
(d)
普里姆(Pபைடு நூலகம்im)算法
普里姆算法的基本思想: 从连通网络 N = { V, E }中的某一顶点 u0 出 发, 选择与它关联的具有最小权值的边 ( u0, v ), 将其顶点加入到生成树顶点集合U中。 以后每一步从一个顶点在 U 中,而另一个 顶点不在 U 中的各条边中选择权值最小的 边(u, v), 把它的顶点加入到集合 U 中。如 此继续下去, 直到网络中的所有顶点都加入 到生成树顶点集合 U 中为止。
(e) (f)

模型建立与算法构造:基于Prim算法构造最小生成树

模型建立与算法构造:基于Prim算法构造最小生成树

模型建立与算法构造:基于Prim算法构造最小生成树作者:李琳赵娟虎建勋来源:《科教导刊·电子版》2019年第12期摘要最小生成树有许多重要的应用。

例如:要在n个城市之间铺设光缆,主要目标是要使这 n 个城市的任意两个之间都可以通信,但铺设光缆的费用很高,且各个城市之间铺设光缆的费用不同,因此另一个目标是要使铺设光缆的总费用最低。

这就需要找到带权的最小生成树。

本文通过使用Matlab软件进行编程实现了用Prim算法构造最小生成树的分析运算。

本程序可以方便地处理层次分析法下较大的运算量,解决层次分析法的效率问题,提高计算机辅助决策的时效性。

其中建立了有效的基于Prim算法的数学模型并验证了模型的合理性和科学准确性。

利用MATLAB、EXCEL、SPSS13.0 for windows等软件,实现了高效、准确的研究。

关键词模型 atlab软件算法构造中图分类号:TP312 文献标识码:A1建立结构模型1.1最小生成树相关概念连通图:任意两个结点之间都有一个路径相连。

生成树(Spannirng Tree):连通图的一个极小的连通子图,它含有图中全部n个顶点,但只有足以构成一棵树的n-1条边。

最小生成树(Minimum Spannirng Tree):连通图的最小代价的生成树(各边的权值之和最小)。

最小生成树即对于连通的带权图(连通网)G,其生成树也是带权的。

生成树T各边的权值总和称为该树的权,记作:这里:TE表示T的边集,w(u,v)表示边(u,v)的权。

权最小的生成樹称为G的最小生成树(Minimum SpannirngTree)。

最小生成树可简记为MST。

1.2 Prim算法实现Prim算法是一种贪心算法,将全部的顶点划分为2个集合,每次总在2个集合之间中找最小的一条边,局部最优最终达到全局最优,这正是贪心的思想。

在无向加权图中,n个顶点的最小生成树有n-1条边,这些边使得n个顶点之间可达,且总的代价最小。

求无向图的最小生成树算法——Prim与Kruskal

求无向图的最小生成树算法——Prim与Kruskal
一.Prim算法
1.算法思想
对于图G=(V,E),用Prim算法求最小生成树T=(S,TE)的流程如下
① 初始化:设S、TE为空集,任选节点K加入S。
② 选取一条权值最小的边(X,Y),其中X∈S,且not (Y∈S) 即,选取一条权值最小的、连接着S中一点与S外一点的边。
以上操作重复|V|-1次结束。由于每次加入S的点i都在当时取到了符合流程②的边min{lowcost},而lowcost[i]=w(i,closest[i]),所以此时的最小生成树的各边就是(i,closest[i]),i∈V且not (i=x)【需要注意的是出发点x的closest[x]还是x,所以应忽略,实际取到x-1条边】。把i从1取到|V|,便得到最小生成树T的每条边。
为了比较快速地选边,我们用两个数组lowcost、closest动态地维护每一个点到S的最短距离。在某一状态下,lowcost[i]表示所有与i相连且另一端点在S中的边中的权值最小值,closest[i]表示在S中且与i相连的点中与i之间距离最小的点。显然,lowcost[i]=w(i,closest[i])。需要注意的是两个数组记录的都是边而不是路径。若i没有边直接连向S,则lowcost[i]=∞。另外,若i已在S中,则lowcost[i]=0。
lowcost[j] = w[k][j];
closest[j] = k;
} //由新加入S中的k点使某些点到S的距离缩短,所以更新各点的lowcost和close= 1; i <= n; i++)
if(i != closest[i]){
设出发点为x。初始时对于任意k∈V,closest[k]=x,lowcost[k]=w(k,x)【w(i,j)表示i、j间的距离。初始化时,若两点间没有边则w(i,j)赋为一个足够大的整数(如maxint),并且所有点到自身的距离赋为0,即w(i,i)=0】

最小生成树(普里姆算法)

最小生成树(普里姆算法)

最⼩⽣成树(普⾥姆算法):所谓⽣成树,就是n个点之间连成n-1条边的图形。

⽽最⼩⽣成树,就是权值(两点间直线的值)之和的最⼩值。

⾸先,要⽤⼆维数组记录点和权值。

如上图所⽰⽆向图:int map[7][7];map[1][2]=map[2][1]=4;map[1][3]=map[3][1]=2;......然后再求最⼩⽣成树。

具体⽅法是:1.先选取⼀个点作起始点,然后选择它邻近的权值最⼩的点(如果有多个与其相连的相同最⼩权值的点,随便选取⼀个)。

如1作为起点。

visited[1]=1;pos=1;//⽤low[]数组不断刷新最⼩权值,low[i](0<i<=点数)的值为:i点到邻近点(未被标记)的最⼩距离。

low[1]=0; //起始点i到邻近点的最⼩距离为0low[2]=map[pos][2]=4;low[3]=map[pos][3]=2;low[4]==map[pos][4]=3;low[5]=map[pos][5]=MaxInt; //⽆法直达low[6]=map[pos][6]=MaxInt;2.再在伸延的点找与它邻近的两者权值最⼩的点。

//low[]以3作当前位置进⾏更新visited[3]=1;pos=3;low[1]=0; //已标记,不更新low[2]=map[1][2]=4; //⽐5⼩,不更新low[3]=2; //已标记,不更新low[4]=map[1][4]=3; //⽐1⼤,更新后为:low[4]=map[3][4]=1;low[5]=map[1][5]=MaxInt;//⽆法直达,不更新low[6]=map[1][6]=MaxInt;//⽐2⼤,更新后为:low[6]=map[3][6]=2;3.如此类推...当所有点都连同后,结果最⽣成树如上图所⽰。

所有权值相加就是最⼩⽣成树,其值为2+1+2+4+3=12。

⾄于具体代码如何实现,现在结合POJ1258例题解释。

最小生成树算法详解

最小生成树算法详解

最小生成树算法详解最小生成树(Minimum Spanning Tree,简称MST)是图论中的一个经典问题,它是指在一个加权连通图中找出一棵包含所有顶点且边权值之和最小的树。

在解决实际问题中,最小生成树算法被广泛应用于网络规划、电力传输、城市道路建设等领域。

本文将详细介绍最小生成树算法的原理及常见的两种算法:Prim算法和Kruskal算法。

一、最小生成树算法原理最小生成树算法的核心思想是贪心算法。

其基本原理是从图的某个顶点开始,逐步选取当前顶点对应的边中权值最小的边,并确保选取的边不会构成环,直到所有顶点都被连接为止。

具体实现最小生成树算法的方法有多种,两种常见的算法是Prim 算法和Kruskal算法。

二、Prim算法Prim算法是一种基于顶点的贪心算法。

它从任意一个顶点开始,逐渐扩展生成树的规模,直到生成整个最小生成树。

算法的具体步骤如下:1. 初始化一个空的生成树集合和一个空的顶点集合,将任意一个顶点加入到顶点集合中。

2. 从顶点集合中选择一个顶点,将其加入到生成树集合中。

3. 以生成树集合中的顶点为起点,寻找与之相邻的顶点中权值最小的边,并将该边与对应的顶点加入到最小生成树中。

4. 重复第3步,直到生成树中包含所有顶点。

Prim算法是一种典型的贪心算法,其时间复杂度为O(V^2),其中V为顶点数。

三、Kruskal算法Kruskal算法是一种基于边的贪心算法。

它首先将所有边按照权值从小到大进行排序,然后从小到大依次选择边,判断选取的边是否与已选取的边构成环,若不构成环,则将该边加入到最小生成树中。

算法的具体步骤如下:1. 初始化一个空的生成树集合。

2. 将图中的所有边按照权值进行排序。

3. 依次选择权值最小的边,判断其两个顶点是否属于同一个连通分量,若不属于,则将该边加入到最小生成树中。

4. 重复第3步,直到最小生成树中包含所有顶点。

Kruskal算法通过并查集来判断两个顶点是否属于同一个连通分量,从而避免形成环。

数据结构之最小生成树Prim算法

数据结构之最小生成树Prim算法

数据结构之最⼩⽣成树Prim算法普⾥姆算法介绍 普⾥姆(Prim)算法,是⽤来求加权连通图的最⼩⽣成树算法 基本思想:对于图G⽽⾔,V是所有顶点的集合;现在,设置两个新的集合U和T,其中U⽤于存放G的最⼩⽣成树中的顶点,T存放G的最⼩⽣成树中的边。

从所有uЄU,vЄ(V-U) (V-U表⽰出去U的所有顶点)的边中选取权值最⼩的边(u, v),将顶点v加⼊集合U中,将边(u, v)加⼊集合T中,如此不断重复,直到U=V为⽌,最⼩⽣成树构造完毕,这时集合T中包含了最⼩⽣成树中的所有边。

代码实现1. 思想逻辑 (1)以⽆向图的某个顶点(A)出发,计算所有点到该点的权重值,若⽆连接取最⼤权重值#define INF (~(0x1<<31)) (2)找到与该顶点最⼩权重值的顶点(B),再以B为顶点计算所有点到改点的权重值,依次更新之前的权重值,注意权重值为0或⼩于当前权重值的不更新,因为1是⼀当找到最⼩权重值的顶点时,将权重值设为了0,2是会出现⽆连接的情况。

(3)将上述过程⼀次循环,并得到最⼩⽣成树。

2. Prim算法// Prim最⼩⽣成树void Prim(int nStart){int i = 0;int nIndex=0; // prim最⼩树的索引,即prims数组的索引char cPrims[MAX]; // prim最⼩树的结果数组int weights[MAX]; // 顶点间边的权值cPrims[nIndex++] = m_mVexs[nStart].data;// 初始化"顶点的权值数组",// 将每个顶点的权值初始化为"第start个顶点"到"该顶点"的权值。

for (i = 0; i < m_nVexNum; i++){weights[i] = GetWeight(nStart, i);}for (i = 0; i < m_nVexNum; i ++){if (nStart == i){continue;}int min = INF;int nMinWeightIndex = 0;for (int k = 0; k < m_nVexNum; k ++){if (weights[k]!= 0 && weights[k] < min){min = weights[k];nMinWeightIndex = k;}}// 找到下⼀个最⼩权重值索引cPrims[nIndex++] = m_mVexs[nMinWeightIndex].data;// 以找到的顶点更新其他点到该点的权重值weights[nMinWeightIndex]=0;int nNewWeight = 0;for (int ii = 0; ii < m_nVexNum; ii++){nNewWeight = GetWeight(nMinWeightIndex, ii);// 该位置需要特别注意if (0 != weights[ii] && weights[ii] > nNewWeight){weights[ii] = nNewWeight;}}for (i = 1; i < nIndex; i ++){int min = INF;int nVexsIndex = GetVIndex(cPrims[i]);for (int kk = 0; kk < i; kk ++){int nNextVexsIndex = GetVIndex(cPrims[kk]);int nWeight = GetWeight(nVexsIndex, nNextVexsIndex);if (nWeight < min){min = nWeight;}}nSum += min;}// 打印最⼩⽣成树cout << "PRIM(" << m_mVexs[nStart].data <<")=" << nSum << ": ";for (i = 0; i < nIndex; i++)cout << cPrims[i] << "";cout << endl;}3. 全部实现#include "stdio.h"#include <iostream>using namespace std;#define MAX 100#define INF (~(0x1<<31)) // 最⼤值(即0X7FFFFFFF)class EData{public:EData(char start, char end, int weight) : nStart(start), nEnd(end), nWeight(weight){} char nStart;char nEnd;int nWeight;};// 边struct ENode{int nVindex; // 该边所指的顶点的位置int nWeight; // 边的权重ENode *pNext; // 指向下⼀个边的指针};struct VNode{char data; // 顶点信息ENode *pFirstEdge; // 指向第⼀条依附该顶点的边};// ⽆向邻接表class listUDG{public:listUDG(){};listUDG(char *vexs, int vlen, EData **pEData, int elen){m_nVexNum = vlen;m_nEdgNum = elen;// 初始化"邻接表"的顶点for (int i = 0; i < vlen; i ++){m_mVexs[i].data = vexs[i];m_mVexs[i].pFirstEdge = NULL;}char c1,c2;int p1,p2;ENode *node1, *node2;// 初始化"邻接表"的边for (int j = 0; j < elen; j ++){// 读取边的起始顶点和结束顶点p1 = GetVIndex(c1);p2 = GetVIndex(c2);node1 = new ENode();node1->nVindex = p2;node1->nWeight = pEData[j]->nWeight;if (m_mVexs[p1].pFirstEdge == NULL){m_mVexs[p1].pFirstEdge = node1;}else{LinkLast(m_mVexs[p1].pFirstEdge, node1);}node2 = new ENode();node2->nVindex = p1;node2->nWeight = pEData[j]->nWeight;if (m_mVexs[p2].pFirstEdge == NULL){m_mVexs[p2].pFirstEdge = node2;}else{LinkLast(m_mVexs[p2].pFirstEdge, node2);}}}~listUDG(){ENode *pENode = NULL;ENode *pTemp = NULL;for (int i = 0; i < m_nVexNum; i ++){pENode = m_mVexs[i].pFirstEdge;if (pENode != NULL){pTemp = pENode;pENode = pENode->pNext;delete pTemp;}delete pENode;}}void PrintUDG(){ENode *pTempNode = NULL;cout << "邻接⽆向表:" << endl;for (int i = 0; i < m_nVexNum; i ++){cout << "顶点:" << GetVIndex(m_mVexs[i].data)<< "-" << m_mVexs[i].data<< "->"; pTempNode = m_mVexs[i].pFirstEdge;while (pTempNode){cout <<pTempNode->nVindex << "->";pTempNode = pTempNode->pNext;}cout << endl;}}// Prim最⼩⽣成树void Prim(int nStart){int i = 0;int nIndex=0; // prim最⼩树的索引,即prims数组的索引char cPrims[MAX]; // prim最⼩树的结果数组int weights[MAX]; // 顶点间边的权值cPrims[nIndex++] = m_mVexs[nStart].data;// 初始化"顶点的权值数组",// 将每个顶点的权值初始化为"第start个顶点"到"该顶点"的权值。

最小生成树Prim算法和单源最短路径Dijkstra算法

最小生成树Prim算法和单源最短路径Dijkstra算法

最小生成树Prim算法和单源最短路径Dijkstra算法问题:1. (最小生成树)给定一个带权的无向连通图,如何选取一棵生成树,使树上所有边上权的总和为最小,即求最小生成树。

2. (单源最短路径)给定一个权值都为正数的无向连通图和一个源点,确定它到其它点的最短距离。

之所以将这两个问题放在一起,是因为Prim算法与Dijkstra算法的思路和程序都非常相似,都是有贪心策略。

1.解法(Prim算法):思路:设连通网络N = { V, E },U表示已加入到生成树的顶点集合。

在初始化阶段,任取一个顶点,用其关联边的权值作为初始的V-U顶点集合的数据。

在每一步中,先在V-U顶点集合中选择距离U中任意点“最近”的顶点v,再把v加入到U中,最后看看在新的V-U顶点集合中,是否有哪个顶点距离v比距离U中其它点更近,若有则更新V-U顶点集合中的数据。

U的元素个数为V-1,所以共要进行V-1步。

总的时间复杂度为Time =O(V)*T_EXTRACT-MIN+O(E)*T_DECREASE-KEY若用数组作为顶点权值的数据结构,T_EXTRACT-MIN用时O(V),T_DECREASE-KEY用时O(1),总共用时O(V^2)若用最小堆作为顶点权值的数据结构,T_EXTRACT-MIN用时O(lgV),T_DECREASE-KEY用时O(lgV),总共用时O((V+E)*lgV)若用斐波那契堆作为顶点权值的数据结构,T_EXTRACT-MIN平均用时O(lgV),T_DECREASE-KEY平均用时O(1),总共用时O(E+V*lgV)下面的代码使用数组作为顶点权值的数据结构:[cpp]view plaincopy1.#include <iostream>2.#include <algorithm>ing namespace std;4.5.#define MAXN 10016.#define INF 10000007.8.int lowcost[MAXN]; // 距离U中顶点的最小权值9.bool visited[MAXN]; // 若为true表示已加入到集合U中10.int mst[MAXN]; // 距离U中哪个顶点最短11.int graph[MAXN][MAXN]; // 用矩阵表示图上各边权值12.13.void Prim(int n)14.{15.int i, j;16. memset(visited, 0, n*sizeof(bool));17. visited[0] = true;18. mst[0] = -1;19.for (i=1; i<n; i++)20. {21. lowcost[i] = graph[0][i];22. mst[i] = 0;23. }24.for (i=1; i<=n-1; i++)25. {26.// 取V-U中的最小权值T_EXTRACT-MIN O(V)27.int min=INF, minid;28.for (j=1; j<n; j++)29.// 用visited数组确定V-U30.if (!visited[j] && lowcost[j] < min)31. {32. min = lowcost[j];33. minid = j;34. }35. visited[minid] = true;36.// 减小V-U中的权值T_DECREASE-KEY O(1)37.for (j=1; j<n; j++)38.if (!visited[j] && lowcost[j] > graph[minid][j])39. {40. lowcost[j] = graph[minid][j];41. mst[j] = minid;42. }43. }44.}45.46.int main()47.{48.int n, m, i, j;49. cin >> n >> m;50.for (i=0; i<n; i++)51.for (j=0; j<n; j++)52. graph[i][j] = INF;53.for (i=0; i<m; i++)54. {55.char a[3], b[3];56.int c;57. scanf("%s %s %d",&a, &b, &c);58. graph[a[0]-'A'][b[0]-'A'] = c;59. graph[b[0]-'A'][a[0]-'A'] = c;60. }61. Prim(n);62.int total = 0;63.for (i=1; i<n; i++)64. {65. total += lowcost[i];66. printf("%c-%c: %d\n", i+'A', mst[i]+'A', lowcost[i]);67. }68. printf("%d\n", total);69.}2.解法(Dijkstra算法):思路:设连通网络N = { V, E }和给定源点u,U表示已确定最短路径的顶点集合。

最小生成树算法的应用

最小生成树算法的应用

最小生成树算法的应用最小生成树算法是图论中重要的算法之一,其可用于解决许多实际问题。

在无向连通图中,最小生成树指的是图中所有边的集合,使得连接所有节点的代价最小。

最小生成树算法主要有Prim算法和Kruskal算法两种,本文将探讨这两种算法的应用。

一、Prim算法及其应用Prim算法是一种基于贪心思想的最小生成树算法,它将整个图分为两个集合:已经包含在最小生成树内的节点集合和未包含在最小生成树内的节点集合。

算法开始时,已经包含在最小生成树内的节点集合为空,未包含节点集合包含整个图。

随着算法的进行,两个集合中的节点不断互相转移,直至最小生成树形成为止。

以下是Prim算法的基本步骤:1. 从任意一个节点开始,将其加入已包含节点集合中。

2. 对于已包含节点集合中的所有节点,找出它们与未包含节点集合中节点的边中权值最小的那条,将与之相连的节点加入已包含节点集合中。

3. 重复步骤2,直至最小生成树形成。

Prim算法的时间复杂度为O(N^2),其中N为图中节点数。

因此,Prim算法适用于节点数量较少的图。

Prim算法有许多实际应用,其中之一是在计算机网络中实现路由协议。

在网络中,每一个节点都需要选择一个和自己相连的节点作为下一步传递数据的目标。

为了避免重复传输或者丢失数据包,路由协议需要保证每一个数据包能够找到最短的传输路径,同时尽可能地避免网络拥塞。

Prim算法恰好能够解决这个问题,它能够由网络中的节点生成一颗树,树上的每个节点都能够连接网络中所有的节点,同时保证整个连接过程中的最短路径。

因此,Prim算法成为计算机网络中重要的算法之一。

二、Kruskal算法及其应用Kruskal算法也是一种基于贪心思想的最小生成树算法,它将整个图先看做是一个节点的集合,然后不断地将边加入其中,形成最小生成树。

Kruskal算法的基本步骤如下:1. 将图中所有边按照权值从小到大排序。

2. 依次遍历所有的边,在加入当前边时,判断当前边的两个节点是否在同一个集合中,如果不在同一个集合中,就将它们合并,并将这条边加入最小生成树的边集中。

图结构习题答案

图结构习题答案

第6章 图【例6-1】回答下列问题:(1)具有n 个顶点的连通图至少有多少条边(2)具有n 个顶点的强连通图至少有多少条边这样的图应该是什么形状 (3)具有n 个顶点的有向无环图最多有多少条边 解:(1)具有n 个顶点的连通图至少有n-1条边。

这是一个与生成树相关的问题。

生成树是一个连通图,它具有能够连通图中任何两个顶点的最小边集,任何一个生成树都具有n-1边。

因此,具有n 个顶点的连通图至少有n-1条边。

(2)具有n 个顶点的强连通图至少有n 条边,这样的图是一个由n 个顶点构成的环。

强连通图是相对于有向图而言的。

由于强连通图要求图中任何两个顶点之间能够相互连通,因此每个顶点至少要有一条以该顶点为弧头的弧和一条以该顶点为弧尾的弧,每个顶点的入度和出度至少各为1,即顶点的度至少为2,这样根据图的顶点数、边数以及各项点的度三者之间的关系计算可得:边数=2×n/2=n 。

(3)具有n 个顶点的有向无环图最多有n ×(n —1)/2条边。

这是一个拓扑排序相关的问题。

—个有向无环图至少可以排出一个拓扑序列,不妨设这n 个顶点排成的拓扑序列为v1,v2,v3,…,vn ,那么在这个序列中,每个顶点vi 只可能与排在它后面的顶点之间存在着以vi 为弧尾的弧,最多有n-i 条,因此在整个图中最多有(n-1)+(n-2)+ … +2+1=n ×(n-1)/2条边。

2.图的存储结构常用的存储结构有邻接矩阵和邻接表。

(1)邻接矩阵表示法设G =(V ,E)是有n(n ≥1)个顶点的图。

则G 的邻接矩阵是按如下定义的n 阶方阵:例如,图6-1中G1,G2的邻接矩阵分别表示为A1、A2,矩阵的行列号对应于图6-1中结点的序号。

由邻接矩阵的定义可知,无向图的邻接矩阵必定是对称阵;有向图的邻接矩阵不一定是对称的。

根据邻接矩阵,很容易判定任意两个顶点之间是否有边相连。

求各顶点的度也是非常容易的。

普里姆算法最小生成树例题

普里姆算法最小生成树例题

普里姆算法最小生成树例题含解答普里姆算法(Prim's Algorithm)是一种用于求解无向图的最小生成树(Minimum Spanning Tree,MST)的算法。

最小生成树是一个包含图中所有顶点的树,且树的边权值之和最小。

下面是一个示例图,我们将使用Prim's算法找到其最小生成树:```图:2 3A --------- B| \ |1| \5 |4| \ |C --------- D6```对应的邻接矩阵:```A B C DA [0, 2, 1, 0]B [2, 0, 5, 3]C [1, 5, 0, 6]D [0, 3, 6, 0]```假设我们从顶点A开始,下面是Prim's算法的执行步骤:1. 初始化:-选择任意起始顶点,这里选择A。

-将A标记为已访问。

-初始化一个最小堆(优先队列),用于存储候选边。

2. 找到最小权值的边:-将A相邻的边(AB、AC)加入最小堆。

3. 选择最小边:-从最小堆中选择权值最小的边,这里是AB(权值为2)。

-将B标记为已访问。

-将与B相邻的边(BD、BC)加入最小堆。

4. 重复步骤3:-从最小堆中选择权值最小的边,这里是BC(权值为3)。

-将C标记为已访问。

-将与C相邻的边(CA、CD)加入最小堆。

5. 继续重复:-从最小堆中选择权值最小的边,这里是CA(权值为1)。

-将A标记为已访问。

-将与A相邻的边(AB、AC)加入最小堆。

6. 重复步骤3和5:-从最小堆中选择权值最小的边,这里是AC(权值为1)。

-将C标记为已访问。

-将与C相邻的边(CA、CD)加入最小堆。

7. 继续重复:-从最小堆中选择权值最小的边,这里是CA(权值为1)。

-将A标记为已访问。

最终,我们得到的最小生成树为:```3A --------- B| \1| \| \C --------- D6```边的权值之和为1+2+3=6,这是该最小生成树的总权值。

请注意,Prim's算法的最终生成树可能不是唯一的,但总权值应始终是最小的。

最小生成树的两种算法

最小生成树的两种算法

最小生成树的两种算法包括:
1. Prim算法:
Prim算法是一种选择点加入树的算法。

首先选择任意一点作为树的第一个节点,然后枚举与它相连的所有点,将两点之间的边权记为这个点到生成树的距离,选择距离最近的点加入生成树,然后枚举与之相邻的节点,用边权更新该节点的距离,使距离等于两个节点之间的边的权重和。

再继续加入当前离生成树最近的点,在更新它相邻的点,以此类推,直到所有点全部加入生成树。

这样就求出了最小生成树。

2. Kruskal算法:
Kruskal算法也称为“加边法”。

首先把图中的所有边按代价从小到大排序,把图中的n个顶点看成独立的n棵树组成的森林,按权值从小到大选择边,所选的边连接的两个顶点应该属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树。

重复以上步骤,直到所有顶点都在一颗树内或者有n-1条边为止。

这样就可以得到最小生成树。

以上信息仅供参考,可以咨询计算机专业人士或者查看专业书籍,
以获取更准确更全面的内容。

最小生成树问题

最小生成树问题

最小生成树问题
最小生成树问题是指在连接有n个点的图的所有n-1条边中,找到一棵边权和最小的树,这棵树包含了图中所有的点,并且所有点之间都是通过这些边相互连接的。

最小生成树问题可以用来解决一些实际问题,比如网络规划、电力传输、通信网络等。

在计算机领域中,最小生成树问题通常可以用来解决分布式系统中的数据同步问题、数据中心间的通信问题等。

常用的解决最小生成树问题的算法有Prim算法和Kruskal算法。

Prim算法是一种贪心算法,它从一个初始点开始,每次选择与当前生成树相连的边中权值最小的边,并且将该边连接的点加入到生成树中。

重复这个过程,直到生成树包含了所有的点为止。

Kruskal算法是一种基于并查集的贪心算法。

它将所有边按照权值从小到大排序,然后依次遍历每条边,如果这条边连接的两个点不在同一个连通分量中,则将这条边添加到最小生成树中,并合并这两个连通分量。

重复这个过程,直到生成树包含了所有的点为止。

最小生成树问题是一个经典的优化问题,可以使用上述的两种算法来解决。

其中Prim算法的时间复杂度为O(n^2),Kruskal
算法的时间复杂度为O(m log n),其中n表示点的个数,m表示边的个数。

数据结构(三十三)最小生成树(Prim、Kruskal)

数据结构(三十三)最小生成树(Prim、Kruskal)

数据结构(三⼗三)最⼩⽣成树(Prim、Kruskal) ⼀、最⼩⽣成树的定义 ⼀个连通图的⽣成树是⼀个极⼩的连通⼦图,它含有图中全部的顶点,但只有⾜以构成⼀棵树的n-1条边。

在⼀个⽹的所有⽣成树中,权值总和最⼩的⽣成树称为最⼩代价⽣成树(Minimum Cost Spanning Tree),简称为最⼩⽣成树。

构造最⼩⽣成树的准则有以下3条:只能使⽤该图中的边构造最⼩⽣成树当且仅当使⽤n-1条边来连接图中的n个顶点不能使⽤产⽣回路的边 对⽐两个算法,Kruskal算法主要是针对边来展开,边数少时效率会⾮常⾼,所以对于稀疏图有很⼤的优势;⽽Prim算法对于稠密图,即边数⾮常多的情况会更好⼀些。

⼆、普⾥姆(Prim)算法 1.Prim算法描述 假设N={V,{E}}是连通⽹,TE是N上最⼩⽣成树中边的集合。

算法从U={u0,u0属于V},TE={}开始。

重复执⾏下⾯的操作:在所有u属于U,v 属于V-U的边(u,v)中找⼀条代价最⼩的边(u0,v0)并加⼊集合TE,同时v0加⼊U,直到U=V为⽌。

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

2.Prim算法的C语⾔代码实现/* Prim算法⽣成最⼩⽣成树 */void MiniSpanTree_Prim(MGraph G){int min, i, j, k;int adjvex[MAXVEX]; /* 保存相关顶点下标 */int lowcost[MAXVEX]; /* 保存相关顶点间边的权值 */lowcost[0] = 0;/* 初始化第⼀个权值为0,即v0加⼊⽣成树 *//* lowcost的值为0,在这⾥就是此下标的顶点已经加⼊⽣成树 */adjvex[0] = 0; /* 初始化第⼀个顶点下标为0 */for(i = 1; i < G.numVertexes; i++) /* 循环除下标为0外的全部顶点 */{lowcost[i] = G.arc[0][i]; /* 将v0顶点与之有边的权值存⼊数组 */adjvex[i] = 0; /* 初始化都为v0的下标 */}for(i = 1; i < G.numVertexes; i++){min = INFINITY; /* 初始化最⼩权值为∞, *//* 通常设置为不可能的⼤数字如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)\n", adjvex[k], k);/* 打印当前顶点边中权值最⼩的边 */lowcost[k] = 0;/* 将当前顶点的权值设置为0,表⽰此顶点已经完成任务 */for(j = 1; j < G.numVertexes; j++) /* 循环所有顶点 */{if(lowcost[j]!=0 && G.arc[k][j] < lowcost[j]){/* 如果下标为k顶点各边权值⼩于此前这些顶点未被加⼊⽣成树权值 */lowcost[j] = G.arc[k][j];/* 将较⼩的权值存⼊lowcost相应位置 */adjvex[j] = k; /* 将下标为k的顶点存⼊adjvex */}}}}Prim算法 3.Prim算法的Java语⾔代码实现package bigjun.iplab.adjacencyMatrix;/*** 最⼩⽣成树之Prim算法*/public class MiniSpanTree_Prim {int lowCost; // 顶点对应的权值public CloseEdge(Object adjVex, int lowCost) {this.adjVex = adjVex;this.lowCost = lowCost;}}private static int getMinMum(CloseEdge[] closeEdges) {int min = Integer.MAX_VALUE; // 初始化最⼩权值为正⽆穷int v = -1; // 顶点数组下标for (int i = 0; i < closeEdges.length; i++) { // 遍历权值数组,找到最⼩的权值以及对应的顶点数组的下标if (closeEdges[i].lowCost != 0 && closeEdges[i].lowCost < min) {min = closeEdges[i].lowCost;v = i;}}return v;}// Prim算法构造图G的以u为起始点的最⼩⽣成树public static void Prim(AdjacencyMatrixGraphINF G, Object u) throws Exception{// 初始化⼀个⼆维最⼩⽣成树数组minSpanTree,由于最⼩⽣成树的边是n-1,所以数组第⼀个参数是G.getVexNum() - 1,第⼆个参数表⽰边的起点和终点符号,所以是2 Object[][] minSpanTree = new Object[G.getVexNum() - 1][2];int count = 0; // 最⼩⽣成树得到的边的序号// 初始化保存相关顶点和相关顶点间边的权值的数组对象CloseEdge[] closeEdges = new CloseEdge[G.getVexNum()];int k = G.locateVex(u);for (int j = 0; j < G.getVexNum(); j++) {if (j!=k) {closeEdges[j] = new CloseEdge(u, G.getArcs()[k][j]);// 将顶点u到其他各个顶点权值写⼊数组中}}closeEdges[k] = new CloseEdge(u, 0); // 加⼊u到⾃⾝的权值0for (int i = 1; i < G.getVexNum(); i++) { // 注意,这⾥从1开始,k = getMinMum(closeEdges); // 获取u到数组下标为k的顶点的权值最短minSpanTree[count][0] = closeEdges[k].adjVex; // 最⼩⽣成树第⼀个值为uminSpanTree[count][1] = G.getVexs()[k]; // 最⼩⽣成树第⼆个值为k对应的顶点count++;closeEdges[k].lowCost = 0; // 下标为k的顶点不参与最⼩权值的查找了for (int j = 0; j < G.getVexNum(); j++) {if (G.getArcs()[k][j] < closeEdges[j].lowCost) {closeEdges[j] = new CloseEdge(G.getVex(k), G.getArcs()[k][j]);}}}System.out.print("通过Prim算法得到的最⼩⽣成树序列为: {");for (Object[] Tree : minSpanTree) {System.out.print("(" + Tree[0].toString() + "-" + Tree[1].toString() + ")");}System.out.println("}");}} 4.举例说明Prim算法实现过程 以下图为例: 测试类:// ⼿动创建⼀个⽤于测试最⼩⽣成树算法的⽆向⽹public static AdjacencyMatrixGraphINF createUDNByYourHand_ForMiniSpanTree() {Object vexs_UDN[] = {"V0", "V1", "V2", "V3", "V4", "V5", "V6", "V7", "V8"};int arcsNum_UDN = 15;int[][] arcs_UDN = new int[vexs_UDN.length][vexs_UDN.length];for (int i = 0; i < vexs_UDN.length; i++) // 构造⽆向图邻接矩阵for (int j = 0; j < vexs_UDN.length; j++)if (i==j) {arcs_UDN[i][j]=0;} else {arcs_UDN[i][j] = arcs_UDN[i][j] = INFINITY;}arcs_UDN[0][5] = 11;arcs_UDN[1][2] = 18;arcs_UDN[1][6] = 16;arcs_UDN[1][8] = 12;arcs_UDN[2][3] = 22;arcs_UDN[2][8] = 8;arcs_UDN[3][4] = 20;arcs_UDN[3][6] = 24;arcs_UDN[3][7] = 16;arcs_UDN[3][8] = 21;arcs_UDN[4][5] = 26;arcs_UDN[4][7] = 7;arcs_UDN[5][6] = 17;arcs_UDN[6][7] = 19;for (int i = 0; i < vexs_UDN.length; i++) // 构造⽆向图邻接矩阵for (int j = i; j < vexs_UDN.length; j++)arcs_UDN[j][i] = arcs_UDN[i][j];return new AdjMatGraph(GraphKind.UDN, vexs_UDN.length, arcsNum_UDN, vexs_UDN, arcs_UDN);}public static void main(String[] args) throws Exception {AdjMatGraph UDN_Graph = (AdjMatGraph) createUDNByYourHand_ForMiniSpanTree();MiniSpanTree_Prim.Prim(UDN_Graph, "V0");} 输出为:通过Prim算法得到的最⼩⽣成树序列为: {(V0-V1)(V0-V5)(V1-V8)(V8-V2)(V1-V6)(V6-V7)(V7-V4)(V7-V3)} 分析算法执⾏过程:从V0开始:-count为0,k为0,closeEdges数组的-lowCost为{0 10 INF INF INF 11 INF INF INF},adjVex数组为{V0,V0,V0,V0,V0,V0,V0,V0,V0}-⽐较lowCost,于是k为1,adjVex[1]为V0,minSpanTree[0]为(V0,V1),lowCost为{0 0 INF INF INF 11 INF INF INF}-k为1,与V1的权值⾏⽐较,得到新的-lowCost为:{0 0 18 INF INF 11 16 INF 12},adjVex数组为{V0,V0,V1,V0,V0,V0,V1,V0,V1}-⽐较lowCost,于是k为5,adjVex[5]为V0,minSpanTree[1]为(V0,V5),lowCost为{0 0 18 INF INF 0 16 INF 12}-k为5,与V5的权值⾏⽐较,得到新的-lowCost为{0 0 18 INF 26 0 16 INF 12},adjVex数组为{V0,V0,V1,V0,V5,V0,V1,V0,V1}-⽐较lowCost,于是k为8,adjVex[8]为V1,minSpanTree[2]为(V1,V8),lowCost为{0 0 18 INF INF 0 16 INF 0}... 三、克鲁斯卡尔(Kruskal)算法 1.Kruskal算法描述 Kruskal算法是根据边的权值递增的⽅式,依次找出权值最⼩的边建⽴的最⼩⽣成树,并且规定每次新增的边,不能造成⽣成树有回路,直到找到n-1条边为⽌。

Prim(普里姆)算法与Dijkstra(迪杰斯特拉)算法分析比较

Prim(普里姆)算法与Dijkstra(迪杰斯特拉)算法分析比较

Prim(普里姆)算法与Dijkstra(迪杰斯特拉)算法分析比较摘要:数据结构中,普里姆算法与迪杰斯特拉算法分析考虑的均是带权图的造价最小问题,而这两种方法在生活的诸多领域应用相当广泛,但是学生往往把这两种算法混为一谈,似乎认为这两种算法求得的结果是一样的,而不明白为什么一个称为最小生成树,另一个则是最短路径。

本文从算法的思想入手,通过示意图进行对比分析,突出不同点,使学生在今后的学习中加深对知识点的理解与认识。

关键词:普里姆;迪杰斯特拉;示意图1 prim算法与dijstra算法思想分析比较prim算法是在由n 个顶点组成的无向连通图中,取图中任意一个顶点v作为生成树的根,之后往生成树上添加新的顶点w。

在添加的顶点w和已经在生成树上的顶点v之间必定存在一条边,并且该边的权值在所有连通顶点v和w之间的边中取值最小。

之后继续往生成树上添加顶点,直至生成树上含有n个顶点为止。

一般情况下所添加的顶点应满足下列条件:在生成树的构造过程中,图中n 个顶点分属两个集合:已落在生成树上的顶点集u和尚未落在生成树上的顶点集v-u,则应在所有连通u中顶点和v-u中顶点的边中选取权值最小的边。

直到u中包含n个顶点,而v-u中为空集。

迪杰斯特拉(dijkstra)算法是在由n个顶点组成的有向图中,从指定一个顶点出发,提出按路径长度的递增次序,逐步产生最短路径的算法,是单源点多目标点最短路径。

最短路径的求解过程中,图中的顶点也是分属两个集合:指定出发点的顶点为一个集合v,其余顶点为一个集合w。

从w中选择一个离v中顶点最近的一个顶点加入到v中,求出了长度最短的一条最短路径,并把刚选择加入最短路径的顶点作为中间点,对其它顶点到源点的路径长度进行修改,求出长度次短的一条最短路径,依次类推,直到从顶点v到其它各顶点的最短路径全部求出为止。

2 prim算法与dijstra算法处理步骤比较prim算法构造最小生成树的步骤是每次都从生成树外顶点与生成树内顶点相连的边中选择代价最小的加入到生成树,也就是每次按权值局部最小的方法,直到加入所有顶点和n-1条边为止。

c语言prim算法

c语言prim算法

c语言prim算法摘要:1.C 语言和Prim 算法简介2.Prim 算法的应用3.Prim 算法的实现4.结论正文:一、C 语言和Prim 算法简介C 语言是一种广泛应用的计算机编程语言,以其高效性和灵活性著称。

它被广泛应用于操作系统、嵌入式系统、游戏开发等领域。

Prim 算法是一种用于解决无向图最小生成树问题的贪心算法。

它的基本思想是从一个顶点开始,不断地寻找与当前生成树距离最近的顶点,将其加入到生成树中,直到所有顶点都加入到生成树中为止。

二、Prim 算法的应用Prim 算法在图论中有着广泛的应用,例如在网络设计、最短路径问题、数据压缩等领域。

在网络设计中,可以用Prim 算法来找到一个网络的最小生成树,从而减少网络的成本。

在最短路径问题中,Prim 算法可以用来找到一个图中两点之间的最短路径。

在数据压缩中,Prim 算法可以用来找到一个图的最小生成树,从而将图压缩成一个更小的表示。

三、Prim 算法的实现下面是一个简单的C 语言实现的Prim 算法:```c#include <stdio.h>#include <stdlib.h>#include <stdbool.h>typedef struct node {int vertex;int weight;} node;typedef struct graph {int vertices;node **adjList;} graph;void add_edge(graph *g, int src, int dest, int weight) { g->adjList[src] = (node*) malloc(sizeof(node));g->adjList[src]->vertex = dest;g->adjList[src]->weight = weight;g->adjList[dest] = (node*) malloc(sizeof(node));g->adjList[dest]->vertex = src;g->adjList[dest]->weight = weight;}bool is_valid_edge(graph *g, int src, int dest) {return g->adjList[src]!= NULL && g->adjList[dest]!= NULL; }int prim_min_tree_weight(graph *g) {int sum = 0;int *dist = (int*) calloc(g->vertices, sizeof(int));for (int i = 0; i < g->vertices; i++) {dist[i] = INT_MAX;}int min_weight = INT_MAX;int root = -1;while (root == -1) {for (int i = 0; i < g->vertices; i++) {if (dist[i] == INT_MAX) {continue;}for (int j = 0; j < g->vertices; j++) {if (is_valid_edge(g, i, j) && dist[j] > dist[i] + g->adjList[i]->weight) {dist[j] = dist[i] + g->adjList[i]->weight;if (min_weight > dist[j]) {min_weight = dist[j];root = j;}}}}}for (int i = 0; i < g->vertices; i++) { sum += dist[i];}free(dist);return min_weight;}int main() {graph g = {3, NULL};add_edge(&g, 0, 1, 4);add_edge(&g, 0, 2, 3);add_edge(&g, 1, 2, 1);add_edge(&g, 1, 3, 2);add_edge(&g, 2, 3, 4);printf("Minimum tree weight: %d ", prim_min_tree_weight(&g));return 0;}```四、结论C 语言和Prim 算法的结合可以有效地解决图论中的最小生成树问题。

Prim算法及证明

Prim算法及证明

Prim算法及证明Prim算法及证明(2012-02-21 12:19:12)1、算法概述用于生成连通无向图的最小代价生成树。

2、算法步骤步骤一:树T初始状态为空;步骤二:从图中任意选取一个点加入T;步骤三:从图中找出能与T形成树的所有边,将代价最小的边加入T,形成新的树T;步骤四:检查T中边的条数;步骤五:如果条数小于n-1,返回步骤三,否则程序结束,T为最小代价生成树。

3、算法证明要证明Prim算法生成的是最小生成树,我们分两步来证明:(1)Prim算法一定能得到一个生成树;(2)该生成树具有最小代价。

证明如下:(1)Prim算法每次引入一个新边,都恰好引入一个新节点:如果少于1个,则新加入的边的两个端点已经在树中,引入新边后就会形成回路;如果多于一个,即2个,则这条边的两个端点都不在树中,这条边与原来的树就独立了,不再构成一个新的树。

由于第一步中已直接引入了一个顶点,所以只需再引入n-1条边(即n-1个顶点)即可。

假设Prim算法引入边数小于n-1,就意味着还有剩余的点与已生成的树没有相连(n个节点的连通图最小边数是n-1,少于这个数说明图不连通),且剩余的点中没有任何点可以和树中的点连通,而由于原图是连通的,所以不可能存在这种情况,因此Prim算法不可能在此之前结束。

因此Prim算法必能引入n-1条边,此时得到的树是原图的生成树。

(2)下面我们用循环不变式证明生成的树具有最小代价。

循环不变式如下:假设每次引入新的边后形成的树T包含的点集为X,X中点与点之间的所有边构成一个子图G0,则T是G0的最小代价生成树。

初始化:引入新边之前,先直接引入一个点,由于此时G0中只有一个点,因此该点就是G的最小代价生成树。

保持:令引入新边前的树是T0,点集是X0,构成的子图是G0,引入的边是e,相应的点是y;得到新的树是T1,点集是X1=X0+y,构成的子图是G1。

假设T1不是G1的最小生成树,我们必然可以在G1中其它边中找到一条边d,由于T1本身是树,d加入后形成回路,回路中有代价大于d的边,将其中之一f删掉,从而得到代价更小的树。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

用Prim算法构造最小生成树班级:2010级计算机1班学号:2010131116 姓名:杨才一、实验目的了解最小生成树的概念,掌握生成最小生成树的方法。

二、实验内容建立一个含任意结点的无向连通网,并用Prim算法构造其最小生成树三、实验要点及说明如果无向连通图是一个网,则其所有生成树中必有一棵树的边的权值总和最小,这棵生成树为最小生成树。

Prim算法:在图G=(V,E)(V为顶点集,E为边集)中任选一顶点v0,令集合U={v0}为初态,在一个顶点在U中,另一顶点在V-U 中的所有边中找权值最小的边(u,v)(U∈ u,v∈ V-U),并将v加入到U中,同时将边(u,v)加入集合T中(T的初态为空集),这样不断地扩大U,直至U=V,则集合T中的边为所求最小生成树的边四、算法思想与算法描述1、邻接矩阵的数据类型的定义如下:typedef struct{ int no; /*顶点编号*/string name; /*顶点其他信息*/} VertexType; /*顶点类型*/typedef struct/*图的定义*/{ int edges[MAXV][MAXV]; /*邻接矩阵*/int vexnum,arcnum; /*顶点数,弧数*/VertexType vexs[MAXV]; /*存放顶点信息*/}MGraph;2、临时数组的存放的数据类型struct {int closest; // U集中的顶点序号int lowcost; // 边的权值} closedge[MAXV];int const INF=32767; /*INF表示∞*/3、prime算法实现:(原理见实验说明)void prime(MGraph g,int v){int lowcost[MAXV];int min;int closest[MAXV];int i,j,k;for(i=0;i<g.vexnum;i++){lowcost[i]=g.edges[v][i];closest[i]=v;}for(i=1;i<g.vexnum;i++){min=INF;for(j=0;j<g.vexnum;j++)if(lowcost[j]!=0&&lowcost[j]<min){min=lowcost[j];k=j;}printf("边(%d,%d)权为:%d\n",closest[k],k,min);lowcost[k]=0;for(j=0;j<g.vexnum;j++)if(g.edges[k][j]!=0&&g.edges[k][j]<lowcost[j]){lowcost[j]=g.edges[k][j];closest[j]=k;}}}4、邻接矩阵的创建void CreatMGraph(MGraph &M){int n,e;cout<<"输入定点数:";cin>>n;M.vexnum=n;cout<<"输入弧数:";cin>>e;M.arcnum=e;for(int i=0;i<n;i++){for(int j=0;j<n;j++){if(i==j)M.edges[i][j]=0;elseM.edges[i][j]=INF;}}cout<<"输入边的权:(如1 2 3 表示点到点的权时)"<<endl;for(int i=0;i<2*e;i++){int x,y,z;cin>>x>>y>>z;M.edges[x][y]=z;}cout<<"输入点编号,名字:"<<endl;for(int i=0;i<n;i++){int No;string str;cin>>No>>str;M.vexs[i].name=str;M.vexs[i].no=No;}}int const MAXV=164、主函数int main(void){MGraph m;CreatMGraph(m);cout<<"输出无向图的二维矩阵:"<<endl;for(int i=0;i<m.vexnum;i++){for(int j=0;j<m.vexnum;j++){if(m.edges[i][j]==INF)cout<<" ";elsecout<<m.edges[i][j]<<" ";}cout<<endl;}cout<<"输入最小生成树:"<<endl;prime(m,0);return 0;}五、实验测试及结果1.输入图的定点数和边数2.输入邻矩阵:3.顶点编号级名字的输入:4.运行结果:六、总结与体会实验任务基本完成,加深队算法思想的认识附源码:#include<iostream>#include<string>using namespace std;int const MAXV=16;typedef struct{ int no; /*顶点编号*/string name; /*顶点其他信息*/} VertexType; /*顶点类型*/typedef struct/*图的定义*/{ int edges[MAXV][MAXV]; /*邻接矩阵*/int vexnum,arcnum; /*顶点数,弧数*/ VertexType vexs[MAXV]; /*存放顶点信息*/}MGraph;struct {int closest; // U集中的顶点序号int lowcost; // 边的权值} closedge[MAXV];int const INF=32767; /*INF表示∞*/void prime(MGraph g,int v){int lowcost[MAXV];int min;int closest[MAXV];int i,j,k;for(i=0;i<g.vexnum;i++){lowcost[i]=g.edges[v][i];closest[i]=v;}for(i=1;i<g.vexnum;i++){min=INF;for(j=0;j<g.vexnum;j++)if(lowcost[j]!=0&&lowcost[j]<min){min=lowcost[j];k=j;}printf("边(%d,%d)权为:%d\n",closest[k],k,min);lowcost[k]=0;for(j=0;j<g.vexnum;j++)if(g.edges[k][j]!=0&&g.edges[k][j]<lowcost[j]){lowcost[j]=g.edges[k][j];closest[j]=k;}}}void CreatMGraph(MGraph &M){int n,e;cout<<"输入定点数:";cin>>n;M.vexnum=n;cout<<"输入弧数:";cin>>e;M.arcnum=e;for(int i=0;i<n;i++){for(int j=0;j<n;j++){if(i==j)M.edges[i][j]=0;elseM.edges[i][j]=INF;}}cout<<"输入边的权:(如1 2 3 表示点到点的权时)"<<endl;for(int i=0;i<2*e;i++){int x,y,z;cin>>x>>y>>z;M.edges[x][y]=z;}cout<<"输入点编号,名字:"<<endl;for(int i=0;i<n;i++){int No;string str;cin>>No>>str;M.vexs[i].name=str;M.vexs[i].no=No;}}int main(void){MGraph m;CreatMGraph(m);cout<<"输出无向图的二维矩阵:"<<endl;for(int i=0;i<m.vexnum;i++){for(int j=0;j<m.vexnum;j++){if(m.edges[i][j]==INF)cout<<" ";elsecout<<m.edges[i][j]<<" ";}cout<<endl;}cout<<"输入最小生成树:"<<endl;prime(m,0);return 0;}---------------------------------------------------------------------------1 2.//图的邻接矩阵存储表示2#define INFINITY INT_MAX3#define MAX_VERTEX_NUM 204typedef enum {DG, DN, UDG, UDN} GraphKind; //{有向图,有向网,无向图,无向网} 5typedef enum {OK, ERROR} Status;6typedef struct ArcCell{7int adj; //顶点关系类型。

对于无权图,用0或1表示相邻否。

对于有权图,则为权值类型。

8 string info; //该弧所携带的信息9}ArcCell, AdjMatrix;10typedef struct {11int vexs[MAX_VERTEX_NUM]; //顶点向量12 AdjMatrix arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; //邻接矩阵13int vexnum, arcnum; //图的当前顶点数和弧数14 GraphKind kind; //图的种类标志15}MGraph;16bool visited[MAX_VERTEX_NUM]; //设访问标志数组,供遍历时使用1//Prim算法-记录从顶点集U到V-U的代价最小的边的辅助数组定义2struct {3int adjvex;4int lowcost;5}closedge[MAX_VERTEX_NUM];1void MiniSpanTree_PRIM(MGraph G, int u)2{34int i,j,k;5for(j=0; j<G.vexnum; ++j) //辅助数组初始化6if(j!=u)7 {8 closedge[j].adjvex = u;9 closedge[j].lowcost = G.arcs[u][j].adj;10 }11 closedge[u].lowcost = 0; //初始化U={u}12for(i=0; i<G.vexnum; ++i)13 {14int min = INT_MAX;15 k=-1;16for(j=0; j<G.vexnum; ++j) //求出最小生成树的下一个顶点k17if(closedge[j].lowcost >0 && closedge[j].lowcost < min)18 {19 k = j;20 min = closedge[j].lowcost;21 }//if22if(k <0)23return;24 cout<<closedge[k].adjvex<<"——"<<G.vexs[k]<<" :"<<closedge[k].lowcost<<endl; //输出生成树的边25 closedge[k].lowcost = 0; //第k顶点并入U集26for(j=0; j<G.vexnum; ++j)27if(G.arcs[k][j].adj < closedge[j].lowcost) //新顶点并入U后,重新选择最小边28 {29 closedge[j].adjvex = G.vexs[k];30 closedge[j].lowcost = G.arcs[k][j].adj;31 }32 }33}//MiniSpanTree_PRIM1void CreateUDNTest(MGraph &G)2{ //构造一个测试用的无向网3int i,j;4 //数组a[10][3]存放“边”和对应的权值5int a[10][3] = { {0, 1, 6}, {0, 2, 1}, {0, 3, 5},6 {1, 2, 5}, {1, 4, 3}, {2, 3, 5}, {2, 4, 6}, {2, 5, 4},7 {4, 5, 6}, {3, 5, 2}8 };9 G.kind = UDN;10 G.vexnum = 6;11 G.arcnum = 10;12for(i=0; i<G.vexnum; ++i) //构造顶点向量13 G.vexs[i] = i;14for(i=0; i<G.vexnum; ++i) //初始化为全INT_MAX15for(j=0; j<G.vexnum; ++j)16 {17 G.arcs[i][j].adj = INT_MAX;18 G.arcs[i][j].info = "";19 }20for(i=0; i<G.arcnum; ++i) //对存在的边赋值21 G.arcs[a[i][0]][a[i][1]].adj = G.arcs[a[i][1]][a[i][0]].adj = a[i][2]; 22}1int main()2{3 MGraph gra2;4 CreateUDNTest(gra2); //构造无向网G5 cout<<"构造最小生成树:"<<endl;6 MiniSpanTree_PRIM(gra2, 0);7return 0;8}-------------------------------------------------------------------------------------------------------------------。

相关文档
最新文档