最小生成树算法(prim&kruskal)
2011-11-01 20:08
447 查看
无向连通图 G = (V, E)的生成树是它的极小连通子图:连接了图中所有的顶点,有保持图连通的最少的边,且不包含回路(最少的边这一条件已经隐含了不含回路的性质,同时,生成树是一种树,也是不允许有回路的)。在所有的生成树中,权值最小的树就是最小生成树(minimum spanning tree)。由最小生成树的性质可知,它的边数比顶点数小一,而且一个图的最小生成树往往是不唯一的。
求解无向连通图的最小生成树的经典方法有两种:kruskal算法和prim算法。两个算法都是贪心思想的应用:一次生成一条安全边:
GENERIC_MST(G,W)
A← 空集
While A does not form a spanning tree
Do find an edge(u, v) thia is safe for A
A ← A (u, v)
return A
说明一下安全边的概念。无向联通图 G = (V, E)的一个割(S,V-S)是对V的一个划分。如果边(u,v)E的一个顶点属于S,另一个属于V-S,则称(u,v)通过割(S,V-S)。如果边的集合A中没有边通过某个割,则称该割不妨害A。如果某条边的权值是通过某个割的权值最小的边,则称该边为通过这个割的一条轻边。下面给出识别安全边的规则:
设图G=(V,E)为无向连通图,设A为E的子集,包含于G德某个最小生成树中,设割(S,V-S)是G的任意一个不妨害A的割,如果(u,v)是通过割(S,V-S)的一条轻边,那么(u,v)对集合A来说是安全的。
Kruskal算法:
初始状态下生成树A是空集,每个顶点都是一棵树(一个连通分支),然后每次都将图中连接两个不同的连通分支的最小权边加入到A中,然后合并两个两个连通分支,直到图中的所有顶点都属于同一个连通分支。
算法的描述挺简单,但是有两个问题需要解决:
1.如何判断两个顶点是否属于不同的连通分支?
《算法导论》中关于该部分的论述使用了不相交集合数据结构。我在实现的时候使用一个标志数组array,长度为|V|,依次对应图中的每个顶点。array[i]、array[j]不同表示顶点i和顶点j属于不同的连通分支,合并操作也很简单,将array中和array[i]相同(表示位于同一连通分支)的元素值置为array[j],这样就将顶点i所在的连通分支与j所在的分支合并。
2.如何找出属于不同连通分支的最小权边?
最直接的方法是遍历图中的所有边,找出两端处于不同的分支且权最小的边。也可以使用排序算法对边按长度进行排序。
3.如何记录生成树?
可以为每个顶点增加一个parent域,记录在生成树中它的父母。比如边(u,v)添加到A中,那么v的parent域就设为u。
Prim算法:
初始状态A包含任意一个顶点r,从r开始,每次都向A中添加一条连接树A和G=(V,A)中某个孤立顶点的轻边,直至生成树A包含了图中所有的顶点。
有效实现该算法的关键是设法较容易地选择一条轻边。我们可以借助最小优先级队列。
图中的顶点可以分为两类,一类是在A中的,已经纳入最小生成树了,另一种是不在A中的,记为B,对于这些顶点,我们需要保存它们与A中的某个顶点相连的边中的最小权值。最小优先级队列保存的就是B(尚未纳入最小生成树)中的顶点以及它们与A中某个顶点相连的边中的最小权值。 每次队首出队,设新加入A的顶点为V,那么我们要修改V的邻接点中尚未在A中(在最小优先级队列)的且与A中顶点相连的边的最小权值(比较拗口)。另外,为了记录生成树,和Kruskal算法一样,我们需要增加parent域。
代码地址:点击打开链接
求解无向连通图的最小生成树的经典方法有两种:kruskal算法和prim算法。两个算法都是贪心思想的应用:一次生成一条安全边:
GENERIC_MST(G,W)
A← 空集
While A does not form a spanning tree
Do find an edge(u, v) thia is safe for A
A ← A (u, v)
return A
说明一下安全边的概念。无向联通图 G = (V, E)的一个割(S,V-S)是对V的一个划分。如果边(u,v)E的一个顶点属于S,另一个属于V-S,则称(u,v)通过割(S,V-S)。如果边的集合A中没有边通过某个割,则称该割不妨害A。如果某条边的权值是通过某个割的权值最小的边,则称该边为通过这个割的一条轻边。下面给出识别安全边的规则:
设图G=(V,E)为无向连通图,设A为E的子集,包含于G德某个最小生成树中,设割(S,V-S)是G的任意一个不妨害A的割,如果(u,v)是通过割(S,V-S)的一条轻边,那么(u,v)对集合A来说是安全的。
Kruskal算法:
初始状态下生成树A是空集,每个顶点都是一棵树(一个连通分支),然后每次都将图中连接两个不同的连通分支的最小权边加入到A中,然后合并两个两个连通分支,直到图中的所有顶点都属于同一个连通分支。
算法的描述挺简单,但是有两个问题需要解决:
1.如何判断两个顶点是否属于不同的连通分支?
《算法导论》中关于该部分的论述使用了不相交集合数据结构。我在实现的时候使用一个标志数组array,长度为|V|,依次对应图中的每个顶点。array[i]、array[j]不同表示顶点i和顶点j属于不同的连通分支,合并操作也很简单,将array中和array[i]相同(表示位于同一连通分支)的元素值置为array[j],这样就将顶点i所在的连通分支与j所在的分支合并。
2.如何找出属于不同连通分支的最小权边?
最直接的方法是遍历图中的所有边,找出两端处于不同的分支且权最小的边。也可以使用排序算法对边按长度进行排序。
3.如何记录生成树?
可以为每个顶点增加一个parent域,记录在生成树中它的父母。比如边(u,v)添加到A中,那么v的parent域就设为u。
Prim算法:
初始状态A包含任意一个顶点r,从r开始,每次都向A中添加一条连接树A和G=(V,A)中某个孤立顶点的轻边,直至生成树A包含了图中所有的顶点。
有效实现该算法的关键是设法较容易地选择一条轻边。我们可以借助最小优先级队列。
图中的顶点可以分为两类,一类是在A中的,已经纳入最小生成树了,另一种是不在A中的,记为B,对于这些顶点,我们需要保存它们与A中的某个顶点相连的边中的最小权值。最小优先级队列保存的就是B(尚未纳入最小生成树)中的顶点以及它们与A中某个顶点相连的边中的最小权值。 每次队首出队,设新加入A的顶点为V,那么我们要修改V的邻接点中尚未在A中(在最小优先级队列)的且与A中顶点相连的边的最小权值(比较拗口)。另外,为了记录生成树,和Kruskal算法一样,我们需要增加parent域。
代码地址:点击打开链接
相关文章推荐
- 【算法复习】图的最小生成树(Prim&Kruskal)
- 算法记录---最小生成树【kruskal&&prim】
- 最小生成树(prim&&kruskal)
- 最小生成树之 prim & kruskal
- Prim && Kruskal 生成MST(最小生成树)及最短路径问题
- 最小生成树两种算法。kruskal和prim
- 2017-07-18:最小生成树(Prim&&Kruskal)
- 51Nod 1212 无向图最小生成树(最小生成树Kruskal & Prim
- zoj 2966 Build The Electric System【最小生成树 Kruskal && prim】
- 最小生成树 ,prim 和Kruskal 算法
- hdu 1863 畅通工程 最小生成树模板入门题 prim+kruskal两种算法AC。
- HDOJ 1233 还是畅通工程 最小生成树 kruskal && prim
- hdu 1233 还是畅通工程(最小生成树的Prim和Kruskal两种算法的c++实现)(prim算法详解)
- 图的广度遍历、深度遍历及最小生成树书算法(Prim、Kruskal)
- 最小生成树(MST)----普里姆(Prim)算法与克鲁斯卡尔(Kruskal)算法
- hdu1162 Eddy's picture (最小生成树之prim 算法)
- zoj 1914 || poj 2349 Arctic Network【最小生成树 kruskal && prim】
- hdoj 1875 畅通工程再续【最小生成树 kruskal && prim】
- 数据结构 学习笔记(九):图(下):最小生成树(Prim,Kruskal 算法),拓扑排序 AOV,关键路径 AOE
- hdoj-1875 畅通工程再续【最小生成树--prim&&kruskal】