您的位置:首页 > 编程语言 > C语言/C++

C++——算法基础之最小生成树(Prim 和 Kruskal)

2016-09-26 19:59 323 查看
今天,大白跟大家分享一下最小生成树算法。

最小生成树算法有两种:Prim算法 和 Kruskal算法

Prim算法:以顶点为基础的向外扩张,从某一顶点(A)开始,依次寻找未知顶点中(未加入最小生成树的顶点)到已知顶点的最小权值的顶点。直到找到包含(A)的最大连通图中的所有顶点为止。(由此可见,从不同的顶点开始,获得的最小生成树也就不同,当把图中的每个顶点都建立最小生成树后,就获得了所谓的生成森林)。

所以,其适用于稠密图(顶点少于边)。

图例说明不可选可选已选(Vnew)
 



此为原始的加权连通图。每条边一侧的数字代表其权值。---


顶点D被任意选为起始点。顶点ABEF通过单条边与D相连。A是距离D最近的顶点,因此将A及对应边AD以高亮表示。C, GA, B, E, FD
 



下一个顶点为距离DA最近的顶点。BD为9,距A为7,E为15,F为6。因此,FDA最近,因此将顶点F与相应边DF以高亮表示。C, GB, E, FA, D

算法继续重复上面的步骤。距离A为7的顶点B被高亮表示。CB, E, GA, D, F
 



在当前情况下,可以在CEG间进行选择。CB为8,EB为7,GF为11。E最近,因此将顶点E与相应边BE高亮表示。C, E, GA, D, F, B
 



这里,可供选择的顶点只有CGCE为5,GE为9,故选取C,并与边EC一同高亮表示。C, GA, D, F, B, E


顶点G是唯一剩下的顶点,它距F为11,距E为9,E最近,故高亮表示G及相应边EGGA, D, F, B, E, C


现在,所有顶点均已被选取,图中绿色部分即为连通图的最小生成树。在此例中,最小生成树的权值之和为39。A, D, F, B, E, C, G
(此图是盗用他人的,别的好东西有助于我们更好的理解,所以就利用利用了哈^_^)

Kruskal算法:以边为基础的向内收拢,首先将所有边进行升序排序,然后从最小边开始,判断当前边的顶点是否已经在最小生成树中,若是,则舍弃该边继续,否则,将该边加入到最小生成树中。直到所有边都遍历完成。(在判断时,用到一个并查集)。(我暂时获得的是生成森林喔*_*)

其适用于稀疏图(即边较少)。

图例描述:


首先第一步,我们有一张图Graph,有若干点和边 



 

将所有的边的长度排序,用排序的结果作为我们选择边的依据。这里再次体现了贪心算法的思想。资源排序,对局部最优的资源进行选择,排序完成后,我们率先选择了边AD。这样我们的图就变成了右图

 

 

 


在剩下的变中寻找。我们找到了CE。这里边的权重也是5


依次类推我们找到了6,7,7,即DF,AB,BE。



下面继续选择, BC或者EF尽管现在长度为8的边是最小的未选择的边。但是现在他们已经连通了(对于BC可以通过CE,EB来连接,类似的EF可以通过EB,BA,AD,DF来接连)。所以不需要选择他们。类似的BD也已经连通了(这里上图的连通线用红色表示了)。

最后就剩下EG和FG了。当然我们选择了EG。最后成功的图就是右:

 
(此图是盗用他人的,别的好东西有助于我们更好的理解,所以就利用利用了哈^_^)

代码如下:
Prim:

#include <iostream>
using namespace std;

#define MaxNum 20
#define MaxEdge 190
#define MAX 32767

struct MGraph
{
char Vertex[MaxNum];
int Edge[MaxNum][MaxNum];
int n, e;
};

struct closedge			//辅助数组,存储从集合U到集合V-U中各顶点的权值最小边
{
int adjvex;
int lowcost;
}closedge[MaxNum];

int locate_vex (MGraph *G, char ver)
{
for(int i = 0; i < G->n; i++)
{
if(G->Vertex[i] == ver)
{
return i;
}
}
return -1;
}

void creatgraph (MGraph *G)
{
int i, j, k, cost;
char vex1, vex2;
cout << "请输入网的顶点名称:";
for(i = 0; i < G->n; i++)
{
cin >> G->Vertex[i];
}
for(i = 0; i < G->n; i++)
{
for(j = 0; j < G->n; j++)
{
G->Edge[i][j] = MAX;
}
}
cout << "请输入网中每条边所依附的顶点信息:";
for(k = 0; k < G->e; k++)
{
cout << "\n请输入第 " << k + 1 << " 条边的第一个顶点信息:";
cin >> vex1;
cout << "请输入第 " << k + 1 << " 条边的第二个顶点信息:";
cin >> vex2;
i = locate_vex (G, vex1);
j = locate_vex (G, vex2);
cout << "请输入第 " << k + 1 << " 条边的权值:";
cin >> cost;
G->Edge[i][j] = G->Edge[j][i] = cost;
}
}

void Prim (MGraph *G)
{
int i, j, k;
int mincost;
char vex;
cout << "\n请输入构造最小生成树的起始顶点:";
cin >> vex;
k = locate_vex (G, vex);
cout << "其余顶点为:";
for(i = 0; i < G->n; i++)
{
if(i != k)
{
closedge[i].adjvex = k;
closedge[i].lowcost = G->Edge[k][i];
}
}
closedge[k].lowcost = 0;
closedge[k].adjvex = k;
for(i = 0; i < G->n - 1; i++)
{
mincost = MAX;
j = 0;
k = 0;
while(j < G->n)
{
if(closedge[j].lowcost < mincost && closedge[j].lowcost != 0)
{
mincost = closedge[j].lowcost;
k = j;
}
j++;
}
cout << "\n顶点 " << G->Vertex[k] << " 边的权值为:" << mincost;
closedge[k].lowcost = 0;
for(j = 0; j < G->n; j++)
{
if(G->Edge[k][j] < closedge[j].lowcost)
{
closedge[j].lowcost = G->Edge[k][j];
closedge[j].adjvex = k;
}
}
}
}

void outputgraph (MGraph *G)
{
for(int i = 0; i < G->n; i++)
{
cout << "\t" << G->Vertex[i];
}
cout << endl << endl;
for(int i = 0; i < G->n; i++)
{
cout << G->Vertex[i];
for(int j = 0; j < G->n; j++)
{
if(G->Edge[i][j] == MAX)
{
cout << "\t" << "∞";
}
else
{
cout << "\t" << G->Edge[i][j];
}
}
cout << endl << endl;
}
}

int main28 ()
{
MGraph mg;
cout << "以 Prim 算法构造无向连通网的最小生成树......";
cout << "\n请输入连通网的顶点数目和边数,以空格‘ ’分隔开:";
cin >> mg.n >> mg.e;
creatgraph (&mg);
outputgraph (&mg);
Prim (&mg);
return 0;
}


Kruskal:

#include <iostream>
using namespace std;

#define INF 10000
#define MAX 20
#define MAXE 100

struct MyGraph
{
int vNum;
int eNum;
int vexs[MAX];
int edges[MAX][MAX];
};

struct edgeArray
{
int v1, v2, w;
};

edgeArray edgearr[MAX];

int getVerIndex (int ver)
{
return ver - 1;
}

void createMygGraph (MyGraph *mg)
{
cout << "请输入图的顶点数和边数:";
cin >> mg->vNum >> mg->eNum;
cout << "顶点:";
for(int i = 0; i < mg->vNum; i++)
{
cin >> mg->vexs[i];
}
for(int i = 0; i < mg->vNum; i++)
{
for(int j = 0; j < mg->vNum; j++)
{
mg->edges[i][j] = INF;
}
}
int v1, v2, w;
for(int i = 0; i < mg->eNum; i++)
{
cout << "边" << i + 1 << " 的顶点1,顶点2,权值:";
cin >> v1 >> v2 >> w;
int index1 = getVerIndex (v1);
int index2 = getVerIndex (v2);
mg->edges[index1][index2] = mg->edges[index2][index1] = w;
edgearr[i].v1 = v1;
edgearr[i].v2 = v2;
edgearr[i].w = w;
}
}

void sortEdge (MyGraph * mg)
{
for(int i = 1; i < mg->eNum; i++)
{
bool flag = true;
for(int j = 0; j < mg->eNum - i; j++)
{
if(edgearr[j].w > edgearr[j + 1].w)
{
edgeArray temp;
temp.v1 = edgearr[j].v1;
temp.v2 = edgearr[j].v2;
temp.w = edgearr[j].w;

edgearr[j].v1 = edgearr[j + 1].v1;
edgearr[j].v2 = edgearr[j + 1].v2;
edgearr[j].w = edgearr[j + 1].w;

edgearr[j + 1].v1 = temp.v1;
edgearr[j + 1].v2 = temp.v2;
edgearr[j + 1].w = temp.w;

flag = false;
}
}
if(flag)
{
break;
}
}
}

int father[MAX];

int find (int x)
{
if(x != father[x])
{
father[x] = find (father[x]);
}
return father[x];
}

void Kruskal (MyGraph *mg)
{
cout << "最小生成树为:\n";
int j = 0;
for(int k = 0; k < mg->vNum; k++)
{
father[k] = k;
}
while(j < mg->eNum)
{
int v1 = edgearr[j].v1;
int v2 = edgearr[j].v2;
v1 = find (v1);
v2 = find (v2);
if(v1 != v2)
{
cout << "<" << edgearr[j].v1 << "," << edgearr[j].v2 << "> : " << edgearr[j].w << "\t";
father[v1] = v2;
}
j++;
}
cout << endl;
}

void printGraph (MyGraph *mg)
{
for(int i = 0; i < mg->vNum; i++)
{
cout << "\t" << mg->vexs[i];
}
cout << endl << endl;
for(int i = 0; i < mg->vNum; i++)
{
cout << mg->vexs[i];
for(int j = 0; j < mg->vNum; j++)
{
if(INF == mg->edges[i][j])
{
cout << '\t' << "∞";
}
else
{
cout << '\t' << mg->edges[i][j];
}
}
cout << endl << endl;
}
}

int main()
{
MyGraph mg;
createMygGraph (&mg);
printGraph (&mg);
sortEdge (&mg);
Kruskal (&mg);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ 算法 kruskal prim