最小生成树(Prim算法)
算法简介:
普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点,且其所有边的权值之和亦为最小。该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克发现;并在1957年由美国计算机科学家罗伯特·普里姆独立发现;1959年,艾兹格·迪科斯彻再次发现了该算法。因此,在某些场合,普里姆算法又被称为DJP算法、亚尔尼克算法或普里姆-亚尔尼克算法。
算法讲解:
该算法为求出图的最小生成树,首先我们需要两个数组来完成这个任务。一个数组用来保存当前节点的状态,另一个数组则用来保存到达该节点的最短距离。假设我们要求的这个图保存在一个二位数组里,保存节点状态的数组命名为vset,保存最短距离的数组命名为lowcast。
执行过程:
- 首先进行两个数组的初始化工作,将数组状态vset全部赋值为0(0代表当前节点未被并入最小生成树中,1代表当前节点已经被并入到最小生成树中)。将lowcast赋值为起始节点到其余各个节点的距离。
- 将起始节点的值置为1(vset[v0]=1),紧接着进行n-1(共有n个节点)次循环查找,知道求出最小生成树为止。这一步的每一次循环过程如下:
1)求出lowcast的最小值,并把最小值指向的节点并入到生成树中。
2)利用刚刚并入生成树的节点更新lowcast状态,得到一个最新的lowcast最短距离的数组。
代码实现:
void Prim(Griph g, int v0){
//定义vset和lowcast两个数组
int []vset = new vset[g.n];
int []lowcast = new lowcast[g.n];
int k,v;
//初始化lowcast数组,并将vset全部置为0
for(int i=0; i<g.n; i++){
lowcast[i] = g.edge[v0][i];
vset[i] = 0;
}
//首先访问v0
vset[v0] = 1;
//求最小生成树核心过程
for(int i=0; i<g.n-1; i++){
//暂时将最小值定为一个大数字
int min = 65535;
for(int j=0; j<g.n; j++){
if(vset[j]==0 && lowcast[j] < min){
min = lowcast[j];
k = j;
}
}
vset[k] = 1;
v = k;
//根据新节点的线索,更新lowcast
for(int j=0 ;j<g.n; j++){
if(vset[j] == 0 && g.edge[v][j] < lowcast[j]){
lowcast[j] = g.edge[v][j];
}
}
}
}
时间复杂度:
我们可以看到普里姆算法的核心为一个双层循环,双层循环内有两个并列的单层循环。第一个循环一定会执行,但是第二个循环不一定会执行。第一个循环中min=lowcast[j]为一个元操作,其每次循环执行次数为n。外层循环共执行n-1次,由此可见普里姆算法的时间复杂度为O(n^2)。普里姆算法的复杂度与节点个数有关而与边数无关。由此可见,普里姆算法适用于边数较多的稠密图。