最小生成树

假设要在n个城市之间建立通信联络网,则连通n个城市只需要n-1条线路。这时,自然各

考虑这样一个问题,如何在最节省经费的前提下建立这个通信网。

在每两个城市之间都可设置一条线路,相应地都要付出一定的经济代价,n个城市之间,最多可能设置n(n-1)2条线路,那么,如何在这些可能的我路中选择n-1条,以使总的耗费最少呢?

可以用连通网来表示n个城市,以及n个城市间可能设置的通信线路,其中网的顶点表示城市,边表示两城市之间的线路,赋予边的权值表示相应的代价。对于n个顶点的连通网可以建立许多不同的生成树,每一棵生成树都可以是一个通信网。最合理的通信网应该是代价之和最小的生成树。在一个连通网的所有生成树,各边的代价之和最小的那棵生成树称为该连通网的最小代价生成树(Minimum Cost Spanning Tree),简称为最小生成树。

Prim算法

普里姆算法的实现思路

假设一个无向网G以邻接矩阵形式存储,从顶点u出发构造G的最小生成树T,要求输出T的各条边。为实现这个算法需附设一个辅助数组closedge,以记录从U到V-U具有最小权值的边。对每个顶点vi属于V-U,在辅助数组中存在一个相应分量closedge[i-1],它包括两个域:lowcost和adjvex。其中lowcost存储最小边上的权值,adjvex存储最小边在U中的那个顶点。显然,closedge[i-1].lowcost=Min{ cost(u,vi)|u属于U},其中cost(u,v)表示赋予边(u,v)的权。

Prim算法就是一直寻找“符合要求的”顶点,每次加入的顶点都可以使那一条边的权值是当前最小的,直到所有的顶点都被加入。

算法步骤

①首先将初始顶点u加入U中,对其余的每一个顶点vi,将closedge[j]均初始化为到u的边信息。

②循环n-1次,做如下处理:

从各组边closedge中选出最小边closedge[k],输出此边;

将k加入U中;

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

【算法分析】

假设网中有n个顶点,则第一个进行初始化的循环语句的频度为n,第二个循环语句的频度为n-1。其中第二个有两个内循环:其一是在closedge[v].lowcost中求最小值,其频度为n-1;其二是重新选择具有最小权值的边,其频度为n。由此,普里姆算法的时间复杂度为0(n^2),与网中的边数无关,因此适用于求稠密网的最小生成树

#####################################################################

首先选择一个顶点加入到U中,即加入到已选顶点集中,接下来进行n-1次循环,每次的操作都是一样的。

对于未选顶点集,每个顶点都有一个closedge数组分量,表示这个数组下标所对应的顶点(未选顶点集)与已选顶点集的顶点之间组成的边,其中权值最小的边的顶点(该顶点是已选顶点集中的顶点的序号,及此时最小的权值。

每次循环,让每个closedge得到最小的记录,并把这个顶点加入已选顶点集中,然后更新每组最小边信息,更新是最重要的一步,每一组都需要更新,就是未选顶点集中的每个顶点的相应closedge分量都要更新,为下一次循环做准备。

closedge的理解:closedge是一个Closedge类型的数组,数组的下标是未选的顶点集中的顶点,不同下标的数组空间中存储的是属于该顶点的信息(对应最小边信息)

知道了算法思路,接下来就是程序的编写了。把思路变为现实的代码需要注意很多技巧和细节。

//Prim算法 //加点法,需要附设一个辅助数组//顶点可以分为两组,一组是已选的,一组是未选的。如何把已选和未选体现在程序中,就需要你自己设置一个标记。显示出二者的区别。
struct  Closedge
{VerTexType adjvex;//最小边在已选的顶点集中的那个顶点(已选的顶点的序号)  主要是记录,便于表示我们找到的那条边int now_vex;//数组下标所对应的顶点  未选的顶点集ArcType lowcost;// 最小边上的权值(已选的顶点和未选的顶点之间的边的权值,取最小。已选对标未选)
}closedge[MVNum];
//对于每一个未选的顶点都有一个自己的closedge分量,每个未选的closedge记录的信息都是通过观察已选的顶点得到的
//对于已选的顶点,需要标明出该顶点已选,则令其lowcost为0int Min(Closedge closedge[],int n)比较权值即lowcost,得到顶点下标,该顶点是未选集中的//仅查找
{int Min,Min_vex,Min_index;for(int i=0;i<n;i++){if(closedge[i].lowcost<min){Min=closedge[i].lowcost;// Min_vex=closedge[i].now_vex;Min_index=i;} }if(closedge[i].lowcost!=0) return Min_vex;//保证是未选顶点集中的
}void MiniSpanTree_Prim(AMGraph G, VerTexType u)
{int i, j, k,count_U=0,count_V=0;VerTexType  u0, v0;k = LocateVex(G, u);for (j = 0; j < G.vexnum; j++){int temp_vex=1;if (j != k)//初始化未选的顶点的closedge,closedge属于未选的顶点{//closedge[j] = { u,G.arcs[k][j] };// temp_vex++;// closedge[j].now_vex=temp_vex;closedge[j].adjvex=u;closedge[j].lowcost=G.arcs[k][j];}}closedge[k].lowcost = 0;//把已选的顶点加入已选顶点集中  如何让代码体现这个加入,只需要做一个标记即可count_U++;//已选加1count_V=G.vexnum-count_U;//未选//上述操作都是初始化,为接下来的工作做准备for (i = 1; i < G.vexnum; i++)//n-1次循环//选择n-1个顶点和n-1条边,上面已经选了一个顶点{k = Min(closedge,count_V);//得到当前权值最小的边//保证一致。。。u0 = closedge[k].adjvex;//u0为最小边的一个顶点,u0属于已选顶点集v0 = G.vexs[k];//v0为最小边的另一个顶点,我们所找到的符合要求的顶点,暂时未选........//vexs...cout << u0 << v0;//输出我们找到的这条边closedge[k].lowcost = 0;//把我们找到的这个符合要求的顶点加入到已选顶点集中count_U++;//已选加1count_V=G.vexnum-count_U;//未选for(j=0;j<G.vexnum;j++)//更新!!!!{if(G.arcs[k][j]<closedge[j].lowcost){closedge[j] = { G.vexs[k],G.arcs[k][j] };}}}
}

完整代码(具体的实现和存储结构,数据类型有关)

#include<iostream>
#include<new>
using namespace std;typedef int Status;
#define  OK 1
#define ERROR 0
#define OVERFLOW -1#define MaxInt 32767 //不相通
#define MVNum 100 //最大顶点个数
typedef char VerTexType;
typedef int ArcType;//权值类型//邻接矩阵,二维数组
typedef struct
{VerTexType vexs[MVNum];//顶点 行 列 存储ArcType arcs[MVNum][MVNum];//存储int vexnum, arcnum;//图的当前点数和边数:我们申请的是固定空间,不是动态申请。固定申请的空间我们不知道将会使用多少,那么此时通过当前记录就可以知道了。}AMGraph;//邻接表
typedef struct
{}OtherInfo;typedef struct ArcNode//边节点
{int adjvex;//可以理解为这个边结点的顶点的序号,因为邻接表中顶点都存储在顶点数组中(表头数组)struct ArcNode* nextarc;OtherInfo info;//和边相关的信息
}ArcNode;typedef struct VNode//表头结点
{VerTexType data;//对比  通过对比关键字可以找到“整体”ArcNode* firstarc;
}VNode, AdjList[MVNum];typedef struct//邻接表
{AdjList vertices;//表头结点表int vexnum, arcnum;//图的当前点数和边数???什么作用???}ALGraph;
#pragma once
#include"AMGraph.h"int LocateVex_UDN(AMGraph G, VerTexType u)//如存在,返回顶点在图中的位置  //监视哨的方式查找
{//VerTexType vexs[MVNum], vexnumint i;for (i = 0; i < G.vexnum; i++){if (G.vexs[i] == u)  return i;}return 0;}Status CreateUDN(AMGraph& G)
{int i, j, k;VerTexType v1, v2;ArcType w;cout << "请输入当前的顶点数和边数" << endl;cin >> G.vexnum >> G.arcnum;//初始化顶点和边(权值)//有权值 有关系 有边for (i = 0; i < G.vexnum; i++){cout << "请输入第" << i + 1 << "个顶点的数据" << endl;cin >> G.vexs[i];}for (i = 0; i < G.vexnum; i++)//矩阵:矩阵的行和列是顶点个数{for (j = 0; j < G.vexnum; j++){G.arcs[i][j] = MaxInt;}}//构造矩阵  有多少条边构造多少次(初始化的时候已经给边赋初值了,//那么接下来我们只需要把”有关系的边“赋给正确的权值(覆盖初值即可),”没有关系的边“,就是不相通,不需要赋值,初始值就表明他们不相通for (k = 0; k < G.arcnum; ++k){cout << "请输入要创建的第" << k + 1 << "条边和它的权值" << endl;cin >> v1 >> v2 >> w;//i = LocateVex_UDN(G, v1);j = LocateVex_UDN(G, v2);G.arcs[i][j] = w;//图  1 ,网  wG.arcs[j][i] = G.arcs[i][j];//无向图  对称}return OK;
}void PrintAMGraph(AMGraph G)//显示一个矩阵。。。(带权)
{for (int i = 0; i < G.vexnum;i++){for(int j=0;j<G.vexnum;j++){cout << G.arcnum[i][j] << " ";}cout << endl;}
}//邻接表创建int LocateVex_UDG(ALGraph G, VerTexType u)//如存在,返回顶点在图中的位置  在顶点中寻找比对  返回顶点的下标(无论是邻接表还是邻接矩阵,顶点都存储在数组中。。。)
{//G.vertices[i].dataint i;for (i = 0; i < G.vexnum; i++){if (G.vertices[i].data == u)  return i;}return 0;
}Status CreateUDG(ALGraph& G)
{int i, j, k;VerTexType v1, v2;ArcNode* p1;ArcNode* p2;cout << "请输入当前的顶点数和边数" << endl;cin >> G.vexnum >> G.arcnum;//初始化for (i = 0; i < G.vexnum; i++){cout << "请输入第" << i + 1 << "个顶点的数据" << endl;cin >> G.vertices[i].data;G.vertices[i].firstarc = NULL;}//构造for (k = 0; k < G.arcnum; ++k){cout << "请输入要创建的第" << k + 1 << "条边" << endl;cin >> v1 >> v2;i = LocateVex_UDG(G, v1);j = LocateVex_UDG(G, v2);//两个顶点一条边  在顶点数组里面寻找。。表头数组vertices返回下标int//jp1 = new ArcNode;//新节点p1->adjvex = j;p1->nextarc = G.vertices[i].firstarc;//头插法G.vertices[i].firstarc = p1;//i  对称p2 = new ArcNode;p2->adjvex = i;p2->nextarc = G.vertices[j].firstarc;G.vertices[j].firstarc = p2;}return OK;
}void PrintALGraph(ALGraph G)//显示一个数表。。。一行是一个单链表。。。
{ArcNode* p;ArcNode* q;cout << "提示:接下来的每一行,第一个为顶点信息,后面的为该顶点的邻接点的信息" << endl;for(int i=0;i<G.vertices;i++){p = new ArcNode;p = G.vertices[i].firstarc;//第一个边结点cout << G.vertices[i].data << " ";while(NULL!=p){cout << G.vertices[p->adjvex].data << "(该边)的信息为" << p->info << " ";p = p->nextarc;}cout << endl;}
}// int main()//如需要。。。
// {
//     cout << "邻接矩阵的创建" << endl;
//     AMGraph G;
//     CreateUDN(G);//     cout << "邻接矩阵创建完成,显示邻接矩阵:" << endl;
//     PrintAMGraph(G);//     cout << "邻接表的创建" << endl;
//     ALGraph H;
//     CreateUDG(H);
//     cout << "邻接表创建完成,显示邻接表:" << endl;
//     PrintALGraph(H);//     return 0;
// }//最小生成树算法(Prim算法  Kruskal算法)//Prim算法 //加点法,需要附设一个辅助数组
struct
{VerTexType adjvex;ArcType lowcost;
}closedge[MVNum];int Min(Closedge closedge[],int n)比较权值即lowcost,得到顶点下标,该顶点是未选集中的//仅查找
{int Min,Min_vex,Min_index;for(int i=0;i<n;i++){if(closedge[i].lowcost<min){Min=closedge[i].lowcost;// Min_vex=closedge[i].now_vex;Min_index=i;} }if(closedge[i].lowcost!=0) return Min_vex;//保证是未选顶点集中的
}void MiniSpanTree_Prim(AMGraph G, VerTexType u)
{int i, j, k;VerTexType  u0, v0;k = LocateVex(G, u);for (j = 0; j < G.vexnum; j++){if (j != k) closedge[j] = { u,G.arcs[k][j] };closedge[k].lowcost = 0;for (i = 1; i < G.vexnum; i++){k = Min(closedge);u0 = closedge[k].adjvex;v0 = G.vexs[k];cout << u0 << v0;closedge[k].lowcost = 0;for(j=0;j<G.vexnum;j++){if(G.arcs[k][j]<closedge[j].lowcost){closedge[j] = { G.vexs[k],G.arcs[k][j] };}}}}}

Kruskal算法

克鲁斯卡尔算法的构造过程

假设连通网N=(V,E),将N中的边按权值从小到大的顺序排列。

①初始状态为只有n个顶点而无边的非连通图T=(V,{}),图中每个顶点自成一个连通分量。

②在E中选择权值最小的边,若该边依附的顶点落在T中不同的连通分量上(不形成回路),则将此边加入T中,否则舍去此边而选择下一条权值最小的边。

③重复②,直至T中所有顶点都在同一连通分量上为止。

【算法步骤】

①将数组Edge中的元素按权值从小到大排序。

②依次查看数组Edge中的边,循环执行以下操作:

依次从排好序的数组Edge中选出一条边(U1,U2);

在Vexset中分别查找v1和v2所在的连通分量vs1和vs2进行判断:如果vs1和vs2不等,表明所选的两个顶点分属不同的连通分量,输出此边,并合并vs1,和vs2两个连通分量;

如果vs1和vs2相等,表明所选的两个顶点属于同一个连通分量,舍去此边而选择下一条权值最小的边。

个人思考

①慢慢把边都加入,随着程序的进行,边越加越多,被合并的越多,即被连通的顶点不止一个,而是集合。

②Vexset[i]:标识各个顶点所在的连通分量。初始时Vexset[i]=i,表示各个顶点自成一个连通分量。

这个数组非常重要,是程序的关键。数组下标 代表不同顶点,不同下标的数组空间存储的值代表该顶点所在的连通分量。连通分量相同表明这些顶点相连通不相同则不连通。(此处、你要理解数组的含义,使用方法以及连通分量的概念)

//Kruskal算法
//加边法
int arcnum=100;
struct  KNode
{VerTexType Head;//边的始点VerTexType Tail;//边的终点ArcType  lowcost;
}Edge[arcnum];int Vexset[MVNum];//标识各个顶点所属的连通分量   即表明谁与谁联通,谁与谁不连通void Sort(struct KNode a[], int n)//从小到大排序,这里采用了改进版的冒泡排序
{int i, j,flag=1,m=n-1;//flag用来标记某一趟排序是否发生交换struct KNode temp;while((m>0)&&(flag==1)){flag=0;for (j = 1; j <=m; j++){if (a[j]>a[j + 1]){flag=1;temp = a[j];a[j] = a[j + 1];a[j + 1] = temp;}--m;}}}void MiniSpanTree_Kruskal(AMGraph G)
{int i,j,v1,v2,vs1,vs2;Sort(Edge,arcnum);//按权值从小到大排序for(i=0;i<G.vexnum;i++)//表示各个顶点自成一个连通分量{Vexset[i]=i;}for(i=0;i<G.arcnum;i++)//依次查看数组Edge中的边{//边v1=LocateVex(G,Edge[i].Head);//v1为边的始点Head的下标v2=LocateVex(G,Edge[i].Tail);//v1为边的终点Tail的下标//所在连通分量vs1=Vexset[v1];//获取边Edge[i]的始点所在的连通分量vs1vs2=Vexset[v2];//获取边Edge[i]的终点所在的连通分量vs2if(vs1!=vs2)//所加入的边起作用  边的两个顶点分属于不同的连通分量{cout<<Edge[i].Head<<Edge[i].Tail;//输出此边for(j=0;j<G.vexnum;j++)//合并vs1和vs2两个连通分量,即两个集合统一编号{if(Vexset[j]==vs2)  Vexset[j]=vs1;//集合编号为vs2的都改为vs1}}}}

完整代码

#include<iostream>
#include<new>
using namespace std;typedef int Status;
#define  OK 1
#define ERROR 0
#define OVERFLOW -1#define MAXSIZE 100#define MaxInt 32767 //不相通
#define MVNum 100 //最大顶点个数
typedef char VerTexType;
typedef int ArcType;//权值类型bool visited[MVNum];typedef int QElemType;
typedef struct
{QElemType data[MAXSIZE];int front; /*头指针*/int rear; /*尾指针,若队列不空,指向队列元素的下一个位置*/
}SqQueue;SqQueue* Q;//链表  栈  队列  都是指针类型的?门牌号?不是房间吗?//邻接矩阵,二维数组
typedef struct
{VerTexType vexs[MVNum];//顶点 行 列 存储ArcType arcs[MVNum][MVNum];//存储int vexnum, arcnum;//图的当前点数和边数}AMGraph;//邻接表
typedef struct
{}OtherInfo;typedef struct ArcNode//边节点
{int adjvex;struct ArcNode* nextarc;OtherInfo info;//和边相关的信息
}ArcNode;typedef struct VNode//表头结点
{VerTexType data;ArcNode* firstarc;
}VNode, AdjList[MVNum];typedef struct//邻接表
{AdjList vertices;//表头结点表int vexnum, arcnum;//图的当前点数和边数}ALGraph;int LocateVex_UDN(AMGraph G, VerTexType u)//如存在,返回顶点在图中的位置  //监视哨的方式查找
{//VerTexType vexs[MVNum], vexnumint i;for (i = 0; i < G.vexnum; i++){if (G.vexs[i] == u)  return i;}return 0;}Status CreateUDN(AMGraph& G)
{int i, j, k;VerTexType v1, v2;ArcType w;cout << "请输入当前的顶点数和边数" << endl;cin >> G.vexnum >> G.arcnum;//初始化顶点和边(权值)//有权值 有关系 有边for (i = 0; i < G.vexnum; i++){cout << "请输入第" << i + 1 << "个顶点的数据" << endl;cin >> G.vexs[i];}for (i = 0; i < G.vexnum; i++)//矩阵:矩阵的行和列是顶点个数{for (j = 0; j < G.vexnum; j++){G.arcs[i][j] = MaxInt;}}//构造矩阵  有多少条边构造多少次(初始化的时候已经给边赋初值了,//那么接下来我们只需要把”有关系的边“赋给正确的权值(覆盖初值即可),”没有关系的边“,就是不相通,不需要赋值,初始值就表明他们不相通for (k = 0; k < G.arcnum; ++k){cout << "请输入要创建的第" << k + 1 << "条边和它的权值" << endl;cin >> v1 >> v2 >> w;//i = LocateVex_UDN(G, v1);j = LocateVex_UDN(G, v2);G.arcs[i][j] = w;//图  1 ,网  wG.arcs[j][i] = G.arcs[i][j];//无向图  对称}return OK;
}void PrintAMGraph(AMGraph G)//显示一个矩阵。。。(带权)
{for (int i = 0; i < G.vexnum;i++){for(int j=0;j<G.vexnum;j++){cout << G.arcnum[i][j] << " ";}cout << endl;}
}//邻接表创建int LocateVex_UDG(ALGraph G, VerTexType u)//如存在,返回顶点在图中的位置  在顶点中寻找比对  返回顶点的下标(无论是邻接表还是邻接矩阵,顶点都存储在数组中。。。)
{//G.vertices[i].dataint i;for (i = 0; i < G.vexnum; i++){if (G.vertices[i].data == u)  return i;}return 0;
}Status CreateUDG(ALGraph& G)
{int i, j, k;VerTexType v1, v2;ArcNode* p1;ArcNode* p2;cout << "请输入当前的顶点数和边数" << endl;cin >> G.vexnum >> G.arcnum;//初始化for (i = 0; i < G.vexnum; i++){cout << "请输入第" << i + 1 << "个顶点的数据" << endl;cin >> G.vertices[i].data;G.vertices[i].firstarc = NULL;}//构造for (k = 0; k < G.arcnum; ++k){cout << "请输入要创建的第" << k + 1 << "条边" << endl;cin >> v1 >> v2;i = LocateVex_UDG(G, v1);j = LocateVex_UDG(G, v2);//两个顶点一条边  在顶点数组里面寻找。。表头数组vertices返回下标int//jp1 = new ArcNode;//新节点p1->adjvex = j;p1->nextarc = G.vertices[i].firstarc;//头插法G.vertices[i].firstarc = p1;//i  对称p2 = new ArcNode;p2->adjvex = i;p2->nextarc = G.vertices[j].firstarc;G.vertices[j].firstarc = p2;}return OK;
}void PrintALGraph(ALGraph G)//显示一个数表。。。一行是一个单链表。。。
{ArcNode* p;ArcNode* q;cout << "提示:接下来的每一行,第一个为顶点信息,后面的为该顶点的邻接点的信息" << endl;for(int i=0;i<G.vertices;i++){p = new ArcNode;p = G.vertices[i].firstarc;//第一个边结点cout << G.vertices[i].data << " ";while(NULL!=p){cout << G.vertices[p->adjvex].data << "(该边)的信息为" << p->info << " ";p = p->nextarc;}cout << endl;}
}// int main()//如需要。。。
// {
//     cout << "邻接矩阵的创建" << endl;
//     AMGraph G;
//     CreateUDN(G);//     cout << "邻接矩阵创建完成,显示邻接矩阵:" << endl;
//     PrintAMGraph(G);//     cout << "邻接表的创建" << endl;
//     ALGraph H;
//     CreateUDG(H);
//     cout << "邻接表创建完成,显示邻接表:" << endl;
//     PrintALGraph(H);//     return 0;
// }//最小生成树算法(Prim算法  Kruskal算法)//Kruskal算法
int arcnum=100;
struct  KNode
{VerTexType Head;//边的始点VerTexType Tail;//边的终点ArcType  lowcost;
}Edge[arcnum];int Vexset[MVNum];void Sort(struct KNode a[], int n)
{int i, j;struct KNode temp;for (i = 0; i < n - 1; i++){for (j = 0; j < n - i - 1; j++){if (a[j]>a[j + 1]){temp = a[j];a[j] = a[j + 1];a[j + 1] = temp;}}}}void MiniSpanTree_Kruskal(AMGraph G)
{int i,j,v1,v2,vs1,vs2;Sort(Edge,arcnum);for(i=0;i<G.vexnum;i++){Vexset[i]=i;}for(i=0;i<G.arcnum;i++){v1=LocateVex(G,Edge[i].Head);v2=LocateVex(G,Edge[i].Tail);vs1=Vexset[v1];vs2=Vexset[v2];if(vs1!=vs2){cout<<Edge[i].Head<<Edge[i].Tail;for(j=0;j<G.vexnum;j++){if(Vexset[j]==vs2)  Vexset[j]=vs1;}}}}

编者语

小新还在努力,如有不足请指出。

最小生成树(Prim算法,Kruskal算法)相关推荐

  1. 最小生成树(Prim、Kruskal)算法,秒懂!

    前言 在数据结构与算法的图论中,(生成)最小生成树算法是一种常用并且和生活贴切比较近的一种算法.但是可能很多人对概念不是很清楚,什么是最小生成树? 一个有 n 个结点的连通图的生成树是原图的极小连通子 ...

  2. 生成树的概念,最小生成树Prim算法 Kruskal算法

    求解最小生成树可以用Prim算法 Kruskal算法

  3. 数据结构(六):图的概念、存储方式、基本操作、最小生成树、最短路径、有向无环图、关键路径 | Prim、Kruskal算法 | BFS、Dijkstra、Floyd算法 | 拓扑排序 | 求关键路径

    文章目录 第六章 图 一.图 (一)图的定义 (二)图逻辑结构的应用 (三)无向图.有向图 (四)简单图.多重图 (五)顶点的度.入度.出度 (六)顶点-顶点的关系描述 (七)连通图.强连通图 (八) ...

  4. dijkstra算法_Python实现图的经典DFS、BFS、Dijkstra、Floyd、Prim、Kruskal算法

    讲在前面的话,图的算法太多,理论知识肯定一篇文章讲不完,关于理论知识大家可以参考教材Sedgewick的<算法>或reference的链接,本文主要还是想在一篇文章中记录六种算法的Pyth ...

  5. 最小生成树原理及Kruskal算法的js实现

    1. 生成树和最小生成树的概念 设图G(V,E)连通,则 生成树:包含图G(V,E)中的所有节点,及|V|-1条边的连通图,一个图的生成树可以有多颗 最小生成树:最小权重生成树,在生成树的概念上加一个 ...

  6. dfs时间复杂度_Python实现图的经典DFS、BFS、Dijkstra、Floyd、Prim、Kruskal算法

    讲在前面的话,图的算法太多,理论知识肯定一篇文章讲不完,关于理论知识大家可以参考教材Sedgewick的<算法>或reference的链接,本文主要还是想在一篇文章中记录六种算法的Pyth ...

  7. Prim和Kruskal算法应用----城市水管连接

    Prim和Kruskal算法应用----城市水管连接 问题描述: Description: 现在有n个城镇,编号为1, 2, 3, 4-n.他们之间有m条互通的道路,每条道路有相应的长度,现在基于这些 ...

  8. 【图解】Prim和Kruskal算法的区别

    [贪心]Prim和Kruskal算法的区别 Kruskal算法和Prim算法的优劣 Kruskal算法,相较于Prim算法是基于点的操作,Kruskal算法是基于边的操作,思想也比Prim简单,更容易 ...

  9. 可视化最小生成树Prim、Kruskal

    代码下载链接:(41条消息) 可视化最小生成树Kruskal,DEV配EGE-C文档类资源-CSDN文库 代码下载链接:(41条消息) 可视化最小生成树Prim,DEV配EGE-C文档类资源-CSDN ...

  10. 【数据结构】最小生成树 Prim算法 Kruskal算法

    最小生成树应用场景: 假设以下场景,有一块木板,板上钉上一些钉子,这些钉子可以由一些细绳连接起来.假设每个钉子可以通过一根或者多根细绳连接起来,那么一定存在这样得情况,即用最少的细绳把所有的钉子连接起 ...

最新文章

  1. 2019牛客网暑假多校训练第四场 K —number
  2. oracle imp 错误604,oracle imp导入出错
  3. java中toarray()的 用法_java容器中toArray的用法
  4. 在Shell中使用alias
  5. 神奇的幻方2015提高组d1t1
  6. 在虚拟机中安装linux6,如何在vmvare中安装redhat linux6虚拟机
  7. Python基础知识:字符串
  8. 面试准备每日系列:计算机底层之并发编程(二)缓存行、一致性协议、伪共享、disruptor、CAS等待
  9. C++STL查找,lower_bound()函数和upper_bound()函数的区别
  10. 为ASP.NET封装的SQL数据库访问类(转)
  11. python快速查城市的地理坐标
  12. WPS文字常用排版快捷键
  13. 《腾讯传》三、马化腾与“幸运女神”那些令人脸红的事
  14. 模数转换(A/D)与数模转换(D/A)
  15. 植物大战僵尸修改金币以及关卡
  16. 4246. 【五校联考6day2】san
  17. 关于程序员失眠的解决方案
  18. 二级联动 三级联动 多级联动 无限级联动下拉列表或菜单专题
  19. 究竟是什么挡住了普通人的逆袭道路
  20. eclipse开发安卓android程序环境

热门文章

  1. 计算机组成原理实验箱D7,计算机组成原理实验(实验箱).doc
  2. oracle rownum执行时机,ORACLE数据库中的ROWNUM和ORDERBY执行顺序
  3. SpriteAtlas
  4. ThreadLocal之强、弱、软、虚引用
  5. 6,xilinx 7系列FPGA理论篇——时钟操作法则
  6. 最贵服务器多少钱_买一台服务器需要多少钱?
  7. 如何在 ggplot2 中更改字体大小
  8. kali安装teamviewer
  9. gdb命令行调试相关操作
  10. Cmake编译配置opencv3.3+contrib+cuda7.5