最小(代价)生成树

来源:互联网 发布:网络剧特点是什么 编辑:程序博客网 时间:2024/06/06 16:26

一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。

一.普利姆算法

从图中任意取出一个顶点,把他当作一棵树,然后从这棵树相接的边中选取一条最短(权值最小)的边,并将这条边及其所连接的顶点也并入这棵树中,此时得到一颗有两个顶点的树。然后在这棵树中相连的顶点中选取最短的边,并将图中的所有顶点并入树中为止,此时得到的树就是最小生成树。
流程如图:

/*普利姆算法*/void Prim(Graph g, int v0, int &sum) {//任意开始节点v0,得到边的总长度int lowcost[MAX_NUM], vset[MAX_NUM], v;//邻接节点的最短路径,被访问的标记,最后访问的节点标记int i, j, k, min;v = v0;for (int i = 0; i < g.vetexs; ++i) {lowcost[i] = g.arcs[v0][i];vset[i] = 0;}vset[v0] = 1;//将v0并入树中sum = 0;//sum清零用来累计树的权值for (i = 0; i < g.vetexs; ++i) {min = INF;//INF是一个比图中所有边权值都要大的树for (j = 0; j < g.vetexs; ++j) //选出当前生成树到其一顶点最短边中的一条if (vset[j] == 0 && lowcost[j] < min) {min = lowcost[j];k = j;}vset[j] = 1;v = k;sum += min;//记录最小生成树的总权值,也可以换成其他操作/*下面这个循环一刚并入的顶点v为媒介更新候选边*/for (j = 0; j < g.vetexs; ++j)if (vset[j] == 0 && g.arcs[v][j] < lowcost[j])lowcost[j] = g.arcs[v][j];}}


二.克鲁斯卡算法

基本思想:(1)构造一个只含n个顶点,边集为空的子图。若将图中各个顶点看成一棵树的根节点,则它是一个含有n棵树的森林。(2)从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图。也就是说,将这两个顶点分别所在的两棵树合成一棵树;反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之(3)依次类推,直至森林中只有一棵树,也即子图中含有 n-1条边为止。

大白话:(1)将图中的所有边都去掉。(2)将边按权值从小到大的顺序添加到图中,保证添加的过程中不会形成环(3)重复上一步直到连接所有顶点,此时就生成了最小生成树。这是一种贪心策略。


typedef struct {int a, b;//a和b是一条边的两个顶点int w;//边的权值}Road;Road road[MAX_NUM];int v[MAX_NUM];//定义并查集数组int getRoot(int a) {  //在并查集中查找a的根节点的函数while (v[a] != -1)a = v[a];//结点a与v[a]是否连接(或者间接即多跳连接)//着(在边子集中)同一个结点 ,注意!!此处是循环while而非判断if return a;}void Kruskal(Graph g, int &sum, Road *road) {int i;int N, E, a, b;N = g.vetexs;//节点数E = g.brim;//边数sum = 0;for (i = 0; i < g.vetexs; ++i)//初始化并查集v[i] = -1;sort(road, E);//排序操作,使数组按权值有小到大排序for (i = 0; i < E; ++i) {a = getRoot(road[i].a);b = getRoot(road[i].b);if (a != b)//判断结点road[i].a与road[i].b是否连//接(或者间接即多跳连接)着(在边子集中)同一个结点,//注意:假设结点edges[i].a与结点edges[i].b都跟量//外一个结点X相连(或者间接相连),如若不加判断,则三个结点会形成回路 {v[a] = b;sum += road[i].w;//求生成树的权值,这句并不是本算法的固定                 //写法,可以换成其他的,,例如输出各边}}}


0 0