kruskal算法
kruskal算法其实也是和prim算法一样求无向图的最小生成树,也属于贪心算法,不过prim算法的复杂度为O(n^2),适用于稠密图,而kruskal算法的复杂度为O(eloge),适用于稀疏图。
kruskal算法描述很容易理解,如下
1.设连通网N=(V,{E}),令最小生成树初始状态为只有n个顶点而无边的非连通图T=(V,{F}),每个顶点自成一个连通分量
2.在E中选取代价最小的边,加入到T中,若它的添加使T 中产生回路,则舍去此边,选取下一条代价最小的边
3.依此类推,直至T中有 n-1 条边为止
但是如何在判定边的添加是否成回路上不失效率?推荐使用不相交集合数据结构--即并查集。
在并查集中,顾名思义,“并”和“查”是非常重要的两个功能,就是在一系列不相交的集合上,我们要迅速找出给定元素所属的集合或者
合并两个集合。
这里的并查集采用了带路径压缩的按秩合并策略来实现并查集,欲知并查集详情请具体看算法导论关于不相交集合的数据结构那一章,具体
实现看代码,有解释,不急
PS:还是老样子,请把箭头去掉,全当无向图,蛋定蛋定
- //kruskal用于无向图的最小生成树
- //稀疏图
- #include<iostream>
- #include<algorithm>
- #include<functional>
- using namespace std;
- //并查集的实现
- //---------------------------------------------------------------------
- const int MAXNUM=101;
- int parent[MAXNUM]; //parent[i]=j表示j是i的父母
- int rank[MAXNUM]; //rank[i]是i的高度的一个上界
- void make_set(int x) //建立单元素集合,父结点为自己,秩为0
- {
- parent[x]=x;
- rank[x]=0;
- }
- //find_set是一种两趟方法,一趟是沿查找路径上升,直至找到根
- //第二趟是沿查找路径下降,以便更新每个结点,使之直接指向根
- //此为带路径压缩的查找
- int find_set(int x)
- {
- if(x!=parent[x])
- parent[x]=find_set(parent[x]);
- return parent[x];
- }
- //合并x所属的集合和y所属的集合为一个集合
- void union_set(int x,int y)
- {
- x=find_set(x);
- y=find_set(y);
- if(rank[x]>rank[y]) //让较高的秩作为父结点
- {
- parent[y]=x;
- }
- else
- {
- parent[x]=y;
- if(rank[x]==rank[y]) //如果秩相等,怎任选一方作为父结点,同时秩升1
- ++rank[y];
- }
- }
- //---------------------------------------------------------------------
- //9*9,图的路径
- int MAP[MAXNUM][MAXNUM]={
- {INT_MAX, 2,INT_MAX,INT_MAX,INT_MAX, 9, 15,INT_MAX,INT_MAX},
- { 2,INT_MAX, 4,INT_MAX,INT_MAX,INT_MAX, 6,INT_MAX,INT_MAX},
- {INT_MAX, 4,INT_MAX, 2,INT_MAX,INT_MAX,INT_MAX,INT_MAX, 15},
- {INT_MAX,INT_MAX, 2,INT_MAX, 1,INT_MAX,INT_MAX,INT_MAX, 1},
- {INT_MAX,INT_MAX,INT_MAX, 1,INT_MAX, 6,INT_MAX, 3,INT_MAX},
- { 9,INT_MAX,INT_MAX,INT_MAX, 6,INT_MAX,INT_MAX, 11,INT_MAX},
- { 15, 6,INT_MAX,INT_MAX,INT_MAX,INT_MAX,INT_MAX, 15, 2},
- {INT_MAX,INT_MAX,INT_MAX,INT_MAX, 3, 11, 15,INT_MAX, 4},
- {INT_MAX,INT_MAX, 15, 1,INT_MAX,INT_MAX, 2, 4,INT_MAX}
- };
- struct edge
- {
- int x,y; //x到y
- int w; //路径代价
- };
- int path[MAXNUM]; //path[i]=x表示e[x]边被选中
- edge e[MAXNUM];
- class isless:binary_function<edge&,edge&,bool>
- {
- public:
- bool operator()(edge& a,edge& b)
- {
- return a.w<b.w;
- }
- };
- //前提图中的边数>=n-1
- void kruskal(int n)
- {
- int i,j;
- int k=0;
- for(i=0;i<n;++i)
- make_set(i);
- for(i=0;i<n-1;++i) //初始化e[]数组,由于是无向图,故邻接矩阵有很多重复的边
- {
- for(j=i+1;j<n;++j)
- if(MAP[i][j]!=INT_MAX)
- {
- e[k].x=i;
- e[k].y=j;
- e[k++].w=MAP[i][j];
- }
- }
- sort(e,e+k,isless()); //对e[]数组进行排序,使其非递减
- j=i=0; //i为选中边的条数,j指向测试边,既e[j]
- while(j<k&&i<n-1)
- {
- if(find_set(e[j].x)!=find_set(e[j].y)) //若边的两个顶点不属于同一个集合
- {
- path[i++]=j; //记录改路径在e中的下标
- union_set(e[j].x,e[j].y); //将相关的顶点加入集合中
- }
- ++j;
- }
- }
- int main()
- {
- kruskal(9);
- int s=0;
- for(int i=0;i<8;++i)
- {
- int x=e[path[i]].x,
- y=e[path[i]].y,
- w=e[path[i]].w;
- s+=w;
- cout<<char(x+'a')<<"到"<<char(y+'a')<<':'<<w<<endl;
- }
- cout<<"最小生成树权值之和:"<<s<<endl;
- return 0;
- }