Kruskal---求最小生成树(贪心算法)

算法描述:

一个图的生成树是一个树并把图的所有顶点连接在一起。一个图可以有许多不同的生成树。最小生成树其实是最小权重生成树的简称。

最小生成树有(V – 1)条边,其中V是给定的图的顶点数量。

Kruskal算法是一种贪心算法。贪心的选择是选择最小的权重的边,并不会和当前的生成树形成环。

算法步骤:

1,按照所有边的权重排序(从小到大)

2,选择最小的边。检查它是否形成与当前生成树形成环。如果没有形成环,将这条边
加入生成树。否则,丢弃它。  

3,重复第2步,直到有生成树(V-1)条边

求下图的最小生成树(MST:minimum spanning tree)

Kruskal---求最小生成树(贪心算法)_第1张图片

该图有9个节点,14条边,MST有8条边。
将每条边按权重排序如下(从小到大):

Weight   Src    Dest
1         7      6
2         8      2
2         6      5
4         0      1
4         2      5
6         8      6
7         2      3
7         7      8
8         0      7
8         1      2
9         3      4
10        5      4
11        1      7
14        3      5

依次选择边:



已经选择V-1条边,算法结束。

时间复杂度:

O(ElogE) 或 O(ElogV)。 排序使用 O(ELogE) 的时间,之后我们遍历中使用并查集O(LogV) ,所以总共复杂度是 O(ELogE + ELogV)。E的值最多为V^2,所以
O(LogV) 和 O(LogE) 可以看做是一样的。

算法实现:

//Kruskal算法求无向带权图的最小生成树
#include <iostream>
#include<stdlib.h>
using namespace std;
//带权边的结构体
struct Edge
{
    int start;
    int end;
    int weight;
};
//无向图
struct Graph
{
    // V-> 顶点个数 E-> 边的个数
    int V,E;
    //图由边组成的数组来表示
    //由于是无向图,从start到end的边,同时也是end到start的边,按一条边计算
    struct Edge* edge;
};
//构建一个V个顶点 E条边的图
struct Graph* createGraph(int V, int E)
{
    struct Graph* graph = (struct Graph*) malloc(sizeof(struct Graph));
    graph->V=V;
    graph->E=E;
    graph->edge=(struct Edge*) malloc(graph->E*sizeof(struct Edge));
    return graph;
};
//并查集的结构体
struct subset
{
    int parent;
    int rank;
};
//使用路径压缩查找元素i所在集合
int find(struct subset subsets[], int i)
{
    if(subsets[i].parent != i)
    {
        subsets[i].parent = find(subsets, subsets[i].parent);
    }
    return subsets[i].parent;
}
// 按秩合并 x,y
void Union(struct subset subsets[], int x, int y)
{
    int xroot = find(subsets,x);
    int yroot = find(subsets,y);
    //将秩小的树合并到秩大的树下边
    if(subsets[xroot].rank < subsets[yroot].rank)
    {
        subsets[xroot].parent = yroot;
    }
    else if(subsets[xroot].rank > subsets[yroot].rank)
    {
        subsets[yroot].parent = xroot;
    }
    // If ranks are same, then make one as root and increment
    // its rank by one
    else
    {
        subsets[yroot].parent=xroot;
        subsets[xroot].rank++;
    }
}
// 很据权重比较两条边
int cmp(const void* a, const void* b)
{
    struct Edge* a1=(struct Edge*)a;
    struct Edge* b1=(struct Edge*)b;
    return a1->weight > b1->weight;
}
// Kruskal 算法
void KruskalMST(struct Graph* graph)
{
    int V = graph->V;
    struct Edge result[V];//存储得到的最小生成树
    int e=0;//result[] 的index
    int i=0;// 已排序的边的 index
    //step 1: 从小到大排序
    qsort(graph->edge,graph->E,sizeof(graph->edge[0]),cmp);
    //为并查集分配内存
    struct subset* subsets=(struct subset*) malloc(V * sizeof(struct subset));
    //初始化并查集
    //用单个元素构造V个集合
    for(int v=0; v<V;++v)
    {
        subsets[v].parent=v;
        subsets[v].rank=0;
    }
    // 边的数量不到V-1时就一直添加,到V-1结束
    while(e < V-1)
    {
        // Step 2: 先选最小权重的边,并且i++
        struct Edge next_edge=graph->edge[i++];
        int x=find(subsets,next_edge.start);
        int y=find(subsets,next_edge.end);
        // 如果此边不会引起环(通过并查集判断)
        //则加入到result中,并且e++
        if(x != y)
        {
            result[e++]=next_edge;
            Union(subsets,x,y);
        }
        // 否则丢弃,继续
    }
    // 打印result[]
    cout<<"Following are the edges in the constructed MST"<<endl;
    for (i = 0; i < e; ++i)
    {
        cout<<result[i].start<<"--"<<result[i].end<<"=="<<result[i].weight<<endl;
    }
}

int main()
{
    /* 创建下面的图: 10 0--------1 | \ | 6| 5\ |15 | \ | 2--------3 4 */
    int V = 4;  // 顶点个数
    int E = 5;  //边的个数
    struct Graph* graph = createGraph(V, E);
    // 添加边 0-1
    graph->edge[0].start = 0;
    graph->edge[0].end = 1;
    graph->edge[0].weight = 10;
    // 添加边 0-2
    graph->edge[1].start = 0;
    graph->edge[1].end = 2;
    graph->edge[1].weight = 6;
    // 添加边 0-3
    graph->edge[2].start = 0;
    graph->edge[2].end = 3;
    graph->edge[2].weight = 5;
    // 添加边 1-3
    graph->edge[3].start = 1;
    graph->edge[3].end = 3;
    graph->edge[3].weight = 15;
    // 添加边 2-3
    graph->edge[4].start = 2;
    graph->edge[4].end = 3;
    graph->edge[4].weight = 4;

    KruskalMST(graph);

    return 0;
}

执行结果:

Following are the edges in the constructed MST
2--3==4
0--3==5
0--1==10

参考:http://www.geeksforgeeks.org/greedy-algorithms-set-2-kruskals-minimum-spanning-tree-mst/

你可能感兴趣的:(最小生成树,kruskal)