关于图的几个概念定义:
- 连通图:在无向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该无向图为连通图。
- 强连通图:在有向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该有向图为强连通图。
- 连通网:在连通图中,若图的边具有一定的意义,每一条边都对应着一个数,称为权;权代表着连接连个顶点的代价,称这种连通图叫做连通网。
- 生成树:一个连通图的生成树是指一个连通子图,它含有图中全部n个顶点,但只有足以构成一棵树的n-1条边。一颗有n个顶点的生成树有且仅有n-1条边,如果生成树中再添加一条边,则必定成环。
- 最小生成树:在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树。
下面我们总结第一种生成最小生成树的算法
一、Prim算法
这个方法的原理就是:
1.将原图划分成“点-边-点”的边集合,并且将所有的点建立离散点集合。并建立生成树的边集合和点集合。
2.找到原图的边集合中权值最小的边当作“种子”,将边加入生成树的边,并在离散点中找到两个端点,删除,加入生成树的点集合。
3.遍历原图的边,找到符合“一端在生成树的点集合,一端在离散的点集合,并且权值最小”的边。将该边不在生成树的一端从离散点集合删除,加入到生成树点集合。将该边加入到生成树的边集合,从原图的边集合删除。
首先
(1)声明边类型,边数组类型,点数组类型
struct 边类型{
int p0,p1;
int k;
边类型(int p0=0, int p1=0, int k=0):p0(p0),p1(p1),k(k){ }
};
struct 图的边类型{
边类型 *边数组;
const int Size;
int n;
图的边类型(int Size):Size(Size),n(0){ 边数组 = new 边类型[Size]; }
~图的边类型( ){ delete [ ]边数组; }
int MinNdx( ){
int minX=0;
for(int i=0; i<n; i++){
if(边数组[i].k < 边数组[minX].k)
minX = i;
}
return minX;
}
边类型 Delete(int Ndx){
边类型 边 = 边数组[Ndx];
for(int i=Ndx+1; i<n; i++){
边数组[i-1] = 边数组[i];
}
n = n-1;
return 边;
}
void Append(边类型 边){
边数组[n] = 边;
n++;
}
};
struct 图的点类型{
const int Size;
int *点数组;
int n;
图的点类型(int Size):Size(Size),n(0){ 点数组 = new int[Size]; }
~图的点类型( ){ delete [ ]点数组; }
int Delete(int Ndx)// 删除下标为Ndx的点(int),并且把删除的点(int)return 回去
{
int 点 = 点数组[Ndx];
for (int i=Ndx + 1;i < n;i++) {
点数组[i - 1] = 点数组[i];
}
n = n - 1;
return 点;
}
void Append(int 点) {
点数组[n] = 点;
n++;
}
bool IN(int 点, int &Ndx){
for(int i=0; i<n; i++){
if (点 == 点数组[i]) {
Ndx = i;
return true;
}
}
return false;
}
};
(2)具体操作同上,在程序中也做了注释
int _tmain(int argc, _TCHAR* argv[])
{
图的点类型 生成树的点(6), 离散的点(6);
图的边类型 原图的边(10),生成树的边(10);
for(int i=1; i<=6; i++)
离散的点.Append(i);
原图的边.Append(边类型(1,2,6)); 原图的边.Append(边类型(1,3,1)); 原图的边.Append(边类型(1,4,5));
原图的边.Append(边类型(2,3,5)); 原图的边.Append(边类型(3,4,5)); 原图的边.Append(边类型(2,5,3));
原图的边.Append(边类型(3,5,6)); 原图的边.Append(边类型(3,6,4)); 原图的边.Append(边类型(4,6,2));
原图的边.Append(边类型(5,6,6));
边类型 边(0,0,0);
//1、找到权值最小的边,把它的两个端点加入到生成树的点
// (1)、在原图边的集里找到权值最小的边A,从原图的边里面把它删出来
边 = 原图的边.Delete(原图的边.MinNdx( ));
// (2)、在离散的点里找到A的两个端点,把它们删出来,加到生成树的点里
int X_Ndx,Y_Ndx;
离散的点.IN(边.p0, X_Ndx);
离散的点.Delete(X_Ndx);
生成树的点.Append(边.p0);
离散的点.IN(边.p1, Y_Ndx); 离散的点.Delete(Y_Ndx); 生成树的点.Append(边.p1);
// (3)、把A添加到生成树的边
生成树的边.Append(边);
//2、
while(离散的点.n > 0){
int NdxTemp;
int MinNdx = -1;
for(int i=0; i<原图的边.n; i++){
//if(原图的边.边数组[i],一端在生成树的点里,另一端在离散的点里){
边 = 原图的边.边数组[i];
if(生成树的点.IN(边.p0,NdxTemp)!=生成树的点.IN(边.p1,NdxTemp)){
if(MinNdx == -1) MinNdx = i;
if(原图的边.边数组[i].k < 原图的边.边数组[MinNdx].k) MinNdx = i;
}
}
//把原图的边.边数组[MinNdx]的不在生成树上的端点移动到生成树上(即从离散的点里删出来,追加到生成树的点)
边 = 原图的边.边数组[MinNdx];
if (生成树的点.IN(边.p0, NdxTemp))
{
生成树的点.Append(边.p1);
离散的点.Delete(边.p1);
}
else {
生成树的点.Append(边.p0);
离散的点.Delete(边.p0);
}
//把下标MinNdx的边从原图的边删出来,追加到生成树的边里
原图的边.Delete(MinNdx); 生成树的边.Append(边);
}
return 0;
}
这个方法主要是以一个最小权值边最为种子,接着找对于种子来说** “可以连通的,并且权值最小的边” **进行连通。
希望对你们算法理解有所帮助!!!