什么是最小生成树?

生成树是相对图来说的,一个图的生成树是一个树并把图的所有顶点连接在一起。一个图可以有许多不同的生成树。一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。最小生成树其实是最小权重生成树的简称。生成树的权重是考虑到了生成树的每条边的权重的总和。

最小生成树有几条边?

最小生成树有(V – 1)条边,其中V是给定的图的顶点数量。

Kruskal算法

下面是步骤寻找MST使用Kruskal算法

1 1,按照所有边的权重排序(从小到大)
2  
3 2,选择最小的边。检查它是否形成与当前生成树形成环。如果没有形成环,讲这条边加入生成树。否则,丢弃它。 
4  
5 3,重复第2步,直到有生成树(V-1)条边

步骤2使用并查集算法来检测环。如果不熟悉并查集建议阅读下并查集

该算法是一种贪心算法。贪心的选择是选择最小的权重的边,并不会和当前的生成树形成环。让我们了解一个例子,考虑下面输入图

spanning-tree-mst

该图包含9个顶点和14个边。因此,形成最小生成树将有(9 – 1)= 8条边。

01 排序后:
02 Weight   Src    Dest
03 1         7      6
04 2         8      2
05 2         6      5
06 4         0      1
07 4         2      5
08 6         8      6
09 7         2      3
10 7         7      8
11 8         0      7
12 8         1      2
13 9         3      4
14 10        5      4
15 11        1      7
16 14        3      5

现在从已经排序的边中逐个选择
1. edge 7-6:没有环,加入

2. edge 8-2: 没有环,加入

3. edge 6-5: 没有环,加入

4. edge 0-1: 没有环,加入

5. edge 2-5: 没有环,加入

6. edge 8-6: 加入后会形成环,舍弃

7. edge 2-3: 没有环,加入

8. edge 7-8: 加入后会形成环,舍弃

9. edge 0-7: 没有环,加入

10. edge 1-2: 加入后会形成环,舍弃

11. edge 3-4: 没有环,加入

目前为止一家有了 V-1 条边,可以肯定V个顶点都一包含在内,到此结束。

代码实现:

// Kruskal 最小生成树算法
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // 带有权重的边
struct Edge
{
int src, dest, weight;
}; // 无向图
struct Graph
{
// V-> 顶点个数, E->边的个数
int V, E;
// 由于是无向图,从 src 到 dest的边,同时也是 dest到src的边,按一条边计算
struct Edge* edge;
}; //构建一个V个顶点 E条边的图
struct Graph* createGraph(int V, int E)
{
struct Graph* graph = (struct Graph*) malloc( sizeof(struct Graph) );
graph->V = V;
graph->E = E;
graph->edge = (struct Edge*) malloc( graph->E * sizeof( struct Edge ) );
return graph;
} //并查集的结构体
struct subset
{
int parent;
int rank;
}; // 使用路径压缩查找元素i
int find(struct subset subsets[], int i)
{
if (subsets[i].parent != i)
subsets[i].parent = find(subsets, subsets[i].parent); return subsets[i].parent;
} // 按秩合并 x,y
void Union(struct subset subsets[], int x, int y)
{
int xroot = find(subsets, x);
int yroot = find(subsets, y);
if (subsets[xroot].rank < subsets[yroot].rank)
subsets[xroot].parent = yroot;
else if (subsets[xroot].rank > subsets[yroot].rank)
subsets[yroot].parent = xroot;
else
{
subsets[yroot].parent = xroot;
subsets[xroot].rank++;
}
} // 很据权重比较两条边
int myComp(const void* a, const void* b)
{
struct Edge* a1 = (struct Edge*)a;
struct Edge* b1 = (struct Edge*)b;
return a1->weight > b1->weight;
} // Kruskal 算法
void KruskalMST(struct Graph* graph)
{
int V = graph->V;
struct Edge result[V]; //存储结果
int e = ; //result[] 的index
int i = ; // 已排序的边的 index //第一步排序
qsort(graph->edge, graph->E, sizeof(graph->edge[]), myComp); // 为并查集分配内存
struct subset *subsets =
(struct subset*) malloc( V * sizeof(struct subset) ); // 初始化并查集
for (int v = ; v < V; ++v)
{
subsets[v].parent = v;
subsets[v].rank = ;
} // 边的数量到V-1结束
while (e < V - )
{
// Step 2: 先选最小权重的边
struct Edge next_edge = graph->edge[i++]; int x = find(subsets, next_edge.src);
int y = find(subsets, next_edge.dest); // 如果此边不会引起环
if (x != y)
{
result[e++] = next_edge;
Union(subsets, x, y);
}
// 否则丢弃,继续
} // 打印result[]
printf("Following are the edges in the constructed MST\n");
for (i = ; i < e; ++i)
printf("%d -- %d == %d\n", result[i].src, result[i].dest,
result[i].weight);
return;
} // 测试
int main()
{
/* 创建下面的图:
10
0--------1
| \ |
6| 5\ |15
| \ |
2--------3
4 */
int V = ; // 顶点个数
int E = ; //边的个数
struct Graph* graph = createGraph(V, E);
// 添加边 0-1
graph->edge[].src = ;
graph->edge[].dest = ;
graph->edge[].weight = ; graph->edge[].src = ;
graph->edge[].dest = ;
graph->edge[].weight = ; graph->edge[].src = ;
graph->edge[].dest = ;
graph->edge[].weight = ; graph->edge[].src = ;
graph->edge[].dest = ;
graph->edge[].weight = ; graph->edge[].src = ;
graph->edge[].dest = ;
graph->edge[].weight = ; KruskalMST(graph); return ;
}

运行结果如下:

Following are the edges in the constructed MST
-- ==
-- ==
-- ==

 时间复杂度:

O(ElogE) 或 O(ElogV)。 排序使用 O(ELogE) 的时间,之后我们遍历中使用并查集O(LogV) ,所以总共复杂度是 O(ELogE + ELogV)。E的值最多为V^2,所以

O(LogV) 和 O(LogE) 可以看做是一样的。

贪心算法(2)-Kruskal最小生成树的更多相关文章

  1. 贪心算法(Greedy Algorithm)最小生成树 克鲁斯卡尔算法(Kruskal&#39;s algorithm)

    克鲁斯卡尔算法(Kruskal's algorithm)它既是古典最低的一个简单的了解生成树算法. 这充分反映了这一点贪心算法的精髓.该方法可以通常的图被表示.图选择这里借用Wikipedia在.非常 ...

  2. 贪心算法之Kruskal

    克鲁斯卡尔Kruskal算法同Prim算法一样,都是求最小生成树.Kruskal是不断的找最短边,加入集合,且不构成回路. 所以,我们可以给每个点定义一个集合,一边的起点和终点查看是否属于同一集合,如 ...

  3. 贪心算法(Greedy Algorithm)之最小生成树 克鲁斯卡尔算法(Kruskal&#39;s algorithm)

    克鲁斯卡尔算法(Kruskal's algorithm)是两个经典的最小生成树算法的较为简单理解的一个.这里面充分体现了贪心算法的精髓.大致的流程能够用一个图来表示.这里的图的选择借用了Wikiped ...

  4. 经典问题----最小生成树(kruskal克鲁斯卡尔贪心算法)

    题目简述:假如有一个无向连通图,有n个顶点,有许多(带有权值即长度)边,让你用在其中选n-1条边把这n个顶点连起来,不漏掉任何一个点,然后这n-1条边的权值总和最小,就是最小生成树了,注意,不可绕成圈 ...

  5. Kruskal 最小生成树算法

    对于一个给定的连通的无向图 G = (V, E),希望找到一个无回路的子集 T,T 是 E 的子集,它连接了所有的顶点,且其权值之和为最小. 因为 T 无回路且连接所有的顶点,所以它必然是一棵树,称为 ...

  6. 最小生成树--Prim算法,基于优先队列的Prim算法,Kruskal算法,Boruvka算法,“等价类”UnionFind

    最小支撑树树--Prim算法,基于优先队列的Prim算法,Kruskal算法,Boruvka算法,“等价类”UnionFind 最小支撑树树 前几节中介绍的算法都是针对无权图的,本节将介绍带权图的最小 ...

  7. 最小生成树之Prim算法,Kruskal算法

    Prim算法 1 .概览 普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (gr ...

  8. 最小生成树(Minimum Spanning Tree)——Prim算法与Kruskal算法+并查集

    最小生成树——Minimum Spanning Tree,是图论中比较重要的模型,通常用于解决实际生活中的路径代价最小一类的问题.我们首先用通俗的语言解释它的定义: 对于有n个节点的有权无向连通图,寻 ...

  9. 数据结构与算法系列----最小生成树(Prim算法&amp;Kruskal算法)

     一:Prim算法       1.概览 普里姆算法(Prim算法).图论中的一种算法.可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中.不但包含了连通图里的全部顶点(英语:Ve ...

随机推荐

  1. Dubbo服务重载方法在JDK1.8上调用出错的问题(待解决)

    据说是javassist版本太低不支持JDK1.8,但是测试升级了还是调用出错.预留,待解决.

  2. linux中配置桥接网络,让虚拟机能够上网

    使用桥接模式最主要的目的就是让虚拟机也能上网,从而有了这篇文章. 1.设置虚拟机的网络连接方式 在设置虚拟机网线的连接方式的时候,注意第一个选择桥接模式,第二个界面名称必须使用和宿主机相同的网卡,然后 ...

  3. 限制su权限

    1.  修改用户的组为wheel组 2.  修改PAM配置文件 修改PAM的配置文件,使得只有属于wheel组的用户才能使用su命令 去掉此处的注释即可: 3.  测试 当不属于wheel组的时候,即 ...

  4. 快速上手Android数据库操作

    Android采用关系型数据库SQLite3,它是一个支持SQL轻量级的嵌入式数据库,在嵌入式操作系统上有很广泛的应用,WM采用的也是SQLite3 关于过于.原理方面的东西在这篇文章里不会提到,但是 ...

  5. 关于自然常数e的理解

    关于自然常数\(e\)的理解 By Z.H. Fu 切问录 ( http://www.fuzihao.org ) 利息增长模型 在上中学学习对数的时候,我们就学到了一个叫做e的东西(\(e\appro ...

  6. hadoop的压缩解压缩,reduce端join,map端join

    hadoop的压缩解压缩 hadoop对于常见的几种压缩算法对于我们的mapreduce都是内置支持,不需要我们关心.经过map之后,数据会产生输出经过shuffle,这个时候的shuffle过程特别 ...

  7. WScript中调用js方法

    http://zhidao.baidu.com/question/484374074.html ———————————————————————————————————————————————— Sub ...

  8. javascript中字符串的trim功能表达式

    string.replace(/(^\s*)|(\s*$)/g, "") 用来删除行首行尾的空白字符(包括空格.制表符.换页符等等)

  9. opencv 模板匹配与滑动窗口(单匹配) (多匹配)

    1单匹配: 测试图片:   code: #include <opencv\cv.h> #include <opencv\highgui.h> #include <open ...

  10. POJ 1679 The Unique MST (次小生成树)

    题目链接:http://poj.org/problem?id=1679 有t组数据,给你n个点,m条边,求是否存在相同权值的最小生成树(次小生成树的权值大小等于最小生成树). 先求出最小生成树的大小, ...