博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

最小生成树

  最小生成树是指对于一个带边权的图,要求删去一些边,使点之间仍相连且剩下的边权尽可能小。以下给出两种方法,都用到了贪心法:
    ①Prim:先建立两个集合S和T,分别表示已连接和未连接的点集。开始时把在S中加入任意一个点x,并在T中加入1到n所有除x以外的点。然后每次找出T中连接到S边权最小的一个点把它加入S,同时从T删除,直到T为空。主要程序段如下:

    int lowcost[MAXN],closest[MAXN];
    int prim(int v0)
    {
        int i,j,mindis,minone;
        int ans = 0;/*用来记录最小生成树的总长度*/
        /*各点距离初始化*/
        for(i = 0;i < n;i++)
        {
            lowcost[i] = cost[v0][i];
            closest[i] = v0;
        }
        for(i = 0;i < n-1;i++)
        {
            mindis = UPPERDIS;
            for(j = 0;j < n;j++)
              if(lowcost[j] && mindis > lowcost[j])
              {
                  mindis = lowcost[j];
                  minone = j;
              }
            /*将找到的最近点加入最小生成树*/
            ans += lowcost[minone];
            lowcost[minone] = 0;
            /*修正其他点到最小生成树的距离*/
            for(j = 0;j < n;j++)
              if(cost[minone][j] < lowcost[j])
              {
                  lowcost[j] = cost[j][minone];
                  closest[j] = minone;
              }
        }
        return ans;
    } 

    时间复杂度为O(n^2)
    ②Kruskal:这种方法很简单,也很高效。先把边从小到大排序,依次枚举边并判断其两点是否在同一连通块(即是否已相连),若没有就加入该边。朴素算法时间复杂度为O(n^2),可用并查集或斐波那契堆加速,以下给出并查集加速代码(网上找来的,莫名地长):

   #include <stdio.h>
    #include <stdlib.h>
    #define MAX 100
    /* 定义边(x,y),权为w */
    typedef struct{
     int x, y;
     int w;
    }edge; 
    edge e[MAX];
    /* rank[x]表示x的秩 */
    int rank[MAX];
    /* father[x]表示x的父节点 */
    int father[MAX];
    int sum;
 
    /* 比较函数,按权值(相同则按x坐标)非降序排序 */
    int cmp(const void *a, const void *b){
        if ((*(edge *)a).w == (*(edge *)b).w){
           return (*(edge *)a).x - (*(edge *)b).x;
         }        return (*(edge *)a).w - (*(edge *)b).w;
    }
 
    /* 初始化集合 */
    void Make_Set(int x){
     father[x] = x;
     rank[x] = 0;
    }
 
    /* 查找x元素所在的集合,回溯时压缩路径 */
    int Find_Set(int x){
        if (x != father[x]){            father[x] = Find_Set(father[x]);
         }
         return father[x];
    }
 
    /* 合并x,y所在的集合 */
    void Union(int x, int y, int w){
         if (x == y) return;
         /* 将秩较小的树连接到秩较大的树后 */
         if (rank[x] > rank[y]){             father[y] = x;
         }
         else{
             if (rank[x] == rank[y]){
                 rank[y]++;
              }
             father[x] = y;        }
     sum += w;
    }
 
    /* 主函数 */
    int main(){
     int i, n;
     int x, y;
     char chx, chy;
 
     /* 读取边的数目 */
     scanf("%d", &n);
     getchar();
 
     /* 读取边信息并初始化集合 */
     for (i = 0; i < n; i++){
          scanf("%c %c %d", &chx, &chy, &e[i].w);
          getchar();
          e[i].x = chx - 'A';
          e[i].y = chy - 'A';
          Make_Set(i);
     }
 
     /* 将边排序 */
     qsort(e, n, sizeof(edge), cmp);
 
     sum = 0;
 
         for (i = 0; i < n; i++){
              x = Find_Set(e[i].x);
              y = Find_Set(e[i].y);
              if (x != y){
                   printf("%c - %c : %d\n", e[i].x + 'A', e[i].y + 'A', e[i].w);
                   Union(x, y, e[i].w);
              }
         }
 
     printf("Total:%d\n", sum);
     return 0; 
    }

     并查集加速后,时间复杂度为O(n*log(n))。

posted @ 2015-11-19 21:24  swm_sxt  阅读(197)  评论(0编辑  收藏  举报