最小生成树问题-Kruskal方法求解

先构造一个只含 n 个顶点、而边集为空的子图,把子图中各个顶点看成各棵树上的根结点,之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,即把两棵树合成一棵树,反之,若该条边的两个顶点已落在同一棵树上,则不可 取,而应该取下一条权值最小的边再试之。依次类推,直到森林中只有一棵树,也即子图中含有 n-1 条边为止。


时间复杂度为为O(e^2), 使用并查集优化后复杂度为 O(eloge),与网中的边数有关,适用于求边稀疏的网的最小生成树。

 

 

 用int parent[i] 保存每个下标为i的节点的双亲节点的下标。

parent []   

初始 parent[]={0},如果parent[i]=0表示下标i的结点没有双亲节点,此时这棵树的根结点就是i。如果parent[i]=n
,n>0,说明i结点的双亲是n。然后在找n的双亲,反复迭代,直到找到这棵树的根结点为止。这也是程序比较巧妙的地方,

只用一个一维数组就表示出了图中所有节点在各个子树中的关系,虽然只能给出每个节点的双亲是哪个,但是对于查找

其所在树的根节点这些信息就足够了。然后通过判断两个节点的所在子树的根节点是否相同,来确定这两个节点是否在

同一个子树上。

由此,可定义一个Find()函数用于查找下标为f节点所在树的根结点。

/* 迭代查找f节点的双亲,直到找到f节点所在树的根结点为止 */
int Find(int *parent, int f)
{
    while ( parent[f] > 0)
    {
        f = parent[f];
    }
    return f;
}

 

对边集数组做循环遍历:从权值最小的边开始循环每一条边(完整代码中,在这之前边集数组已经按照边的权值排好序了)

通过判断每条边的begin结点和end结点所在的子树是否有相同的根结点来判断begin和end是否在同一个子树上。如果在同一棵子树上,直接跳过此次循环。

如果没在同一棵子树上,说明begin和end结点可以相连。然后,begin结点所在的树的根结点挂到end节点所在树的根节点上。

  for (i = 0; i < G.numEdges; i++)    
    {
        n = Find(parent,edges[i].begin);    //查找下标为edges[i].begin结点所在树的根结点
        m = Find(parent,edges[i].end);        //查找下标为edges[i].end结点所在树的根结点
        if (n != m)        /* 假如n与m不等,说明两个结点在图中不同的子树上,可以将两个子树相连,使n的双亲是m */
        {
            parent[n] = m;    
            //打印edges[i].begin和edges[i].end组成的边
            printf("(%d, %d) %d\n", edges[i].begin, edges[i].end, edges[i].weight);
        }
    }

 

 完整代码如下:

#include "stdio.h"    
#include "stdlib.h"   
#include "io.h"  
#include "math.h"  
#include "time.h"

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

typedef int Status;    /* Status是函数的类型,其值是函数结果状态代码,如OK等 */

#define MAXEDGE 20
#define MAXVEX 20
#define INFINITY 65535

typedef struct
{
    int arc[MAXVEX][MAXVEX];
    int numVertexes, numEdges;
}MGraph;

typedef struct
{
    int begin;
    int end;
    int weight;
}Edge;   /* 对边集数组Edge结构的定义 */

/* 构建图 */
void CreateMGraph(MGraph *G)
{
    int i, j;

    /* printf("请输入边数和顶点数:"); */
    G->numEdges=15;
    G->numVertexes=9;

    for (i = 0; i < G->numVertexes; i++)/* 初始化图 */
    {
        for ( j = 0; j < G->numVertexes; j++)
        {
            if (i==j)
                G->arc[i][j]=0;
            else
                G->arc[i][j] = G->arc[j][i] = INFINITY;
        }
    }

    G->arc[0][1]=10;
    G->arc[0][5]=11; 
    G->arc[1][2]=18; 
    G->arc[1][8]=12; 
    G->arc[1][6]=16; 
    G->arc[2][8]=8; 
    G->arc[2][3]=22; 
    G->arc[3][8]=21; 
    G->arc[3][6]=24; 
    G->arc[3][7]=16;
    G->arc[3][4]=20;
    G->arc[4][7]=7; 
    G->arc[4][5]=26; 
    G->arc[5][6]=17; 
    G->arc[6][7]=19; 

    for(i = 0; i < G->numVertexes; i++)
    {
        for(j = i; j < G->numVertexes; j++)
        {
            G->arc[j][i] =G->arc[i][j];
        }
    }

}

/* 交换权值 以及头和尾 */
void Swapn(Edge *edges,int i, int j)
{
    int temp;
    temp = edges[i].begin;
    edges[i].begin = edges[j].begin;
    edges[j].begin = temp;

    temp = edges[i].end;
    edges[i].end = edges[j].end;
    edges[j].end = temp;

    temp = edges[i].weight;
    edges[i].weight = edges[j].weight;
    edges[j].weight = temp;
}

/* 对权值进行排序 */
void sort(Edge edges[],MGraph *G)
{
    int i, j;
    for ( i = 0; i < G->numEdges; i++)
    {
        for ( j = i + 1; j < G->numEdges; j++)
        {
            if (edges[i].weight > edges[j].weight)
            {
                Swapn(edges, i, j);
            }
        }
    }
    printf("权排序之后的为:\n");
    for (i = 0; i < G->numEdges; i++)
    {
        printf("(%d, %d) %d\n", edges[i].begin, edges[i].end, edges[i].weight);
    }

}

/* 迭代查找f节点的双亲,直到找到f节点所在树的根结点为止 */
int Find(int *parent, int f)
{
    while ( parent[f] > 0)
    {
        f = parent[f];
    }
    return f;
}

/* 生成最小生成树 */
void MiniSpanTree_Kruskal(MGraph G)
{
    int i, j, n, m;
    int k = 0;
    int parent[MAXVEX];    /* 定义一数组,存放每个节点的双亲 */
    
    Edge edges[MAXEDGE];    /* 定义边集数组,edge的结构为begin,end,weight,均为整型 */

    /* 用来构建边集数组并排序********************* */
    for ( i = 0; i < G.numVertexes-1; i++)
    {
        for (j = i + 1; j < G.numVertexes; j++)
        {
            if (G.arc[i][j]<INFINITY)
            {
                edges[k].begin = i;
                edges[k].end = j;
                edges[k].weight = G.arc[i][j];
                k++;
            }
        }
    }
    sort(edges, &G);
    /* ******************************************* */

    for (i = 0; i < G.numVertexes; i++)
        parent[i] = 0;    /* 初始化数组值为0 */

    printf("打印最小生成树:\n");

    for (i = 0; i < G.numEdges; i++)    /* 循环每一条边 */
    {
        n = Find(parent,edges[i].begin);    //查找下标为edges[i].begin结点所在树的根结点
        m = Find(parent,edges[i].end);        //查找下标为edges[i].end结点所在树的根结点
        if (n != m)        /* 假如n与m不等,说明两个结点在图中不同的子树上,可以将两个子树相连,使n的双亲是m */
        {
            parent[n] = m;    
            //打印edges[i].begin和edges[i].end组成的边
            printf("(%d, %d) %d\n", edges[i].begin, edges[i].end, edges[i].weight);
        }
    }
}

int main(void)
{
    MGraph G;
    CreateMGraph(&G);
    MiniSpanTree_Kruskal(G);
    return 0;
}

 结果:

                 
posted @ 2017-08-11 16:05  Allen101  阅读(370)  评论(0编辑  收藏  举报