prim算法求解最小生成树
- 格式:doc
- 大小:54.50 KB
- 文档页数:2
的最小生成树算法Prim与Kruskal算法的比较Prim算法和Kruskal算法都是常用的最小生成树算法,它们可以在给定的加权连通图中找到连接所有节点的最小权重边集合。
然而,这两种算法在实现细节和时间复杂度上有所不同。
本文将对Prim算法和Kruskal算法进行比较,并讨论它们的优缺点以及适用场景。
一、Prim算法Prim算法是一种贪心算法,它从一个起始节点开始,逐步扩展最小生成树的边集合,直到包含所有节点为止。
具体步骤如下:1. 选取一个起始节点作为最小生成树的根节点。
2. 在最小生成树的边集合中寻找与当前树集合相连的最小权重边,并将这条边添加到最小生成树中。
3. 将新添加的节点加入到树集合中。
4. 重复步骤2和3,直到最小生成树包含所有节点为止。
Prim算法的时间复杂度为O(V^2),其中V是节点的个数。
这是因为在每轮迭代中,需要从树集合以外的节点中找到与树集合相连的最小权重边,而在最坏情况下,可能需要检查所有的边。
二、Kruskal算法Kruskal算法是一种基于边的贪心算法,它按照边的权重从小到大的顺序依次选择边,并判断是否加入最小生成树中。
具体步骤如下:1. 初始化一个空的最小生成树。
2. 将所有边按照权重从小到大进行排序。
3. 依次检查每条边,如果这条边连接了两个不同的树(即不会形成环),则将这条边加入到最小生成树中。
4. 重复步骤3,直到最小生成树包含所有节点为止。
Kruskal算法使用并查集数据结构来快速判断连通性,时间复杂度为O(ElogE),其中E是边的个数。
排序边的时间复杂度为O(ElogE),而对每条边进行判断和合并操作的时间复杂度为O(E)。
三、比较与总结1. 时间复杂度:Prim算法的时间复杂度为O(V^2),而Kruskal算法的时间复杂度为O(ElogE)。
因此,在边的数量较大的情况下,Kruskal 算法的效率优于Prim算法。
2. 空间复杂度:Prim算法需要维护一个大小为V的优先队列和一个大小为V的布尔数组,而Kruskal算法需要维护一个大小为V的并查集。
一、概述在图论中,最小生成树是指在一个连通图中生成一棵包含图中所有顶点且边权值之和最小的树。
prim算法是一种常用的求取最小生成树的算法之一,其基本思想是从一个起始顶点开始,逐步选择与当前树相邻的并且权值最小的边,直到包含了图中所有的顶点为止。
本文将介绍prim算法的原理以及给出相应的C代码实现。
二、prim算法原理1. 初始化选择任意一个顶点作为起始顶点,并将其标记为已访问。
设置一个集合V来存放已经访问的顶点,初始化为空集合。
2. 重复以下步骤直到V包含了所有顶点:(1)遍历集合V中的所有顶点,找到与之相邻且未被访问过的顶点中边权值最小的顶点,将其加入集合V中。
(2)将找到的顶点与其相邻的边加入最小生成树中。
3. 输出最小生成树当集合V包含了所有顶点之后,即可输出最小生成树。
三、prim算法C代码实现以下是prim算法的C代码实现:#include <stdio.h>#include <limits.h>#define V 5 // 顶点个数int minKey(int key[], bool mstSet[]) {int min = INT_MAX, min_index;for (int v = 0; v < V; v++) {if (!mstSet[v] key[v] < min) {min = key[v], min_index = v;}}return min_index;}void printMST(int parent[], int graph[V][V]) {printf("Edge \tWeight\n");for (int i = 1; i < V; i++) {printf("d - d \td \n", parent[i], i, graph[i][parent[i]]);}void primMST(int graph[V][V]) {int parent[V]; // 存放最小生成树的结点int key[V]; // 存放与最小生成树相邻的边的权值bool mstSet[V]; // 存放已访问的顶点for (int i = 0; i < V; i++) {key[i] = INT_MAX, mstSet[i] = false;}key[0] = 0;parent[0] = -1;for (int count = 0; count < V - 1; count++) {int u = minKey(key, mstSet);mstSet[u] = true;for (int v = 0; v < V; v++) {if (graph[u][v] !mstSet[v] graph[u][v] < key[v]) { parent[v] = u, key[v] = graph[u][v];}}}printMST(parent, graph); }int m本人n() {int graph[V][V] = {{0, 2, 0, 6, 0},{2, 0, 3, 8, 5},{0, 3, 0, 0, 7},{6, 8, 0, 0, 9},{0, 5, 7, 9, 0}};primMST(graph);return 0;}```四、代码说明1. minKey函数该函数用于在尚未加入最小生成树的结点中找到与最小生成树相邻的边中权值最小的结点。
Prim算法详细步骤Prim算法是一种用于解决最小生成树问题的贪心算法。
它通过选择边的方式逐步构建最小生成树,从而使得树中所有边的权重之和最小。
本文将详细介绍Prim算法的步骤。
1. 首先,我们需要确定一个起始点作为最小生成树的根节点。
这个起始点可以是图中的任意一个顶点,我们可以根据具体问题的需求来选择。
2. 接下来,我们需要定义一个集合T来存放最小生成树的边。
一开始,集合T是空的。
3. 然后,我们需要定义一个集合V来存放已经加入最小生成树的顶点。
一开始,集合V中只包含起始点。
4. 然后,我们需要找到一条从集合V中的顶点到集合V之外的顶点的边,且该边的权重最小。
我们将这条边称为"最小权重边"。
首先,我们可以将起始点连接到任意一个集合V之外的顶点的边加入集合T,并将该顶点加入集合V。
5. 继续执行步骤4,每次选择一条连接集合V与集合V之外顶点的最小权重边,并将该边加入集合T。
同时,将该边连接的顶点加入集合V。
6. 重复执行步骤5,直到最小生成树中包含了图中的所有顶点。
7. 最后,我们得到的集合T即为最小生成树的边。
通过以上步骤,Prim算法可以找到一个最小生成树。
下面我们通过一个具体的例子来演示Prim算法的过程。
假设我们有一个无向加权图,其中包含了6个顶点和9条边。
我们以A作为起始点开始Prim算法。
初始状态:集合V:{A}集合T:{}步骤4:起始点A连接到B的边为最小权重边,加入集合T,并将B加入集合V。
集合V:{A, B}集合T:{(A, B)}步骤5:集合V中的顶点A连接到D的边为最小权重边,加入集合T,并将D加入集合V。
集合V:{A, B, D}集合T:{(A, B), (A, D)}步骤5:集合V中的顶点D连接到F的边为最小权重边,加入集合T,并将F加入集合V。
集合V:{A, B, D, F}集合T:{(A, B), (A, D), (D, F)}步骤5:集合V中的顶点F连接到G的边为最小权重边,加入集合T,并将G加入集合V。
最⼩⽣成树的Prim算法以及Kruskal算法的证明Prime算法的思路:从任何⼀个顶点开始,将这个顶点作为最⼩⽣成树的⼦树,通过逐步为该⼦树添加边直到所有的顶点都在树中为⽌。
其中添加边的策略是每次选择外界到该⼦树的最短的边添加到树中(前提是⽆回路)。
Prime算法的正确性证明:引理1:对于连通图中的顶点vi,与它相连的所有边中的最短边⼀定是属于最⼩⽣成树的。
引理2:证明:假设最⼩⽣成树已经建成;(vi, vj)是连接到顶点vi的最短边,在最⼩⽣成树中取出vi,断开连接到vi的边,则⽣成树被拆分成1、顶点vi2、顶点vj所在的连通分量(单独⼀个顶点也看作⼀个独⽴的连通分量)3、其余若⼲个连通分量(个数⼤于等于0)三个部分现在要重建⽣成树,就要重新连接之前被断开的各边虽然不知道之前被断开的都是哪⼏条边,但是可以通过这样⼀个简单的策略来重建连接:将vi分别以最⼩的成本逐个连接到这若⼲个互相分离的连通分量;具体来说,就是要分别遍历顶点vi到某个连通分量中的所有顶点的连接,然后选择其中最短的边来连接vi和该连通分量;⽽要将vi连接到vj所在的连通分量,显然通过边(vi, vj)连接的成本最低,所以边(vi, vj)必然属于最⼩⽣成树(如果连接到vi的最短边不⽌⼀条,只要任意挑选其中的⼀条(vi, vj)即可,以上的证明对于这种情况同样适⽤)。
这样我们就为原来只有⼀个顶点vi的⼦树添加了⼀个新的顶点vj及新边(vi, vj);接下来只要将这棵新⼦树作为⼀个连通⼦图,并且⽤这个连通⼦图替换顶点vi重复以上的分析,迭代地为⼦树逐个地添加新顶点和新边即可。
Kruskal算法:通过从⼩到⼤遍历边集,每次尝试为最⼩⽣成树加⼊当前最短的边,加⼊成功的条件是该边不会在当前已构建的图中造成回路,当加⼊的边的数⽬达到n-1,遍历结束。
Kruskal算法的正确性证明:Kruskal算法每次为当前的图添加⼀条不会造成回路的新边,其本质是逐步地连接当前彼此分散的各个连通分量(单个顶点也算作⼀个连通分量),⽽连接的策略是每次只⽤最⼩的成本连接任意两个连通分量。
简述prim算法的过程Prim算法是一种用于求解最小生成树的贪心算法,它把一个连通图分割成多个子图,使得每个子图都是一棵最小生成树,且这些子树的联合就是原连通图的最小生成树。
Prim算法是由安德鲁普里姆(Andrew.Prim)在1957年提出的,用于求解最短路径(解决连通度和权重问题)。
Prim算法一般步骤如下:(1)初始化:从图中任选一顶点,作为第一个顶点加入到最小生成树中;(2)循环:从剩余的顶点中,找到最小代价的边,将这条边加入到最小生成树中,并将这条边的顶点也加入到最小生成树中;(3)重复:重复步骤2,直到最小生成树中包括全部的顶点;(4)停止:最小生成树已经构造完成,停止算法的执行。
Prim算法是贪心算法的一种,它每次在可选的边中搜索代价最小的边,加入到最小生成树中,直至所有的顶点都在最小生成树中。
它的一般步骤可表示为:(1)从最小生成树中所有的顶点中,找出一个确定的顶点 u,它的邻居还未加入最小生成树;(2)从u的邻居v中,找出一个代价最小的边(u,v),将这条边加到最小生成树中;(3)将顶点v加入到最小生成树中;(4)重复步骤1到3,直到最小生成树中包括所有的顶点;深入分析Prim算法,我们可以发现它是一种贪心策略,它在设计上采用了“最优化原则”,即每次都选择代价最小的边加入到最小生成树中,而不管这个边是否有利于求解最小生成树的问题,因而贪心算法的实现对“最优策略”的选择是关键。
Prim算法的时间复杂度取决于边的存储结构,如果存储为邻接表,其时间复杂度为O(V2),如果存储为邻接矩阵,其时间复杂度为O(V2+E)。
其中,V为顶点数,E为边数。
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's Algorithm)和克鲁斯卡尔算法(Kruskal's Algorithm)是求解最小生成树的两种常用方法。
最小生成树是指连接图中所有节点,且边的权重和最小的树。
这两种算法各有特点,在不同的场景中使用。
1.普利姆算法:适用于边稠密的图普利姆算法是一种贪心算法,从一个节点开始,不断选择与当前树相连的、权重最小的边,并将该边连接的节点加入树中,直到所有节点都被遍历完。
这样就得到了最小生成树。
以下是普利姆算法的伪代码:1.创建一个空的树,用于保存最小生成树2.选择一个起始节点,将其加入树中3.从树中已有的节点出发,找到与树相连的边中权重最小的边4.将找到的边连接的节点加入树中5.重复步骤3和4,直到所有节点都加入树中普利姆算法的时间复杂度为O(ElogV),其中E为边的数量,V为节点的数量。
2.克鲁斯卡尔算法:适用于边稀疏的图克鲁斯卡尔算法是一种基于排序和并查集的贪心算法,按照边的权重从小到大的顺序选择,并判断是否会构成环。
如果不会构成环,则选择该边,并将其加入最小生成树中,直到所有节点都被连接。
以下是克鲁斯卡尔算法的伪代码:1.创建一个空的树,用于保存最小生成树2.将所有边按权重从小到大排序3.创建一个并查集,用于判断边是否会构成环4.遍历排序后的边,对于每条边,判断其连接的两个节点是否属于同一个集合(即是否会构成环)5.如果不会构成环,则选择该边,并将其加入树中,同时将该边连接的两个节点合并到同一个集合中6.重复步骤4和5,直到所有节点都连接在一起克鲁斯卡尔算法的时间复杂度为O(ElogE),其中E为边的数量。
这两种算法的应用场景有所不同。
如果要求解的图是边稠密的(即边的数量接近节点数量的平方),则使用普利姆算法更为高效。
因为普利姆算法的时间复杂度与边的数量有关,所以处理边稠密的图会更快一些。
而对于边稀疏的图(即边的数量接近节点数量的线性),克鲁斯卡尔算法更加适用,因为它的时间复杂度与边的数量有关。
程序测试用例如下:程序运行过程截图:源程序清单如下:#include <stdio.h>#define n 6#define MaxNum 10000 /*定义一个最大整数*/2 6 4 43 5 5 1 5 6 1 2 43 5 6/*定义邻接矩阵类型*/typedef int adjmatrix[n+1][n+1]; /*0号单元没用*/typedef struct{int fromvex,tovex;int weight;}Edge;typedef Edge *EdgeNode;int arcnum; /*边的个数*//*建立图的邻接矩阵*/void CreatMatrix(adjmatrix GA){int i,j,k,e;printf("图中有%d个顶点\n",n);for(i=1;i<=n;i++){for(j=1;j<=n;j++){if(i==j){GA[i][j]=0; /*对角线的值置为0*/}else{GA[i][j]=MaxNum; /*其它位置的值置初始化为一个最大整数*/ }}}printf("请输入边的个数:");scanf("%d",&arcnum);printf("请输入边的信息,按照起点,终点,权值的形式输入:\n");for(k=1;k<=arcnum;k++){scanf("%d,%d,%d",&i,&j,&e); /*读入边的信息*/GA[i][j]=e;GA[j][i]=e;}}/*初始化图的边集数组*/void InitEdge(EdgeNode GE,int m){int i;for(i=1;i<=m;i++){GE[i].weight=0;}}/*根据图的邻接矩阵生成图的边集数组*/void GetEdgeSet(adjmatrix GA,EdgeNode GE){ int i,j,k=1;for(i=1;i<=n;i++){for(j=i+1;j<=n;j++){if(GA[i][j]!=0&&GA[i][j]!=MaxNum){GE[k].fromvex=i;GE[k].tovex=j;GE[k].weight=GA[i][j];k++;}}}}/*按升序排列图的边集数组*/void SortEdge(EdgeNode GE,int m){int i,j,k;Edge temp;for(i=1;i<m;i++){k=i;for(j=i+1;j<=m;j++){if(GE[k].weight>GE[j].weight){k=j;}}if(k!=i){temp=GE[i];GE[i]=GE[k];GE[k]=temp;}}}/*利用普里姆算法从初始点v出发求邻接矩阵表示的图的最小生成树*/void Prim(adjmatrix GA,EdgeNode T){int i,j,k,min,u,m,w;Edge temp;/*给T赋初值,对应为v1依次到其余各顶点的边*/k=1;for(i=1;i<=n;i++){if(i!=1){T[k].fromvex=1;T[k].tovex=i;T[k].weight=GA[1][i];k++;}}/*进行n-1次循环,每次求出最小生成树中的第k条边*/for(k=1;k<n;k++){min=MaxNum;m=k;for(j=k;j<n;j++){if(T[j].weight<min){min=T[j].weight;m=j;}}/*把最短边对调到k-1下标位置*/temp=T[k];T[k]=T[m];T[m]=temp;/*把新加入最小生成树T中的顶点序号赋给j*/j=T[k].tovex;/*修改有关边,使T中到T外的每一个顶点保持一条到目前为止最短的边*/for(i=k+1;i<n;i++){u=T[i].tovex;w=GA[j][u];if(w<T[i].weight){T[i].weight=w;T[i].fromvex=j;}}}}/*输出边集数组的每条边*/void OutEdge(EdgeNode GE,int e){int i;printf("按照起点,终点,权值的形式输出的最小生成树为:\n");for(i=1;i<=e;i++){printf("%d,%d,%d\n",GE[i].fromvex,GE[i].tovex,GE[i].weight);}}void main(){adjmatrix GA;Edge GE[n*(n-1)/2],T[n];CreatMatrix(GA);InitEdge(GE,arcnum);GetEdgeSet(GA,GE);SortEdge(GE,arcnum);Prim(GA,T);printf("\n");OutEdge(T,n-1);}。
prim算法c语言什么是Prim算法?Prim算法,也叫普里姆算法,是一种用于求解最小生成树的贪心算法。
最小生成树是指在一个无向连通图中,连接所有节点且边权值之和最小的树。
Prim算法的基本思想是从一个起始节点开始,每次选择与当前已经构建好的部分形成的子图相连的、权值最小的边所连接的节点,并将该节点加入到已经构建好的部分中。
直到所有节点都被加入到已经构建好的部分中,此时得到了一棵最小生成树。
Prim算法步骤1. 选定一个起点作为已经构建好的部分。
2. 将与该起点相连且未被访问过的边加入到候选集合中。
3. 从候选集合中选择一条权值最小的边连接到未被访问过的节点,并将该节点加入到已经构建好的部分中。
4. 将新加入节点所连接且未被访问过的边加入到候选集合中。
5. 重复步骤3和步骤4,直至所有节点都被加入到已经构建好的部分中。
Prim算法C语言实现下面给出Prim算法C语言实现代码:```#include <stdio.h>#include <stdlib.h>#include <limits.h>#define MAX_VERTICES 100#define INF INT_MAXtypedef struct {int weight;int visited;} Vertex;typedef struct {int vertices[MAX_VERTICES][MAX_VERTICES]; int num_vertices;} Graph;void init_graph(Graph *graph, int num_vertices) {graph->num_vertices = num_vertices;for (int i = 0; i < num_vertices; i++) {for (int j = 0; j < num_vertices; j++) {graph->vertices[i][j] = INF;}}}void add_edge(Graph *graph, int u, int v, int weight) { graph->vertices[u][v] = weight;graph->vertices[v][u] = weight;}void prim(Graph *graph) {Vertex vertices[MAX_VERTICES];for (int i = 0; i < graph->num_vertices; i++) {vertices[i].weight = INF;vertices[i].visited = 0;}vertices[0].weight = 0;for (int i = 0; i < graph->num_vertices - 1; i++) {// 找到未访问过的权值最小的节点int min_vertex_index = -1;for (int j = 0; j < graph->num_vertices; j++) {if (!vertices[j].visited && (min_vertex_index == -1 || vertices[j].weight < vertices[min_vertex_index].weight)) { min_vertex_index = j;}}// 将该节点标记为已访问vertices[min_vertex_index].visited = 1;// 更新与该节点相连的未访问过的节点的权值for (int j = 0; j < graph->num_vertices; j++) {if (!vertices[j].visited && graph->vertices[min_vertex_index][j] < vertices[j].weight) {vertices[j].weight = graph->vertices[min_vertex_index][j];}}}// 输出最小生成树printf("Minimum Spanning Tree:\n");for (int i = 1; i < graph->num_vertices; i++) {printf("%d - %d (%d)\n", i, (i - 1), vertices[i].weight); }}int main() {Graph graph;init_graph(&graph, 6);add_edge(&graph, 0, 1, 6);add_edge(&graph, 0, 2, 1);add_edge(&graph, 0, 3, 5);add_edge(&graph, 1, 4, 3);add_edge(&graph, 2, 4, 5);add_edge(&graph, 2, 3, 5);add_edge(&graph, 2, 5, 4);add_edge(&graph, 3 ,5 ,2);prim(&graph);return EXIT_SUCCESS;}```代码解释- 定义了Vertex结构体,用于存储节点的权值和访问状态。
Prim算法优化策略Prim算法是一种用于求解最小生成树问题的经典算法。
它通过逐步选择与当前生成树相连的最小权值边来构造最小生成树。
在实际应用中,Prim算法的时间复杂度较高,因此需要一些优化策略来提高算法效率。
一、延迟更新策略在Prim算法中,每次选择最小权值的边添加到生成树中后,就需要更新与新增节点相邻的边的权值。
而延迟更新策略可以将这个更新过程延迟到后面再进行,避免了反复更新造成的时间浪费。
具体实现时,可以使用一个优先队列(最小堆)来存储与生成树相邻的边,每次从队列中取出权值最小的边,将其添加到生成树中,并标记其相邻节点已访问。
当队列为空时,表示所有节点都已加入生成树,算法结束。
延迟更新策略可以避免多次更新同一条边的权值,大大减少了更新操作的次数,提高了算法效率。
二、稠密图优化策略Prim算法在处理稠密图(边数接近或等于节点数的平方)时,时间复杂度较高。
为了解决这个问题,可以使用邻接矩阵来表示图,同时使用一个数组来记录每个节点到生成树的最小权值。
具体实现时,可以将邻接矩阵中的边权值初始化为一个较大的值,然后从第一个节点开始,选择与当前节点最近的未访问节点,并更新它们到生成树的最小权值。
通过这种方式,可以有效地减少对稠密图中未访问节点的搜索次数,提高算法效率。
三、堆优化策略Prim算法中,每次需要选择与当前生成树相连的最小权值边,这个过程可以通过堆来实现,以减少对边权值的搜索时间。
具体实现时,可以使用一个最小堆来存储边,堆中的每个元素都是一个包含边的两个节点和权值的数据结构。
首先将第一个节点加入生成树中,然后将其相邻边添加到堆中。
每次从堆中取出权值最小的边,将其相邻节点加入生成树,并将新的边添加到堆中。
通过使用堆结构,可以快速找到最小权值的边,提高算法的效率。
综上所述,Prim算法可以通过延迟更新策略、稠密图优化策略和堆优化策略等方法来进行优化,提高算法的效率。
在实际应用中,根据具体的问题和数据特点,选择适当的优化策略可以进一步加快算法的执行速度,提高算法的实用性和可扩展性。
1.如下图,根结点为a,给出Prim算法求解最小生成树的伪代码,在下图中标出最小生成树,给出用binary min-heap来表示min-priority queue时,Prim算法的时间复杂度。
解:伪代码如下:
MST_PRIM(G, w, r) //r=a
for each u∈V[G]
do key[u]←∞
π[u]←NIL
key[r]←Q
Q←V[G]
while Q≠φ
do u←EXTRACT-MIN(Q)
for each v∈Adj[u]
do if v∈Q and w(u,v)<key[v]
thenπ[v]←u
key[v]←w(u,v)
时间复杂度分析:
如果用二叉最小堆来实现最小优先队列Q,则可以用过程BUILD-MIN-HEAP来实现程序初始化部分,其运行时间为O(V)。
while循环的循环体需执行|V|次,且由于每次EXTRACT-MIN操作需要O(lgV),所以对EXTRACT-MIN的全部调用所占用的时间为O(VlgV)。
while循环体中的for循环总共要执行O(E)次,for循环内部最后一行的赋值语句隐含了一个对最小堆进行的DECREASE-KEY操作,该操作在二叉最小堆上可以用O(lgV)时间完成。
因此,Prim算法的整个运行时间为O(VlgV+ElgV)=O(ElgV)。