第七章 图(最小生成树之prime算法和 kruskal算法)
2017-12-09 15:06
537 查看
最小生成树
最小生成树定义引用来源所谓最小生成树,就是在一个具有N个顶点的带权连通图G中,如果存在某个子图G’,其包含了图G中的所有顶点和一部分边,且不形成回路,并且子图G’的各边权值之和最小,则称G’为图G的最小生成树。
由定义我们可得知最小生成树的三个性质:
•最小生成树不能有回路。
•最小生成树可能是一个,也可能是多个。
•最小生成树边的个数等于顶点的个数减一。
prime算法
[prime算法演示实例](http://blog.csdn.net/lqcsp/article/details/14118871)
以下面的图为例,实现prime算法
#include<iostream> #include<cstring> using namespace std; const int inf=10000; int graph[4][4]={ {inf,6,1,inf}, {6,inf,5,3}, {1,5,inf,4}, {inf,3,4,inf} }; bool vis[4]; int m=4; int dis[100]; int prime(int cur) { int index; int sum = 0; memset(vis, false, sizeof(vis)); vis[cur] = true;//最小生成树的起点标记访问过 for(int i = 0; i < m; i ++){ dis[i] = graph[cur][i]; } //cur点已经访问过,剩下的结点数目是m-1 for(int i = 1; i < m; i ++){ int minmum=inf; for(int j = 0; j < m; j ++){ if(!vis[j] && dis[j] < minmum){ minmum = dis[j]; index = j; } } vis[index] = true; sum += minmum;//求最小生成树的最小边权之和 //更新dis数组,对index点来说,与之相连的边和对应的以前的边 //比较,选择较短的边。比如开始和点2相连的所有边是6,inf,5,3 //index更新为点4后,与4相连的所有边是inf,3,4,inf,原来的dis //是6,inf,5,3更新成6,3,4,3 for(int j = 0; j < m; j ++){ if(!vis[j] && dis[j] > graph[index][j]){ dis[j] = graph[index][j];//更新dis数组 } } } return sum; } int main(){ cout<<prime(1)<<endl; return 0; }
kruskal算法
讲kruskal算法之前,先讲并查集并查集
并查集根据其名字干两件事:1.把元素a所在的集合和元素b所在的集合合并为一个集合。
2.查元素a和元素b是否属于同一个集合。
下面给出代码(含有解释),该代码合并集合的方法是根据树的深度,把深度小的树(集合)合并到深度大的(集合)上,如下示例。
/* 并查集思路。 并查集算法的思想,结合了树的思想。 刚开始给出n个元素,这n个元素可以看成n棵树。对于其中一个元素a, 如果另一个元素b和a属于同一个集合,那么我们可以把b当做a的父母, 或者把a当做b的父母(起初,每个元素的父母是他自己),如果元素c 和a,b仍然属于同一个集合,那么我们可以把a,b中任一个当做c的父母, 或者把c当做a或b的父母,以此类推。这样集合用一棵树表示了。不同 的集合表示成了不同棵树。如果我们想把不同集合合并,我们只需要将 一个集合中任一个元素当做另一个集合的父母即可(即在两棵树直接添 加了一条边,把两棵树连在了一起)。如果我们想查两个元素d,e是否 属于同一个集合,我们只需要一直递归向上查d,e的父母(即他们分别 所属的树根)(请记得起初的时候每个元素的父母是他自己,递归到最 后一定能查出一个值),如果他们的父母相同,则他们属于同一个集合, 否则,不属于同一个集合。 上面的描述中,还有一点补充,给每一个元素定个级别,刚开始每个元 素的级别是1,表明以该元素为根的树深为1。 */ /* 本程序例子是,包含6个结点,分别是1,2,3,4,5,6。7其中1,2, 3,7属于一个集合,4,5属于一个集合。6属于一个集合 */ #include <iostream> #include <cstring> #include<algorithm> using namespace std; const int maxn = 10000; int par[maxn]; //结点的父母 int r[maxn]; //每个结点的等级,即以该结点为根的树的深度 //初始化n个元素,父母是他自己,等级(树深)初始化为1 void init(int n) { for (int i = 1; i <=n; i++) { par[i] = i; r[i] = 1; } } //查询树根 int find_root(int x) { if (par[x] == x) { return x; } else { return find_root(par[x]); } } //合并x和y所属集合 void united(int x, int y) { x = find_root(x); y = find_root(y); if (x == y) return;//属于同一棵树,无需进行合并 //深度浅的树合并到深度大的树上 if (r[x]<r[y]) { par[x] = y;r[y]=max(r[x]+1,r[y]); } else { par[y] = x;r[x]=max(r[x],r[y]+1); } } //判断x和y是否属于同一个集合 bool same_set(int x, int y) { return find_root(x) == find_root(y); } int main() { int n,m; cout<<"请输入元素的总数: ";cin>>n;init(n); cout<<"请输入集合的总数: ";cin>>m; cout<<"输入每个集合中的任意两个元素(最多情况数为该集合元素数减1):"<<endl; //n个元素构成m个集合,最多只需输入n-m对元素即可完成所有关系创建 //比如1,2,3,7为一个集合,4,5为一个集合,6为一个集合,7个元素 //形成3个集合,只需要输入1、2,1、3,4、5这三对(<=7-3)即可 int e,p; for(int i=1;i<=n-m;i++){ cin>>e>>p; p=find_root(p); par[e]=p; //路径压缩,e的父母直接变为p的树根 r[p]=2; //由于建立的树只有两层,孩子层深度为1,父母层深度为2 //这样建立起来的树只有两层,一个父母,众多个孩子 } cout << "输入两个元素,把这两个元素所在的集合合并: "; int e1,e2; cin>>e1>>e2; united(e1, e2);//把e1,e2所在的两个集合合并 cout << "输入两个元素,查询是否属于一个集合: "; cin >> e1 >> e2; if(same_set(e1,e2)) cout<<"same"<<endl; else cout<<"different"<<endl; return 0; } /* 本程序样例输入输出: 请输入元素的总数: 7 请输入集合的总数: 3 输入每个集合中的任意两个元素(最多情况数为该集合元素数减1): 1 2 2 3 4 5 6 6 输入两个元素,把这两个元素所在的集合合并: 2 5 输入两个元素,查询是否属于一个集合: 1 4 same */
kruskal算法过程
构造一个只含n个顶点,而边集为空的子图,若将该子图中各个顶点看成是各棵树的根节点,则它是一个含有n棵树的森林 。之后,从网的边集中选取一条权值最小的边,若该边的两个顶点分属不同的树 ,则将其加入子图,也就是这两个顶点分别所在的 两棵树合成一棵树;反之,若该边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直至森林只有一棵树。kruskal算法能够在并查集的基础很快的实现。
以下面的图为例,实现kruskal算法
#include<iostream> #include<algorithm> using namespace std; const int maxn=10000; int par[maxn];//结点的父母,用并查集 int r[maxn];//每个结点等级(以该结点为树根的树深),用并查集 typedef struct{ int st;//一条边的起始结点 int en;//一条边的结束结点 int w;//边权重 }edge_node; edge_node edge[maxn/2*maxn]; //并查集算法的初始化 void init(int n){ for(int i=1;i<=n;i++){ par[i]=i; r[i]=1; } } //并查集算法的找树根 int find_root(int x){ if(x==par[x]) return x; else return find_root(par[x]); } //并查集算法的合并集合 void united(int x,int y){ x=find_root(x); y=find_root(y); if(x==y) return; if(r[x]<r[y]) {par[x]=y;r[y]=r[x]+1;} else{par[y]=x;r[x]=r[y]+1;} } bool cmp(edge_node e1,edge_node e2){ return e1.w<e2.w; } int kruskal(int n,edge_node edge[]){ int sum=0; init(n); sort(edge+1,edge+n+1,cmp); for(int i=1;i<=n;i++){ if(find_root(edge[i].st)!=(find_root(edge[i].en))){ united(edge[i].st,edge[i].en); sum+=edge[i].w; } } return sum; } int main(){ int n; cout<<"请输入边的条数: ";cin>>n; int x,y,w; for(int i=1;i<=n;i++){ cout<<"请输入第"<<i<<"条边的两个顶点及权重: "<<endl; cin>>x>>y>>w; edge[i].st=x;edge[i].en=y;edge[i].w=w; } cout<<"最小生成树的边权之和: "; cout<<kruskal(n,edge)<<endl; return 0; } /* 输入输出样例: 请输入边的条数: 5 请输入第1条边的两个顶点及权重: 1 2 6 请输入第2条边的两个顶点及权重: 1 3 1 请输入第3条边的两个顶点及权重: 2 3 5 请输入第4条边的两个顶点及权重: 2 4 3 请输入第5条边的两个顶点及权重: 3 4 4 最小生成树的边权之和: 8 */
相关文章推荐
- 最小生成树(prime算法、kruskal算法) 和 最短路径算法(floyd、dijkstra)
- 最小生成树(prime算法、kruskal算法) 和 最短路径算法(floyd、dijkstra)
- 最小生成树(prime算法、kruskal算法) 和 最短路径算法(floyd、dijkstra)
- 最小生成树(prime算法、kruskal算法) 和 最短路径算法(floyd、dijkstra)
- 最小生成树之prime算法实现
- 最小生成树(prime算法、kruskal算法) 和 最短路径算法(floyd、dijkstra)
- 最小生成树(kruskal算法)和 (Prime算法)
- 最小生成树(prime算法 & kruskal算法)和 最短路径算法(floyd算法 & dijkstra算法)
- 求图的最小生成树的prime算法和Kruskal算法
- 最小生成树(prime算法、kruskal算法) 和 最短路径算法(floyd、dijkstra)
- 最小生成树(prime算法、kruskal算法) 和 最短路径算法(floyd、dijkstra)
- 最小生成树(prime算法、kruskal算法) 和 最短路径算法(floyd、dijkstra)
- 最小生成树(prime算法、kruskal算法) 和 最短路径算法(floyd、dijkstra)(转)
- 最小生成树(prime算法、kruskal算法) 和 最短路径算法(floyd、dijkstra)
- 最小生成树(prime算法、kruskal算法) 和 最短路径算法(floyd、dijkstra)
- 最小生成树(prime算法、kruskal算法) 和 最短路径算法(floyd、dijkstra)
- 最小生成树(Prime算法、Kruskal算法)和最短路径算法(Floyd算法、DijKstra算法)
- 最小生成树(prime算法、kruskal算法) 和 最短路径算法(floyd、dijkstra)
- 最小生成树(prime算法、kruskal算法) 和 最短路径算法(floyd、dijkstra)
- 最小生成树(prime算法、kruskal算法) 和 最短路径算法(floyd、dijkstra)