Kruskal算法(一)之 C語言詳解


最小生成樹

在含有n個頂點的連通圖中選擇n-1條邊,構成一棵極小連通子圖,並使該連通子圖中n-1條邊上權值之和達到最小,則稱其為連通網的最小生成樹。 

例如,對於如上圖G4所示的連通網可以有多棵權值總和不相同的生成樹。

克魯斯卡爾算法介紹

克魯斯卡爾(Kruskal)算法,是用來求加權連通圖的最小生成樹的算法。

基本思想:按照權值從小到大的順序選擇n-1條邊,並保證這n-1條邊不構成回路。 
具體做法:首先構造一個只含n個頂點的森林,然后依權值從小到大從連通網中選擇邊加入到森林中,並使森林中不產生回路,直至森林變成一棵樹為止。

克魯斯卡爾算法圖解

以上圖G4為例,來對克魯斯卡爾進行演示(假設,用數組R保存最小生成樹結果)。

第1步:將邊<E,F>加入R中。 
    邊<E,F>的權值最小,因此將它加入到最小生成樹結果R中。 
第2步:將邊<C,D>加入R中。 
    上一步操作之后,邊<C,D>的權值最小,因此將它加入到最小生成樹結果R中。 
第3步:將邊<D,E>加入R中。 
    上一步操作之后,邊<D,E>的權值最小,因此將它加入到最小生成樹結果R中。 
第4步:將邊<B,F>加入R中。 
    上一步操作之后,邊<C,E>的權值最小,但<C,E>會和已有的邊構成回路;因此,跳過邊<C,E>。同理,跳過邊<C,F>。將邊<B,F>加入到最小生成樹結果R中。 
第5步:將邊<E,G>加入R中。 
    上一步操作之后,邊<E,G>的權值最小,因此將它加入到最小生成樹結果R中。 
第6步:將邊<A,B>加入R中。 
    上一步操作之后,邊<F,G>的權值最小,但<F,G>會和已有的邊構成回路;因此,跳過邊<F,G>。同理,跳過邊<B,C>。將邊<A,B>加入到最小生成樹結果R中。

此時,最小生成樹構造完成!它包括的邊依次是:<E,F> <C,D> <D,E> <B,F> <E,G> <A,B>

克魯斯卡爾算法分析

根據前面介紹的克魯斯卡爾算法的基本思想和做法,我們能夠了解到,克魯斯卡爾算法重點需要解決的以下兩個問題: 
問題一 對圖的所有邊按照權值大小進行排序。 
問題二 將邊添加到最小生成樹中時,怎么樣判斷是否形成了回路。

問題一很好解決,采用排序算法進行排序即可。

問題二,處理方式是:記錄頂點在"最小生成樹"中的終點,頂點的終點是"在最小生成樹中與它連通的最大頂點"(關於這一點,后面會通過圖片給出說明)。然后每次需要將一條邊添加到最小生存樹時,判斷該邊的兩個頂點的終點是否重合,重合的話則會構成回路。 以下圖來進行說明:

在將<E,F> <C,D> <D,E>加入到最小生成樹R中之后,這幾條邊的頂點就都有了終點:

(01) C的終點是F。 
(02) D的終點是F。 
(03) E的終點是F。 
(04) F的終點是F。

關於終點,就是將所有頂點按照從小到大的順序排列好之后;某個頂點的終點就是"與它連通的最大頂點"。 因此,接下來,雖然<C,E>是權值最小的邊。但是C和E的重點都是F,即它們的終點相同,因此,將<C,E>加入最小生成樹的話,會形成回路。這就是判斷回路的方式。

克魯斯卡爾算法的代碼說明

有了前面的算法分析之后,下面我們來查看具體代碼。這里選取"鄰接矩陣"進行說明,對於"鄰接表"實現的圖在后面的源碼中會給出相應的源碼。

1. 基本定義

// 鄰接矩陣
typedef struct _graph
{
    char vexs[MAX];       // 頂點集合
    int vexnum;           // 頂點數
    int edgnum;           // 邊數
    int matrix[MAX][MAX]; // 鄰接矩陣
}Graph, *PGraph;

// 邊的結構體
typedef struct _EdgeData
{
    char start; // 邊的起點
    char end;   // 邊的終點
    int weight; // 邊的權重
}EData;

Graph是鄰接矩陣對應的結構體。 
vexs用於保存頂點,vexnum是頂點數,edgnum是邊數;matrix則是用於保存矩陣信息的二維數組。例如,matrix[i][j]=1,則表示"頂點i(即vexs[i])"和"頂點j(即vexs[j])"是鄰接點;matrix[i][j]=0,則表示它們不是鄰接點。 
EData是鄰接矩陣邊對應的結構體。

2. 克魯斯卡爾算法

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<string.h>
#define MAX 100
#define INF -1
typedef struct Graph
{
    char vexs[MAX];
    int vexnum;
    int edgnum;
    int matrix[MAX][MAX];
}Graph,*PGraph;

typedef struct EdgeData
{
    char start;
    char end;
    int weight;
}EData;

static int get_position(Graph g,char ch)
{
    int i;
    for(i=0;i<g.vexnum;i++)
        if(g.vexs[i]==ch)
            return i;
    return -1;
}

Graph* create_graph()
{
    char vexs[]= {'A','B','C','D','E','F','G'};
    int matrix[][7]= {
        {0,12,INF,INF,INF,16,14},
        {12,0,10,INF,INF,7,INF},
        {INF,10,0,3,5,6,INF},
        {INF,INF,3,0,4,INF,INF},
        {INF,INF,5,4,0,INF,8},
        {16,7,6,INF,2,0,9},
        {14,INF,INF,INF,8,9,0}};
    int vlen=sizeof(vexs)/sizeof(vexs[0]);
    int i,j;
    Graph *pG;
    if((pG=(Graph*)malloc(sizeof(Graph)))==NULL)
        return NULL;
    memset(pG,0,sizeof(pG));
    pG->vexnum=vlen;
    for(i=0;i<pG->vexnum;i++)
        pG->vexs[i]=vexs[i];
    for(i=0;i<pG->vexnum;i++)
        for(j=0;j<pG->vexnum;j++)
            pG->matrix[i][j]=matrix[i][j];
    for(i=0;i<pG->vexnum;i++)
    {
        for(j=0;j<pG->vexnum;j++)
        {
            if(i!=j&&pG->matrix[i][j]!=INF)
                pG->edgnum++;
        }
    }
    pG->edgnum/=2;
    return pG;
}

void print_graph(Graph G)
{
    int i,j;
    printf("Matrix Graph: \n");
    for(i=0;i<G.vexnum;i++)
    {
        for(j=0;j<G.vexnum;j++)
            printf("%10d ",G.matrix[i][j]);
        printf("\n");
    }
}

EData* get_edges(Graph G)
{
    EData *edges;
    edges=(EData*)malloc(G.edgnum*sizeof(EData));
    int i,j;
    int index=0;
    for(i=0;i<G.vexnum;i++)
    {
        for(j=i+1;j<G.vexnum;j++)
        {
            if(G.matrix[i][j]!=INF)
            {
                edges[index].start=G.vexs[i];
                edges[index].end=G.vexs[j];
                edges[index].weight=G.matrix[i][j];
                index++;
            }
        }
    }
    return edges;
}

void sort_edges(EData *edges,int elen)
{
    int i,j;
    for(i=0;i<elen;i++)
    {
        for(j=i+1;j<elen;j++)
        {
            if(edges[i].weight>edges[j].weight)
            {
                EData tmp=edges[i];
                edges[i]=edges[j];
                edges[j]=tmp;
            }
        }
    }
}

int get_end(int vends[],int i)
{
    while(vends[i]!=0)
        i=vends[i];
    return i;
}

void kruskal(Graph G)
{
    int i,m,n,p1,p2;
    int length;
    int index=0;
    int vends[MAX]={0};
    EData rets[MAX];
    EData *edges;
    edges=get_edges(G);
    sort_edges(edges,G.edgnum);

    for(i=0;i<G.edgnum;i++)
        printf("%d ",edges[i].weight);
    printf("\n");
    for(i=0;i<G.edgnum;i++)
    {
        p1=get_position(G,edges[i].start);
        p2=get_position(G,edges[i].end);
        m=get_end(vends,p1);
        n=get_end(vends,p2);
        printf("m= %d,n= %d",m,n);
        if(m!=n)
        {
            vends[m]=n;
            rets[index++]=edges[i];
        }
    }
    free(edges);

    length=0;
    for(i=0;i<index;i++)
        length+=rets[i].weight;
    printf("Kruskal = %d\n",length);
    for(i=0;i<index;i++)
        printf("( %c , %c ) ",rets[i].start,rets[i].end);
    printf("\n");
}

int main()
{
    Graph *pG;
    pG=create_graph();
    print_graph(*pG);
    kruskal(*pG);
}

運行結果:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM