最小生成树之算法记录【prime算法+Kruskal算法】【模板】
首先说一下什么是树:
1、只含一个根节点
2、任意两个节点之间只能有一条或者没有线相连
3、任意两个节点之间都可以通过别的节点间接相连
4、除了根节点没一个节点都只有唯一的一个父节点
5、也有可能是空树(不含任何节点)
最小生成树就是:
在所有数据满足是一棵树的情况下一条将所有节点都连接起来且长度最短的一条路(因为任意两个节点之间有权值
(相连的两点之间权值为一个具体的数,不相连的两个点之间权值为无穷大))
下面介绍通用的求最小生成树的两种算法:
ps:这里用的两种算法都是用邻接矩阵实现适合点稠密型数据或者数据较小的情况:
(1)prime算法:
/*
* 数组tree[]用来记录最小生成树的节点
* 数组lowdis[]记录从起点到其余所有点的距离并不断更新
* 数组map[][]记录所有数据两点之间的距离
* point是所有节点的数目,begin是起点
* mindis是最小生成树的长度
*/
void prime()
{
int i,j,min,mindis=0,next;
memset(tree,0,sizeof(tree));
for(i=1;i<=point;i++)
{
lowdis[i]=map[begin][i];//用lowdis[]数组记录下从起点到剩下所有点的距离
}
tree[begin]=1;//标记起点(即最小生成树中的点)
for(i=1;i<point;i++)
{
min=INF;
for(j=1;j<=point;j++)
{
if(!tree[j]&&min>lowdis[j])
{
min=lowdis[j];//求出从当前起点到其余所有点的距离中最短的
next=j;
}
}
mindis+=min;//记录下整条最小树的长度
tree[next]=1;
for(j=1;j<=point;j++)
{
if(!tree[j]&&lowdis[j]>map[next][j])
lowdis[j]=map[next][j];//更新lowdis[]数组
}
}
printf("%d\n",mindis);
}
kruskal算法:
此算法的核心就是在并查集(并查集知识请看 知识小总结记录分类中的并查集(http://www.cnblogs.com/tonghao/p/4442821.html))的基础上对两点之间距离进行排序:
find()函数用来查找根节点
int find(int father)//查找根节点
{
int t;
int children=father;
while(father!=set[father])
father=set[father];
while(fa!=set[children])
{
t=set[children];
set[children]=father;
children=t;
}
return father;
}
mix函数用来合并两个节点,使两个节点的父节点相同
void mix(int x,int y)//将两个点合并(即另两点根节点相同)
{
int fx;
int fy;
fx=find(x);
fy=find(y);
if(fx!=fy)
set[fx]=fy;
}
利用结构体排序:
struct record
{
int begin;//记录两个点中的一个
int end;//记录两个点中的一个
int dis;//记录两点之间距离
}num[MAX];
bool cmp(int a,int b)
{
return a.dis<b.dis;//对两点之间距离进行从大到小的排序
}
用一个题来实现上述两个算法:
行对应村庄间道路的成本,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间道路的成本(也是正整数)。为简单起见,村庄从1到M编号。当N为0时,全部输入结束,相应的结果不要输出。
prime算法:
#include<stdio.h>
#include<string.h>
#define INF 0x3f3f3f
int lowcost[110];//此数组用来记录第j个节点到其余节点最少花费
int map[110][110];//用来记录第i个节点到其余n-1个节点的距离
int visit[110];//用来记录最小生成树中的节点
int city;
void prime()
{
int min,i,j,next,mincost=0;
memset(visit,0,sizeof(visit));//给最小生成树数组清零
for(i=1;i<=city;i++)
{
lowcost[i]=map[1][i];//初始化lowcost数组为第1个节点到剩下所有节点的距离
}
visit[1]=1;//选择第一个点为最小生成树的起点
for(i=1;i<city;i++)
{
min=INF;
for(j=1;j<=city;j++)
{
if(!visit[j]&&min>lowcost[j])//如果第j个点不是最小生成树中的点并且其花费小于min
{
min=lowcost[j];
next=j;//记录下此时最小的位置节点
}
}
if(min==INF)
{
printf("?\n");
return ;
}
mincost+=min;//将最小生成树中所有权值相加
visit[next]=1;//next点加入最小生成树
for(j=1;j<=city;j++)
{
if(!visit[j]&&lowcost[j]>map[next][j])//如果第j点不是最小生成树中的点并且此点处权值大于第next点到j点的权值
{
lowcost[j]=map[next][j]; //更新lowcost数组
}
}
}
printf("%d\n",mincost);
}
int main()
{
int road;
int j,i,x,y,c;
while(scanf("%d%d",&road,&city)&&road!=0)
{
memset(map,INF,sizeof(map));//初始化数组map为无穷大
while(road--)
{
scanf("%d%d%d",&x,&y,&c);
map[x][y]=map[y][x]=c;//城市x到y的花费==城市y到想的花费
}
prime();
}
return 0;
}
kruskal算法:
/*
* set[]数组用来存放根节点
* find()函数用来查找根节点
* mix()函数用来将两个点合并(即使其父节点相同)
*/
#include<stdio.h>
#include<algorithm>
using namespace std;
int set[110];
struct record
{
int beg;
int end;
int money;
}s[11000];
int find(int fa) //查找根节点
{
int ch=fa;
int t;
while(fa!=set[fa])
fa=set[fa];
while(ch!=fa)
{
t=set[ch];
set[ch]=fa;
ch=t;
}
return fa;
}
void mix(int x,int y) //合并两点
{
int fx,fy;
fx=find(x);
fy=find(y);
if(fx!=fy)
set[fx]=fy;
}
bool cmp(record a,record b)
{
return a.money<b.money; //对价格进行排序
}
int main()
{
int city,road,n,m,j,i,sum;
while(scanf("%d",&road)&&road!=0)
{
scanf("%d",&city);
for(i=0;i<road;i++)
{
scanf("%d%d%d",&s[i].beg,&s[i].end,&s[i].money);
}
for(i=1;i<=city;i++)
set[i]=i; //初始化set[]数组
sort(s,s+road,cmp);
sum=0;
for(i=0;i<road;i++) //这个地方可能不好理解 我在下面作出解释
{
if(find(s[i].beg)!=find(s[i].end)) //当两点没有连接时才可以连接两点
{
mix(s[i].beg,s[i].end); //因为已经按照价钱从高到低排序好了所以可以找到
sum+=s[i].money; //在两个点 未相连的情况下的最小权值并将其相连
}
}
j=0;
for(i=1;i<=city;i++)
{
if(set[i]==i)
j++;
if(j>1)
break;
}
if(j>1) //如果根节点不只一个则证明此组数据不是一棵树
printf("?\n");
else
printf("%d\n",sum);
}
return 0;
}
这里对上面代码中所说的地方作出解释:
/*
*例如给出一组数据
*3 3
*1 2 1
*2 3 4
*1 3 2
*我们按照价钱递减的顺序排号之后就是
*1 2 1
*1 3 2
*2 3 4
*通过查找根节点我们首先是对1 2进行根节点查找即
*find(1) , find(2)可知find(1)==1,find(2)==2 不相等
*所以合并两点的根节点即find(2)==1;同理find(3)==1
*因为2,3都已经合并所以第三组数据2 3 4的价格4就不会
* 加到最小树的结果中去
*/
for(i=0;i<road;i++)
{
if(find(s[i].beg)!=find(s[i].end)) //当两点没有连接时才可以连接两点
{
mix(s[i].beg,s[i].end); //因为已经按照价钱从高到低排序好了所以可以找到
sum+=s[i].money; //在两个点 未相连的情况下的最小权值并将其相连
}
}
最小生成树之算法记录【prime算法+Kruskal算法】【模板】的更多相关文章
- 图论——最小生成树:Prim算法及优化、Kruskal算法,及时间复杂度比较
最小生成树: 一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边.简单来说就是有且仅有n个点n-1条边的连通图. 而最小生成树就是最小权 ...
- 关于最小生成树(并查集)prime和kruskal
适合对并查集有一定理解的人. 新手可能看不懂吧.... 并查集简单点说就是将相关的2个数字联系起来 比如 房子 1 2 3 4 5 6 ...
- 最小生成树之Prim算法,Kruskal算法
Prim算法 1 .概览 普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (gr ...
- [数据结构]最小生成树算法Prim和Kruskal算法
最小生成树 在含有n个顶点的连通图中选择n-1条边,构成一棵极小连通子图,并使该连通子图中n-1条边上权值之和达到最小,则称其为连通网的最小生成树. 例如,对于如上图G4所示的连通网可以有多棵权值总 ...
- 最小生成树Prim算法和Kruskal算法(转)
(转自这位大佬的博客 http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html ) Prim算法 1.概览 普里姆算法(Pr ...
- 最小生成树的Prim算法以及Kruskal算法的证明
Prime算法的思路:从任何一个顶点开始,将这个顶点作为最小生成树的子树,通过逐步为该子树添加边直到所有的顶点都在树中为止.其中添加边的策略是每次选择外界到该子树的最短的边添加到树中(前提是无回路). ...
- 最小生成树---Prim算法和Kruskal算法
Prim算法 1.概览 普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (gra ...
- 转载:最小生成树-Prim算法和Kruskal算法
本文摘自:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html 最小生成树-Prim算法和Kruskal算法 Prim算 ...
- 数据结构与算法--最小生成树之Kruskal算法
数据结构与算法--最小生成树之Kruskal算法 上一节介绍了Prim算法,接着来看Kruskal算法. 我们知道Prim算法是从某个顶点开始,从现有树周围的所有邻边中选出权值最小的那条加入到MST中 ...
- 数据结构与算法系列----最小生成树(Prim算法&Kruskal算法)
一:Prim算法 1.概览 普里姆算法(Prim算法).图论中的一种算法.可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中.不但包含了连通图里的全部顶点(英语:Ve ...
随机推荐
- Vue.js 2.0 和 React、Augular
Vue.js 2.0 和 React.Augular 引言 这个页面无疑是最难编写的,但也是非常重要的.或许你遇到了一些问题并且先前用其他的框架解决了.来这里的目的是看看Vue是否有更好的解决方案.那 ...
- SQL Join PK ChinaJoy
P PK
- Java按正则提取字符串
在Java开发中,有时会遇到一些比较别扭的规则从字符串中提取子字符串,规则无疑是写正则表达式来表达了,那按照正则来提取子字符串就会用到java.util.regex包. java.util.regex ...
- (00)Java编程思想开篇立言。
从今天开始,在相当长的时间中我在看Java编程思想.也把这个博客作为开始.这就是一个读书的笔记.
- 转:在Eclipse中进行C/C++开发的配置方法(20140721最新版)
http://blog.csdn.net/baimafujinji/article/details/38026421 Eclipse 是一个开放源代码的.基于Java的可扩展开发平台.就其本身而言,它 ...
- 忽然想到,为什么以前iOS的工资高
听说做iOS的在上海工资也可以 前几年还行,现在也不太行了除非你水平很高 现在移动应用已经100多万了,基本饱和了以前是新的商业模式要抢时间,会的人也少
- C++静态变量对象的建立和删除,兼论MFC开始运行的起点(全局对象)
看了不少C++书,当讲到静态变量的时候,总是以int成员来举例,是啊,这样很好理解.但是如果这个静态变量是一个对象行不行呢?不仅行,有时候还非常必要,而且别有洞天. 比如: // .h 文件 clas ...
- 2.6.2 Notification的功能与用法
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout ...
- GCC优化选项-fomit-frame-pointer对于esp和ebp优化的作用
我的博客:www.while0.com -fomit-frame-pointer选项是发布产品时经常会用到的优化选项,它可以优化汇编函数中用edp协助获取堆栈中函数参数的部分,不使用edp,而是通过计 ...
- 17.2.2.1 The Slave Relay Log Slave中继日志
17.2.2.1 The Slave Relay Log Slave中继日志 中继日志, 像binary log,有一组文件组成包含events 描述数据库的修改,和一个index文件包含所有使用过的 ...