最小生成树算法 prime算法和克鲁斯卡尔算法
克鲁斯卡尔算法
思路 优先队列+并查集
Kuskal算法
【算法简介】:上一篇中的Prime算法是一种“加点式的算法”,而Kuskal算法是一种“加边式的算法”;Kuskal算法与Prime算法都是一种贪心算法,但Kruskal算法对图中存在相同权值的边时也有效。
【算法思想】:算法对权值从小到大排序,每次选取当前权值最小的边加入结点,直到所有的结点都已加入结点。算法中用到了并查集的思想(并查集),通过并查集来判断两个结点是否有共同的父节点,如果有,则表明两个结点已经联通。如果没有,就将两个结点联通,记录路径。
Kruskal算法也是采用贪心算法的思想,运行时间为O(nlogn)。
代码设计
1、利用优先级队列将权值小的边放到队列最前,优先出对,保证了每次选择的都是权值最小的边。
2、利用并查集的查找及结合把同处同一连通分量中的顶点连到同一父节点下。这样,每次判断是否构成回路,只要判断父节点是否相同的即可。
1.1 存图方式
使用结构体数组来存图;
//因为每条边需要保存数据 起始节点 ,到达节点 ,花费(路的长度)
struct edge{
int start;//出发节点
int target;//目标节点
int cost;//花费(路径的长度)
};
//因为定义了边类型,需要使用优先队列,即需要比大小,需要重新定义<
bool operator<( edge a, edge b ){
//升序
if(a.cost>b.cost)
return true;
else
return false;
}
1.2 并查集思想(重点)
我们可以把每个连通分量看成一个集合,该集合包含了连通分量的所有点。而具体的连通方式无关紧要,好比集合中的元素没有先后顺序之分,只有“属于”与“不属于”的区别。图的所有连通分量可以用若干个不相交集合来表示。
分为三部分:
(1)初始化:使每个结点的初始根节点为自己,并且每个结点构成一颗树,树的深度是1;
(2)查找:使用递归来查找每个结点的父亲结点;
(3)合并:将不同父节点的结点合并;
注:这里的并查集是优化后的,即:进行了路径压缩。如果题目中无要求,可以只写简单的并查集算法
void inin(){
//为每个节点初始化它的父节点
for(int i=0;i<nodeNum;i++){
father[i]=i;//初始化一下寻找父节点的数组
}
}
int find(int x){
//查看节点x的最终父节点,即并查集,方便查看是否属于同一集合中
if(x == father[x])//此时该结点孤立没有父节点
return x;
else
find(father[x]);
}
bool join(int node_start,int node_target){
//用于判断这俩结点是否在同一连通分支中,是,true,不是 false
//并查集的使用
int start_father = find(node_start);
int target_father = find(node_target);
if( start_father == target_father)//这俩结点在同一个连通分支中
return true;
if( start_father != target_father){
//这俩结点不在同一个连通分支中
father[target_father] = find(start_father);//将两个点放到同一个集合中去
return false;
}
}
1.3 Kuskal算法主体
变量每个结点;
int Kruskal(){
inin();
int num=0;//已经处理了几个结点
while(!myqueue.empty()