C++——算法基础之最小生成树(Prim 和 Kruskal)
2016-09-26 19:59
323 查看
今天,大白跟大家分享一下最小生成树算法。
最小生成树算法有两种:Prim算法 和 Kruskal算法
Prim算法:以顶点为基础的向外扩张,从某一顶点(A)开始,依次寻找未知顶点中(未加入最小生成树的顶点)到已知顶点的最小权值的顶点。直到找到包含(A)的最大连通图中的所有顶点为止。(由此可见,从不同的顶点开始,获得的最小生成树也就不同,当把图中的每个顶点都建立最小生成树后,就获得了所谓的生成森林)。
所以,其适用于稠密图(顶点少于边)。
(此图是盗用他人的,别的好东西有助于我们更好的理解,所以就利用利用了哈^_^)
Kruskal算法:以边为基础的向内收拢,首先将所有边进行升序排序,然后从最小边开始,判断当前边的顶点是否已经在最小生成树中,若是,则舍弃该边继续,否则,将该边加入到最小生成树中。直到所有边都遍历完成。(在判断时,用到一个并查集)。(我暂时获得的是生成森林喔*_*)
其适用于稀疏图(即边较少)。
图例描述:
![](http://pic002.cnblogs.com/images/2012/426620/2012073015215729.jpg)
首先第一步,我们有一张图Graph,有若干点和边
![](http://pic002.cnblogs.com/images/2012/426620/2012073015234045.jpg)
将所有的边的长度排序,用排序的结果作为我们选择边的依据。这里再次体现了贪心算法的思想。资源排序,对局部最优的资源进行选择,排序完成后,我们率先选择了边AD。这样我们的图就变成了右图
![](http://pic002.cnblogs.com/images/2012/426620/2012073015313195.jpg)
在剩下的变中寻找。我们找到了CE。这里边的权重也是5
![](http://pic002.cnblogs.com/images/2012/426620/2012073015332154.jpg)
依次类推我们找到了6,7,7,即DF,AB,BE。
![](http://pic002.cnblogs.com/images/2012/426620/2012073015361536.jpg)
下面继续选择, BC或者EF尽管现在长度为8的边是最小的未选择的边。但是现在他们已经连通了(对于BC可以通过CE,EB来连接,类似的EF可以通过EB,BA,AD,DF来接连)。所以不需要选择他们。类似的BD也已经连通了(这里上图的连通线用红色表示了)。
最后就剩下EG和FG了。当然我们选择了EG。最后成功的图就是右:
(此图是盗用他人的,别的好东西有助于我们更好的理解,所以就利用利用了哈^_^)
代码如下:
Prim:
Kruskal:
最小生成树算法有两种:Prim算法 和 Kruskal算法
Prim算法:以顶点为基础的向外扩张,从某一顶点(A)开始,依次寻找未知顶点中(未加入最小生成树的顶点)到已知顶点的最小权值的顶点。直到找到包含(A)的最大连通图中的所有顶点为止。(由此可见,从不同的顶点开始,获得的最小生成树也就不同,当把图中的每个顶点都建立最小生成树后,就获得了所谓的生成森林)。
所以,其适用于稠密图(顶点少于边)。
图例 | 说明 | 不可选 | 可选 | 已选(Vnew) |
---|---|---|---|---|
![]() | 此为原始的加权连通图。每条边一侧的数字代表其权值。 | - | - | - |
![]() | 顶点D被任意选为起始点。顶点A、B、E和F通过单条边与D相连。A是距离D最近的顶点,因此将A及对应边AD以高亮表示。 | C, G | A, B, E, F | D |
![]() | 下一个顶点为距离D或A最近的顶点。B距D为9,距A为7,E为15,F为6。因此,F距D或A最近,因此将顶点F与相应边DF以高亮表示。 | C, G | B, E, F | A, D |
![]() | 算法继续重复上面的步骤。距离A为7的顶点B被高亮表示。 | C | B, E, G | A, D, F |
![]() | 在当前情况下,可以在C、E与G间进行选择。C距B为8,E距B为7,G距F为11。E最近,因此将顶点E与相应边BE高亮表示。 | 无 | C, E, G | A, D, F, B |
![]() | 这里,可供选择的顶点只有C和G。C距E为5,G距E为9,故选取C,并与边EC一同高亮表示。 | 无 | C, G | A, D, F, B, E |
![]() | 顶点G是唯一剩下的顶点,它距F为11,距E为9,E最近,故高亮表示G及相应边EG。 | 无 | G | A, D, F, B, E, C |
![]() | 现在,所有顶点均已被选取,图中绿色部分即为连通图的最小生成树。在此例中,最小生成树的权值之和为39。 | 无 | 无 | A, D, F, B, E, C, G |
Kruskal算法:以边为基础的向内收拢,首先将所有边进行升序排序,然后从最小边开始,判断当前边的顶点是否已经在最小生成树中,若是,则舍弃该边继续,否则,将该边加入到最小生成树中。直到所有边都遍历完成。(在判断时,用到一个并查集)。(我暂时获得的是生成森林喔*_*)
其适用于稀疏图(即边较少)。
图例描述:
![](http://pic002.cnblogs.com/images/2012/426620/2012073015215729.jpg)
首先第一步,我们有一张图Graph,有若干点和边
![](http://pic002.cnblogs.com/images/2012/426620/2012073015234045.jpg)
将所有的边的长度排序,用排序的结果作为我们选择边的依据。这里再次体现了贪心算法的思想。资源排序,对局部最优的资源进行选择,排序完成后,我们率先选择了边AD。这样我们的图就变成了右图
![](http://pic002.cnblogs.com/images/2012/426620/2012073015313195.jpg)
在剩下的变中寻找。我们找到了CE。这里边的权重也是5
![](http://pic002.cnblogs.com/images/2012/426620/2012073015332154.jpg)
依次类推我们找到了6,7,7,即DF,AB,BE。
![](http://pic002.cnblogs.com/images/2012/426620/2012073015361536.jpg)
下面继续选择, 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; }
相关文章推荐
- 贪心算法基础之最小生成树prim算法 51nod prim模板
- hdu 1233 还是畅通工程(最小生成树的Prim和Kruskal两种算法的c++实现)(prim算法详解)
- hdu 1233 还是畅通工程(最小生成树的Prim和Kruskal两种算法的c++实现)(prim算法详解)
- 最小生成树总结(prim、并查集和kruskal) C++实现
- Prim最小生成树算法(C++)
- 图结构之最小生成树(MST)——Prims(普里姆)算法、Kruskal(克鲁斯卡尔)算法
- 贪心算法之最小生成树Kruskal
- hdu 1863 畅通工程 最小生成树模板入门题 prim+kruskal两种算法AC。
- 最小生成树之克鲁斯卡尔(Kruskal)算法、普里姆(prim)算法
- 最小生成树算法(prim&kruskal)
- 最小生成树算法(Prim和Kruskal)
- 算法复习 - 最小生成树算法 Prim、Kruskal(普里姆算法、 克鲁斯卡尔算法)
- 最小生成树算法(Prim和Kruskal)
- 最小生成树 prim 算法 与kruskal 算法
- 最小生成树两种算法。kruskal和prim
- 最小生成树算法Prim、Kruskal
- 数据结构之(图之最小生成树)Kruskal(克鲁斯卡尔)算法
- 最小生成树两个重要的算法:Prim 和 Kruskal
- 贪心算法之最小生成树prim与单源最短路径dijkstra
- 关于图的常用算法——Dijkstra单源最短路径、Floyd多源最短路径、Prim和Kruskal最小生成树算法