- 浏览: 1408090 次
- 性别:
- 来自: 广州
文章分类
最新评论
-
sdgxxtc:
[quo[color=red]te][/color]
C#使用OleDb读取Excel,生成SQL语句 -
zcs302567601:
博主,你好,一直都有个问题没有搞明白,就是 2.x的版本是通过 ...
NGUI所见即所得之UIPanel -
一样的追寻:
感谢楼主!
有向强连通和网络流大讲堂——史无前例求解最大流(最小割)、最小费用最大流 -
cp1993518:
感谢!从你的博客里学到了很多
Unity日志工具——封装,跳转 -
cp1993518:
学习了~,话说现在的版本custom还真的变委托了
NGUI所见即所得之UIGrid & UITable
最小生成树——Prim、Kruskal、Sollin(Boruvka)
本文内容框架:
1.Prim算法及其基于优先队列实现
2.Kruskal算法
3.Sollin算法
对于最小生成树,有两种算法可以解决。一种是Prim算法,该算法的时间复杂度为O(n²),与图中边数无关,该算法适合于稠密图,而另外一种是Kruskal,该算法的时间主要取决于边数,它较适合于稀疏图。
Prim算法
Prim算法描述
设图G =(V,E),其生成树的顶点集合为U。
①、把v0放入U。
②、在所有u∈U,v∈V-U的边(u,v)∈E中找一条最小权值的边,加入生成树。
③、把②找到的边的v加入U集合。如果U集合已有n个元素,则结束,否则继续执行②。
时间复杂度
邻接矩阵、搜索 | O(V2) |
二叉堆(后文伪代码中使用的数据结构)、邻接表 | O((V + E) log(V)) = O(E log(V)) |
斐波那契堆、邻接表 | O(E + V log(V)) |
(该图转自wikipedia)
通过邻接矩阵图表示的简易实现中,找到所有最小权边共需O(V²)的运行时间。使用简单的二叉堆与邻接表来表示的话,普里姆算法的运行时间则可缩减为O(E *log V),其中E为连通图的边数,V为顶点数。如果使用较为复杂的斐波那契堆,则可将运行时间进一步缩短为O(E + V* log V),这在连通图足够密集时(当E满足Ω(V* log V)条件时),可较显著地提高运行速度。
Prim算法实现
用优先队列实现
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <algorithm> #include <set> #include <map> #include <vector> #include <queue> #include <ctime> using namespace std; #define LL long long const int N = 2000; const int INF = 1 << 30; struct Node { int v,next,w; bool operator < (const Node &a) const { return w > a.w; } } p[N],t1,t2; int dis[N],vis[N],head[N],cnt; int res; void addedge(int u,int v,int w) { p[cnt].v = v; p[cnt].next = head[u]; p[cnt].w = w; head[u] = cnt++; } void prim() { priority_queue<Node> q; for(int i = head[0] ; i != -1 ; i = p[i].next) { int v = p[i].v; if(p[i].w < dis[v]) { dis[v] = p[i].w; t1.w = dis[v]; t1.v = v; q.push(t1); } } vis[0] = 1; while(!q.empty()) { t1 = q.top(); q.pop(); int u = t1.v; if(vis[u]) continue; vis[u] = 1; res += dis[u]; for(int i = head[u]; i != -1; i = p[i].next) { int v = p[i].v; if(!vis[v] && dis[v] > p[i].w) { dis[v] = p[i].w; t2.v = v; t2.w = dis[v]; q.push(t2); } } } } int main() { int n,m,w; while(scanf("%d",&n),n) { memset(p,0,sizeof(p)); memset(head,-1,sizeof(head)); memset(vis,0,sizeof(vis)); char u,v; for(int i=0; i<n-1; i++) { cin>>u>>m; for(int j=0; j<m; j++) { cin>>v>>w; addedge(u-'A',v-'A',w); addedge(v-'A',u-'A',w); } } for(int i = 0 ; i < n ; i ++) dis[i] = INF; res = 0; prim(); printf("%d\n",res); } return 0; }
转自http://blog.csdn.net/acceptedxukai/article/details/6978868
Kruskal算法
Kruskal算法与Prim算法的不同之处在于,Kruskal在找最小生成树结点之前,需要对所有权重边做从小到大排序。将排序好的权重边依次加入到最小生成树中,如果加入时产生回路就跳过这条边,加入下一条边。当所有结点都加入到最小生成树中之后,就找出了最小生成树。
Kruskal算法步骤
1.新建图G,G中拥有原图中相同的点,但没有边
2.将原图中所有的边按权值从小到大排序
3.从权值最小的边开始,如果这条边链接的两个点于图G中不在同一个连通分量中,则添加这条边到图G中
4.重复3,直至图G中所有的点都在同一个连通分量中
Kruskal算法实现
利用最小堆来存储边集E,利用并-查集来判断向T中添加边是否构成环路。
#include <iostream> #include <vector> #include <algorithm> using namespace std; struct Edge { int from, to, w; //~ 不要被假象迷惑,这里是无向图 Edge(int f, int t, int _w): from(f), to(t), w(_w){} /* //~ bool operator <(const Edge& e){ return w < e.w; } bool operator >(const Edge& e){ return w > e.w; } */ }; //~ 为什么我把operator<重载为成员会出错? //~ bool operator <(const Edge& e1, const Edge& e2){ return e1.w < e2.w; } bool operator >(const Edge& e1, const Edge& e2){ return e1.w > e2.w; } bool AddEdge(vector<int> & V, const Edge& e); int main(int argc, char* argv[]) { vector<Edge> E; int from, to, w; int n; //~ 顶点数 cin>>n; vector<int> V(n+1, -1); //~ 顶点并查集 while (cin>>from>>to>>w) E.push_back(Edge(from, to, w)); make_heap(E.begin(), E.end(), greater<Edge>()); int count = 0; //~ 已添加边数 while (E.size()) { Edge e = E[0]; if(AddEdge(V, e)) //~ 将成功添加的边输出 { count++; if(count == n - 1) break; //~ 树已生成完毕 cout<<e.from<<"->"<<e.to<<": "<<e.w<<endl; } pop_heap(E.begin(), E.end(),greater<Edge>()); E.pop_back(); } if (count != n - 1) cout<<"I cannot do what you want."<<endl; return 0; } bool AddEdge(vector<int> & V, const Edge& e) { int i = e.from; for (; V[i] > 0;) i = V[i]; //~ 寻找根节点 int j = e.to; for (; V[j] > 0;) j = V[j]; //~ 寻找根节点 if (i == j) return false; //~ i,j两节点已经联通 if (V[i] > V[j]) //~ 将小集合合并至大集合上 V[i] = j; else V[j] = i; return true; //~ ^_^
转自http://www.cppblog.com/superKiki/archive/2010/05/02/114180.aspx
Sollin(Boruvka)算法
Sollin(Brouvka)算法虽然是最小生成树最古老的一个算法之一,其实是前面介绍两种算法的综合,每次迭代同时扩展多课子树,直到得到最小生成树T。
Sollin(Boruvka)算法步骤
1.用定点数组记录每个子树(一开始是单个定点)的最近邻居。(类似Prim算法)
2.对于每一条边进行处理(类似Kruskal算法)
如果这条边连成的两个顶点同属于一个集合,则不处理,否则检测这条边连接的两个子树,如果是连接这两个子树的最小边,则更新(合并)
由于每次循环迭代时,每棵树都会合并成一棵较大的子树,因此每次循环迭代都会使子树的数量至少减少一半,或者说第i次迭代每个分量大小至少为。所以,循环迭代的总次数为O(logn)。每次循环迭代所需要的计算时间:对于第2步,每次检查所有边O(m),去更新每个连通分量的最小弧;对于第3步,合并个子树。所以总的复杂度为O(E*logV)。
Sollin(Boruvka)算法实现
typedef struct{int v;int w;double wt;}Edge; typeder struct{int V;int E;double **adj}Graph; /*nn存储每个分量的最邻近,a存储尚未删除且还没在MST中的边 *h用于访问要检查的下一条边 *N用于存放下一步所保存的边 *每一步都对应着检查剩余的边,连接不同分量的顶点的边被保留在下一步中 *最后每一步将每个分量与它最邻近的分量合并,并将最近邻边添加到MST中 */ Edge nn[maxE],a[maxE]; void Boruvka(Graph g,Edge mst[]) { int h,i,j,k,v,w,N; Edge e; int E=GRAPHedges(a,G); for(UFinit(G->V);E!=0;E=N) { for(k=0;k<G->V;k++) nn[k]=Edge(G->V,G->V,maxWT); for(h=0,N=0;h<E;h++) { i=find(a[h].v);j=find(a[h].w); if(i==h) continue; if(a[h].wt<nn[i].wt)nn[i]=a[h]; if(a[h].wt<nn[j].wt)nn[j]=a[h]; a[N++]=a[h]; } for(k=0;k<G->V;k++) { e=nn[k];v=e.v;w=e.w; if(v!=G->V&&!UFfind(v,w)) { UFunion(v,w);mst[k]=e; } } }
小结
这篇文章详细的讲解了图最小生成树的三个算法的原理和实现,希望能派上用场。如果你有任何建议或者批评和补充,请留言指出,不胜感激,更多参考请移步互联网。
发表评论
-
C# 调用Delegate.CreateDelegate方法出现“未处理ArgumentException”错误解决
2013-05-31 12:24 3547在C# 调用Delegate.Create ... -
数组问题集结号
2012-12-06 22:01 0数组是最简单的数据结构,数组问题作为公司招聘的笔试和面试题目 ... -
算法问题分析笔记
2012-12-05 11:59 01.Crash Balloon Zhejiang Univer ... -
Java基础进阶整理
2012-11-26 09:59 2313Java学习笔记整理 ... -
Java学习笔记整理
2012-11-24 23:43 211Java学习笔记整理 本文档是我个人 ... -
《C++必知必会》学习笔记
2012-11-24 23:40 2631《C++必知必会》学 ... -
《C++必知必会》学习笔记
2012-11-24 23:34 1《C++必知必会》学习笔 ... -
基本技术——贪心法、分治法、动态规划三大神兵
2012-11-03 19:30 0基本技术——贪心法、分治法、动态规划三大神兵 -
优先队列三大利器——二项堆、斐波那契堆、Pairing 堆
2012-11-03 13:12 35620优先队列三大利器——二项堆、斐波那契堆、Pairing ... -
优先队列三大利器——二项堆、斐波那契堆、Pairing 堆
2012-11-03 13:01 3优先队列三大利器——二项堆、斐波那契堆、Pairing 堆 ... -
排序算法群星豪华大汇演
2012-10-30 00:09 3114排序算法群星豪华大汇演 排序算法相对简单些,但是由于 ... -
分布排序(distribution sorts)算法大串讲
2012-10-29 15:33 4635分布排序(distribution sorts)算法大串讲 ... -
归并排序(merge sorts)算法大串讲
2012-10-29 10:04 8295归并排序(merge sorts)算法大串讲 ... -
交换排序(exchange sorts)算法大串讲
2012-10-29 00:22 4387交换排序(exchange sorts)算法大串讲 本 ... -
选择排序(selection sorts)算法大串讲
2012-10-28 12:55 3694选择排序(selection sorts)算法大串讲 本文内 ... -
插入排序(insertion sorts)算法大串讲
2012-10-28 11:30 2724插入排序(insertion sorts ... -
伸展树(Splay Tree)尽收眼底
2012-10-27 15:11 5500伸展树(Splay Tree)尽收眼底 本文内容 ... -
红黑树(Red-Black Tree)不在话下
2012-10-26 20:54 2217红黑树(Red-Black Tree) 红黑树定义 红黑树 ... -
平衡二叉树(AVL)原理透析和编码解密
2012-10-26 10:22 2976平衡二叉树(AVL)原理透析和编码解密 本文内容 ... -
Trie三兄弟——标准Trie、压缩Trie、后缀Trie
2012-10-26 01:45 10686Trie三兄弟——标准Trie ...
相关推荐
本篇将详细介绍图的操作,主要包括图的遍历和最小生成树的构建。 一、图的遍历 图的遍历是指按照一定的顺序访问图中的每一个顶点。主要分为深度优先搜索(DFS)和广度优先搜索(BFS)两种方式。 1. 深度优先搜索...
最小生成树的生成通常有两种经典的算法:Prim算法和Kruskal算法。 1. Prim算法: - 基本思想是从一个初始顶点开始,逐步将与当前生成树连接的最小权重边添加到树中,直到包含所有顶点。 - 操作过程可以使用优先...
C#,最小生成树(MST)克鲁斯卡尔(Kruskal)算法的源代码。克鲁斯卡尔(Kruskal)算法是一种用来寻找最小生成树的算法,由Joseph Kruskal在1956年发表。用来解决同样问题的还有Prim算法和Boruvka算法等。三种算法都...
本次将详细解析一个特定的图论算法——最小生成树算法,具体而言,我们将深入探讨一种名为Prim算法的实现方式,并结合给定代码片段进行分析。 ### 图论算法-最小生成树 #### 最小生成树概念 最小生成树(Minimum ...
Boruvka算法用于查找边加权图的最小生成树(MST),它早于Prim和Kruskal的算法,但仍然可以被认为是两者的关联。1926年,奥塔卡·博鲁夫卡(Otakar Boruvka)首次提出了一种求给定图的MST的方法。这在计算机出现之前...
除了Boruvka算法外,还有两种经典的最小生成树算法:Prim算法和Kruskal算法。 - **Prim算法**从一个顶点出发,逐步将邻近的顶点加入到树中,直到树覆盖所有顶点。 - **Kruskal算法**则是按边的权重从小到大排序,...
有多种算法可以用来求解最小生成树,其中最著名的两种是Prim算法和Kruskal算法。 1. **Prim算法**: Prim算法是一种贪心策略,从一个初始顶点开始,逐步添加边,每次选择与当前生成树连接的边中权值最小的一条,...
- Kruskal's Algorithm:克鲁斯卡尔算法,通过按边的权重升序排序,逐步选择不形成环的边来构造最小生成树。关键在于并查集(Disjoint Set)的数据结构,用于判断添加的边是否会形成环。 - Prim's Algorithm:...
在本讲座中,我们主要探讨四种经典算法来解决最小生成树问题:Boruvka算法、Prim算法和Kruskal算法,以及MST的唯一性判定和瓶颈生成树。 首先,最小生成树问题的基本要求是构建一个连通图,并且使所有边的总权重尽...
总结来说,“tulun.zip_生成树”是一个用于寻找加权图最小生成树的程序,它可能实现了Prim或Kruskal算法,需要用户输入图的信息,然后输出最小生成树的结构。掌握最小生成树的概念及其算法对于理解网络优化、数据...
解决最小生成树问题有多种算法,其中最著名的两种是Prim算法和Kruskal算法。 1. **Prim算法**:该算法基于贪心策略,从一个初始顶点开始,每次选择与当前生成树边集连接的且权重最小的边,将新的顶点加入到树中。...
3. Boruvka算法:该算法是分阶段进行的,每个阶段都找到每个连通分量中权重最小的边,然后将这些边加入到最小生成树中,直至所有顶点都在同一连通分量内。 "YPAP116 Minimum Spanning Tree"的代码实现可能涵盖了...
实验还鼓励你设计和实现其他最小生成树算法,如管梅谷破圈算法和Sollin(Boruvka)算法。 在实验过程中,你需要将图的数据从文件中读取,至少包含10个顶点和13条边,并以适当方式展示结果。实验报告应包括源代码、...
【算法设计】中的知识点主要涉及贪心算法,具体讲解了Prim算法、Kruskal算法以及两种特殊的图着色规则——红规则(red-rule)和蓝规则(blue-rule),还有Boruvka算法的演示。这些算法在图论和网络流问题中广泛应用...
1. **Prim算法**:从一个顶点开始,逐步添加边到当前生成树,每次选择与已生成树连接且权重最小的边,直到所有顶点都被包含。Prim算法保证了生成的树是最小的,因为它始终优先选取边权重最小的边。 2. **Kruskal...
生成树问题的解决可以使用 Kruskal 算法、Prim 算法、Boruvka 算法等。 连通性问题是图论中非常重要的问题之一。它包括强大的 DFS 算法、无向图连通性、有向图连通性、割点、割边、二连通分支、强连通分支、2-SAT ...
4. **最小生成树算法**:Prim、Kruskal和Boruvka算法用于寻找无向图的最小生成树,即连接所有节点的树,其总边权重尽可能小。Prim算法从一个节点开始,每次都添加与当前树中节点相连的最低权重边;Kruskal算法则按边...
解决此问题的一种常用算法是Prim算法或Kruskal算法,它们都是用于寻找图的最小生成树。最小生成树是一棵树形结构,包含了原图的所有顶点,但只包含足够连接所有顶点的最少边,且这些边的总权重最小。 1. Prim算法:...
2. Prim's Algorithm(普里姆算法):同样是一种贪心算法,它从一个顶点开始,逐步扩展生成树,每次添加一条与当前生成树连接的新边,使得这条边的端点分属不同的连通分量,并且边的权重尽可能小。 3. Boruvka's ...
2. **Prim's Algorithm**:Prim算法从一个顶点开始,逐步增加边来扩大生成树,每次都选择连接两个不同集合且权值最小的边。这个算法可以使用优先队列(如二叉堆)来优化查找最小边的过程。 除了这两种基本算法,...