最小生成树 prim算法和kruskal算法

来源:互联网 发布:多进程编程 编辑:程序博客网 时间:2024/06/03 15:49

最小生成树

假设在n个城市中要建立通信网络,那么连接n个点需要n-1条边。任意两个城市间建立通信网络都是需要费用的,这个可以认为是边的权值,用来表示响应的代价。对于n个顶点的连通图可以构建不同的生成树,而这个代价最小的生成树称为最小生成树。

最小生成树的MST特性,假设N = (V,{E})是一个连通网,U是顶点集V的一个非空子集。若(u,v)是一条具有最小权值的边,其中u属于U,v属于V-U,则必存在一颗包含(u,v)的最小生成树,证明略。根据MST,求最小生成树有两种方法,普利姆(prim)和克鲁斯卡尔(kruskal)。

Prim算法

假设N = (V,{E}),TE是最小生成树中边的集合。算法从U = {u0}, u属于V,TE = {}开始,重复执行以下操作:在所有u属于U,v属于V - U的边(u,v)属于E中找一条权值最小的边(u0,v0)并入集合TE,同时v0并入U,直到 U = V为止。此时TE中必有n-1条边,则T = (V,{TE})为N的最小生成树。

如图,计算过程如下:

closedge/i12345UV - Uk

adjvex

lowcost

v1

6

v1

1

v1

5

  {v1}

{v2, v3,v4,

v5, v6}

2

adjvex

lowcost

v3

5


0

v1

5

v3

6

v3

4

{v1, v3}

{v2, v4,

v5, v6}

5

adjvex

lowcost

v3

5


0

v6

2

v3

6


0

{v1, v3

v6}

{v2, v4

v5}

3

adjvex

lowcost

v3

5


0


0

v3

6


0

{v1, v3,

v6, v4}

{v2, v5}1

adjvex

lowcost


0


0


0

v2

3


0

{v1, v3, v6

v4, v2}

{v5}4

adjvex

lowcost


0


0


0


0


0

{v1,v3, v6

v4, v2, v5}

{ } 
//由于每个顶点都需要与其他的顶点比较找寻lowcost,所以本算法采用邻接矩阵存储,//并利用辅助数组closedge来存放每个顶点的最小costvoid Prim(MGraph *G, int v){    k = v;    struct Closedge closedge[G -> vexnum];    for(int i = 0; i < G -> vexnum; i++){        if(i != k)            closedge[i]= G -> arcs[k][i];    }    closedge[k].lowcost = 0;    for(int i = 1; i < G -> vexnum; i++){        k = min_closedge(closedge, G -> vexnum);            //寻找不为0的最小权值的顶点        cout << k << " " << closedge[k].adjvex;        for(int j = 0; j < G -> vexnum; j++){            if(G -> arcs[k][j] < closedge[j])                closedge[j] = G -> arcs[k][j];        }    }}int min_closedge(struct Closedge closedge[], int n){    int min = INT_MAX, ans = 0;    for(int i = 0; i < n; i++)        if(closedge[i]!= 0 && min > closedge[i]){            min = closedge[ans = i];        }    return ans;}
Prim算法时间复杂度为O(n^2),与顶点有关,与边无关,适用于稠密图。

Kruskal算法

假设连通网 N = (V,{ E }),令最小生成树的初始状态为只有n个顶点而无边的非连通图 T = (V,{ }),图中的每个顶点自称一个连通分量。在E中选取边中权值最小的,如果将此边加入T不产生回路,那么就将其加入T,否则舍去此边,寻找下一个最小权值的边,直到T中所有顶点都在同一连通分量上。由此可以看出Kruskal是基于边来做的,又每次都是挑选权值最小的边,所以在这里可以用一个指针数组存储边信息,并对其按权值由小到大排序,这样按数组由小到大遍历一次即可。为了判断新加入的边是否会构成回路,使用一个辅助数组 parent 来判断,parent[ i ] = k,表示顶点 i 和顶点 k 在边集合A中,而当k = 0时,表示集合A暂时到头。

#define MAX_NODE 20struct Node{    int begin, end, weight;};struct Node edge[MAX_NODE];//此处的读入输入数据,并对edge排序省略void kruskal(){    int i = 0, n = 0, m = 0;    int parent[MAX_NODE];    for(; i < MAX_NODE; i++)        parent[i] = i;    for(i = 0; i < MAX_NODE; i++){        n = find(parent, edge[i].begin);        m = find(parent, edge[i].end);        if(n != m){            parent[n] = m;            cout << edge[i].begin << " " << edge[i].end << " " << edge.weight << endl;        }    }}int find(int parent[], int v){    while(parent[v] != v)        v = parent[v];    return v;}
由于find时间复杂度O(loge),一共循环e次,所以kruskal时间复杂度为O(eloge),只和边个数有关,适合稀疏图。


0 0