最小生成树-Kruscal算法

所谓最小生成树,就是在一个具有N个顶点的带权连通图G中,如果存在某个子图G',其包含了图G中的所有顶点和一部分边,且不形成回路,并且子图G'的各边权值之和最小,则称G'为图G的最小生成树。
      由定义我们可得知最小生成树的三个性质:
      最小生成树不能有回路。
      最小生成树可能是一个,也可能是多个。

      最小生成树边的个数等于顶点的个数减一。

 克鲁斯卡尔算法的核心思想是:在带权连通图中,不断地在边集合中找到最小的边,如果该边满足得到最小生成树的条件,就将其构造,直到最后得到一颗最小生成树。
       克鲁斯卡尔算法的执行步骤:
       第一步:在带权连通图中,将边的权值排序;
       第二步:判断是否需要选择这条边(此时图中的边已按权值从小到大排好序)。判断的依据是边的两个顶点是否已连通,如果连通则继续下一条;如果不连通,那么就选择使其连通。
       第三步:循环第二步,直到图中所有的顶点都在同一个连通分量中,即得到最小生成树。

下面我用图示法来演示克鲁斯卡尔算法的工作流程,如下图:

最小生成树-Kruscal算法_第1张图片
首先,将图中所有的边排序(从小到大),我们将以此结果来选择。排序后各边按权值从小到大依次是:
HG < (CI=GF) < (AB=CF) < GI < (CD=HI) < (AH=BC) < DE < BH < DF
接下来,我们先选择HG边,将这两个点加入到已找到点的集合。这样图就变成了,如图
最小生成树-Kruscal算法_第2张图片
继续,这次选择边CI(当有两条边权值相等时,可随意选一条),此时需做判断。
每次添加一个边的时候都要判断添加这条边后时候会产生回路, 如果不产生回路就添加这条边,如果产生就继续判断下一条边。

根据判断法则,不会形成回路,将点C和点I连通,并将点C和点I加入到集合中。如图:

最小生成树-Kruscal算法_第3张图片
继续,这次选择边GF,根据判断法则,不会形成回路,将点G和点F连通,并将点F加入到集合中。如图:
最小生成树-Kruscal算法_第4张图片
继续,这次选择边AB,根据判断法则,不会形成回路,将其连通,并将点A和点B加入到集合中。如图:
最小生成树-Kruscal算法_第5张图片
继续,这次选择边CF,根据判断法则,不会形成回路,将其连通,此时这两个点已经在集合中了,所以不用加入。如图:
最小生成树-Kruscal算法_第6张图片
继续,这次选择边GI,根据判断法则,会形成回路,如下图,直接进行下一次操作。
最小生成树-Kruscal算法_第7张图片
继续,这次选择边CD,根据判断法则,不会形成回路,将其连通,并将点D加入到集合中。如图:
最小生成树-Kruscal算法_第8张图片
继续,这次选择边HI,根据判断法则,会形成回路,直接进行下一次操作。
继续,这次选择边AH,根据判断法则,不会形成回路,将其连通,此时这两个点已经在集合中了,所以不用加入。
继续,这次选择边BC,根据判断法则,会形成回路,直接进行下一次操作。
继续,这次选择边DE,根据判断法则,不会形成回路,将其连通,并将点E加入到集合中。如图:
最小生成树-Kruscal算法_第9张图片
继续,这次选择边BH,根据法则,会形成回路,进行下一次操作。
最后选择边DF,根据法则,会形成回路,不将其连通,也不用加入到集合中。

好了,所有的边都遍历完成了,所有的顶点都在同一个连通分量中,我们得到了这颗最小生成树。

下面用一道POJ的题目来看一下代码实现:

//POJ2395

#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 2010;
const int MAXM = 10010;
int N, M;
int pre[MAXN], rank[MAXN];
struct Edge 
{
  int x, y;
  int cost;
};

Edge edge[MAXM];

bool cmp(const Edge& a, const Edge& b)
{
  return a.cost < b.cost;
}

void Init()
{
  for(size_t i = 0; i <= N; ++i)
  {
    rank[i] = 0;
    pre[i] = i;
  }
}

int find(int x)
{
  if(x != pre[x])
  {
    pre[x] = find(pre[x]);
  }
  return pre[x];
}

void Union(int x, int y)
{
  x = find(x);
  y = find(y);
  if(x == y)
    return;
  if(rank[x] > rank[y])
    pre[y] = x;
  else 
  {
    if(rank[x] == rank[y])
    {
      rank[y]++;
    }
    pre[x] = y;
  }
}

int Kruscal()
{
  int ans = 0;
  int sum = 0;
  int cnt = 0;
  sort(edge, edge + M, cmp);
  Init();
  for(size_t i = 0; i < M; ++i)
  {
    if(find(edge[i].x) !=  find(edge[i].y))
    {
      Union(edge[i].x, edge[i].y);
      if(ans < edge[i].cost)//get the maximun edge in the minimum spanning trees
        ans = edge[i].cost;
      //sum += edge[i].cost;//compute the total length of minimum spanning trees
      cnt++;
      if(cnt == N - 1)
        break;
    }
  }
  return ans;
}

int main()
{
  while(cin >> N >> M)
  {
    for(size_t i = 0; i < M; ++i)
    {
      cin >> edge[i].x >> edge[i].y >> edge[i].cost ;
    }
    cout << Kruscal() << endl;
  }
}


你可能感兴趣的:(算法,ACM,图论,Kruscal算法)