问题提出:
一个公司计划建立一个通信网络来连接它的一个计算机中心。可以用租用的电话线连接这些中心的任何一对。应当妊娠瘙痒哪些连接,以便保证在任何两个计算机中心之间都有通路,且网络的总成本最小?可以用下较长所示的带权图为这个问题建模,其中顶点表示计算机中心,边表示可能租用的电话线,边上的权是边所表示的电话线的月租费。通过找出一棵生成树,使得这棵树的各边的权之和为最小,就可以解决这个问题。这样的生成树称为最小生成树。
最小生成树定义:
在一个具有V个节点的连通无向图中,找到一个子图,该子图包含原图的所有节点和部分连接边,且不能形成回路,同时子图边的权值总和最小。
最小生成树的算法:
根据对安全边的不同规则,有两种算法可以生成最小生成树。即Kruskal算法和Prim算法。
1.普林算法(Prim)
该算法所具有的一个性质是集合A中的边总是构成一棵树,而不是森林。这棵树从任意一个根节点开始,一直长大到覆盖V中所有的节点为止。算法每一步在链接集合A和A之外的节点的边,选择一条轻量级边加入到集合A中。
步骤:
设是带权值的连通图,A是上最小生成树中边的集合:
(1)初始令,(), A=NULL
(2)在所有,的边中,找一条权值最小的边
(3)将并入集合A,同时并入
(4)重复上述操作直至为止,则为的最小生成树
判断规则:在所有,的边中,找一条权值最小的边,连接该边,但不能形成回路;
设是带权值的连通图,A是上最小生成树中边的集合:
(1)初始令,(), A=NULL
(2)在所有,的边中,找一条权值最小的边
(3)将并入集合A,同时并入
(4)重复上述操作直至为止,则为的最小生成树
判断规则:在所有,的边中,找一条权值最小的边,连接该边,但不能形成回路;
举例:
该图为原始图
第一步:选取一个初始节点a为根节点,并找到权值为最小的边,即为ab;
第二步:在所有,的边中,找一条权值最小的边;即权值最小边为bc或者ah;这里选取bc
第三步:在所有,的边中,找一条权值最小的边;即选择ci;
第四步:在所有,的边中,找一条权值最小的边;即选择cf;
第五步:在所有,的边中,找一条权值最小的边;即选择fg;
第六步:在所有,的边中,找一条权值最小的边;即选择gh;
第七步:在所有,的边中,找一条权值最小的边;必须保证不能形成回路,即选择cd;
最后一步:在所有,的边中,找一条权值最小的边;必须保证不能形成回路,即选择de;由于树包含了所有节点,且满足,则在此终止,为的最小生成树。
很简单的地说,就是建立一棵树T,和不在树中的顶点集合L,找出两者之间最短的距离的点,加入到树中,直到所有点加进去为止。
//d为图的邻近矩阵,p为所生成树的邻近矩阵,
//n为图中点的个数
void Prim(int **d,int **p,int n)
{
int i,j,tmp,iT,iL;
vector<int> T,L;//T是要生成的树,L为还没生成树的顶点
for(i=0;i<n;i++)
L.push_back(i);//给L赋初始
while(L.size()){//当L为空时,即已经生成树完毕
tmp=d[0][0];iT=iL=0;//以第一个点作为初始点
for(i=0;i<T.size();i++){//找出树与非树顶点之间距离最小的点,并记录
for(j=L.size()-1;j>=0;j--){
if(tmp>=d[T[i]][L[j]]){
tmp=d[T[i]][L[j]];
iT=i;iL=j;
}
}
}
cout<<L[iL]<<" ";
T.push_back(L[iL]);//将找到的点添加到树中
p[T[iT]][L[iL]]=1;
L.erase(L.begin()+iL);//且从剩余的点中删除
}
cout<<endl;
}
时间复杂度: