图的最小生成树MST--Kruskal算法

图的最小生成树算法中,prim算法是以顶点为单位,每次遍历剩余顶点求得最优的边权值顶点进行更新,其时间复杂度为O(n^2), 和图的边的数量没有关系,所以比较适合稠密图的MST求解。还有一个算法Kruskal,每次都是以边为基础进行添加,每次选取图中权值最小的未被添加且满足条件的边进行添加,他和图的顶点数没有关系,其时间复杂度为O(eloge),对于处理稀疏图速度会快一些。下面我们先讲解Kruskal算法:

Kruskal算法流程:

第一步:将图中数据按边的结构保存在一个边集数组中,然后对该边集数组进行按权值从小到大进行排序。

第二步:初始化father数组, 该数组是图中各顶点的连通分量标记,已经处于同一连通分量的顶点拥有相同的root标记,比如有A-B, B-C,那么ABC都处于同一个连通分量,其连通分量root标记均为A。即当father[B] = A, father[C] = A,那么他们拥有相同的root顶点,就可以说他们在相同的连通分量,此处应用了并查集(union-find-set)思想。

第三步:遍历排序好的所有边,以此判断该边的起点和终点是否已经在同一连通分量,即father[begin]和father[end]是否拥有相同的root标记节点,如果已经在一个连通分量中,则此时放弃当前边进行下一条边的处理,原因是两个顶点已经连通,若此时再添加边会形成环。但是如果两个顶点不在同一连通分量重,则处理该边加入MST,而且合并两个顶点到同一连通分量。

第四步:重复上述第三步过程,知道所有的边都处理完毕。

第五步:检查father标记的连通分量数组,当处理完所有边之后,是否存在大于一个的连通分量,如果此时连通分量只有一个那么MST已经求得,若连通分量多余一个则此图为不连通图。

处理流程大概如下所示:



代码如下所示:

#define MaxVer 105
#define MaxEdge MaxVer*(MaxVer-1)/2
typedef struct edge
{
	int begin;
	int end;
	int value;
	bool operator < (const edge& e)const
	{
		return value < e.value;
	}
}Edge, Edges[MaxEdge];

int father[MaxVer];

// 递归查询对应顶点X的连通分量标记根节点
int Find(int x)	
{
	while(father[x] > 0) 
		x = father[x];
	return x;
}

int Kruskal(Edges edges, int ver_num, int edge_num)
{

  // father 用来标记不同的连通分量 
  // 初始化为n个独立的连通分量 
  for(int i = 0; i <= ver_num; i++)
	  father[i] = 0;

  sort(edges, edges+edge_num); // 按边权值升序排序边集数组
  int sum = 0;
  int sroot, eroot;

  for(int i = 0; i < edge_num; i++)	// 从小到大权值遍历所有的边 
  {
	  sroot = Find(edges[i].begin);	// 查找起点的连通分量root标记
	  eroot = Find(edges[i].end);	// 查找终点的连通分量root标记
	 
	  // 起点和终点的root标记不同表示两个顶点处在不同的连通分量,
	  // 该边也没有加入到生成树,若root标记相同,则说明两个顶点位于
	  // 相同的连通分量再添加边会形成环路

	  if (sroot != eroot)	{	// 起点和终点不在一个连通分量,合并分量,加入边
		  father[sroot] = eroot;// 将起点的连通标志 指向终点 从而合并
		  sum += edges[i].value;
	  }
  }

  int tmp_count = 0;
  for (int i = 1; i <= ver_num; i++)
  {
	  if (father[i] == 0)	// 判断处理完所有边之后是不是所有的顶点都处于同一个连通分量
		  tmp_count++;		// 如果图为连通图则为一个,否则可能会有多个连通分量无法形成MST
  }

  if (tmp_count > 1) 
	  return -1;
  return sum;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值