kruskal算法
克鲁斯卡尔算法的基本思想是以边为主导地位,始终选择当前可用(所选的边不能构成回路)的最小权植边。所以Kruskal算法的第一步是给所有的边按照从小到大的顺序排序。这一步可以直接使用库函数qsort或者sort。接下来从小到大依次考察每一条边(u,v)。
具体实现过程如下:
<1> 设一个有n个顶点的连通网络为G(V,E),最初先构造一个只有n个顶点,没有边的非连通图T={V,空},图中每个顶点自成一格连通分量。
<2> 在E中选择一条具有最小权植的边时,若该边的两个顶点落在不同的连通分量上,则将此边加入到T中;否则,即这条边的两个顶点落到同一连通分量上,则将此边舍去(此后永不选用这条边),重新选择一条权植最小的边。
<3> 如此重复下去,直到所有顶点在同一连通分量上为止。
简单来说就是挑最小边,加入当前的“树”中,而挑最小边可以遍历,时间复杂度是O(n),如果用最小堆时间复杂度可以达到o(logn)
并入当前树中又不会形成环路解决方法当然就是用并查集
并查集(Union-Find set)这个数据结构可以方便快速的解决这个问题。基本的处理思想是:初始时把每个对象看作是一个单元素集合;然后依次按顺序读入联通边,将连通边中的两个元素合并。在此过程中将重复使用一个搜索(Findroot)运算,确定一个集合在那个集合中。当读入一个连通边(u,v)时,先判断u和v是否在同一个集合中,如果是则不用合并;如果不是,则用一个合并(Union)运算把u、v所在集合合并,使得这两个集合中的任意两个元素都连通。因此并查集在处理时,主要用到搜索和合并两个运算。
为了方便并查集的描述与实现,通常把先后加入到一个集合中的元素表示成一个树结构,并用根结点的序号来表示这个集合。因此定义一个uf[n]的数组,uf[i]中存放的就是结点i所在的树中结点i的父亲节点的序号。例如,如果uf[4]=5,就是说4号结点的父亲结点是5号结点。约定:如果i的父结点(即uf[i])是负数,则表示结点i就是它所在的集合的根结点,因为集合中没有结点的序号是负的;并且用负数的绝对值作为这个集合中所含结点的个数。例如,如uf[7]=-4,说明7号结点就是它所在集合的根结点,这个集合有四个元素。初始时结点的值为-1(每个结点都是根结点,只包含它自己一个元素)。
#include<iostream>
#include<algorithm>
#include<cstdlib>
using namespace std;
#define inf 99999
#define maxsize 10
typedef struct Graph//定义图 a,b,代表边的两顶点,w表权值
{
int a,b;
int w;
}G;
int nv,ne;//图的点、边
int uf[maxsize];//并查集数组
G minheap[maxsize];//最小堆数组
int size=0;
void insert(G t)//向最小堆中插入元素
{
int i=++size;
for(;minheap[i/2].w>t.w;i/=2)
minheap[i]=minheap[i/2];
minheap[i]=t;
}
void down()//下滤
{
int ch;
for(int pa=1;pa*2<=size;pa=ch)
{
ch=pa*2;
if(ch!=size&&minheap[ch].w>minheap[ch+1].w)
ch=ch+1;
if(minheap[ch].w<minheap[pa].w)
swap(minheap[ch],minheap[pa]);
else
break;
}
}
G del()//从最小堆中删除元素
{
if(size>0)
{
G mx=minheap[1];
minheap[1]=minheap[size--];
down();
return mx;
}
}
int findroot(int u)//发现根
{
int i;
for( i=u;uf[i]!=-1;i=uf[i]);
return i;
}
void Create()//创建最小堆
{
int a,b,weigh,k=0;
G g;
for(int i=1;i<=ne;i++)//直接输入图的边及权值
{
cin>>a>>b>>weigh;
g.a=a;
g.b=b;
g.w=weigh;
insert(g);//放入最小堆
}
for(int i=0;i<=nv;i++)//初始化并查集
uf[i]=-1;
minheap[0].a=minheap[0].b=0;
minheap[0].w=-1;
}
int kruskal()
{
int a,b,sum=0;
G tmp;
for(int i=1;i<=ne;i++)
{
tmp=del();//每次从最小堆中找到一个最小的边保存并删除
a=findroot(tmp.a);//找到a的根
b=findroot(tmp.b);//找到b的根
if(a!=b)//合并相当于union函数
{
uf[b]=a;
sum+=tmp.w;
}
}
return sum;//返回最小生成树的总权值
}
int main()
{
cin>>nv>>ne;
Create();
cout<<kruskal()<<endl;
return 0;
}
Kruskal算法是一种求解最小生成树的贪心算法,通过选择最小权值边并避免形成环路来逐步构建连通图。算法首先按边权值排序,然后使用并查集判断新加入的边是否会形成环路。如果不会,就将边加入结果中。并查集在此过程中起关键作用,用于快速判断元素是否属于同一集合,以防止形成环路。通过并查集的搜索和合并操作,可以高效地实现Kruskal算法,优化后的算法时间复杂度可达O(n log n)。
&spm=1001.2101.3001.5002&articleId=79504472&d=1&t=3&u=a3af24b17fbe40c8beb53b8805702ab7)
3470





