数据结构--图--最小生成树

时间:2021-07-17 12:26:46

数据结构–图 –最小生成树

1.基本概念

生成树:一个连通图的生成树是一个极小的连通子图,它含有图中全部的顶点,但只有足以构成一棵树的n-1条边。

最小生成树:我们把构造连通网的最小代价生成树称为最小生成树。(一棵生成树的代价就是树上各边的代价之和)

2.经典的两种算法

2.1 Prim算法思路

    假设全部顶点的集合是V,已经被挑选出来的是U,那么从集合V-U中不断挑选权值最低的点。(这里的权值最低,是相对于U中所有的点来说,是把U看成一个整体。Prim算法和后面的求最短路径的Dijkstra算法有些相似,具体区别看2.3(Prim算法和Dijkstra算法比较))。
    以某起点为顶点,逐步找各顶点上最小权值的边来构建最小生成树。(这就像是我们如果去参观某个展会,例如世博会,你从一个入口进去,然后找你所在位置周边的场馆中你最感兴趣的场馆观看,看完后再用同样的办法看下一个)

2. 2 Kruskal算法思路

    从宏观着眼,将所有的边排成序列,依次从小到大找边来构建最小生成树。若加入某条边构成回路则舍弃这条边。(这就像是我们参观世博会,事先计划好,进园后直接去到最想看的场馆观看)

2.3

Prim算法与Kruskal算法比较:
Prim算法主要针对顶点展开的,对于稠密图,即边数非常多的情况会更好一些;
Kruskal算法主要针对边来展开,边数少时效率会非常高,所以对于稀疏图有很大的优势。
Prim算法与Dijkstra算法比较[转]1
    在图论中,Prim算法是计算最小生成树的算法,而Dijkstra算法是计算最短路径的算法。二者看起来比较类似,因为假设全部顶点的集合是V,已经被挑选出来的点的集合是U,那么二者都是从集合V-U中不断的挑选权值最低的点加入U,那么二者是否等价呢?也就是说是否Dijkstra也可以计算出最小生成树而Prim也可以计算出从第一个顶点v0到其他点的最短路径呢?答案是否定的,否则就不必有两个算法了。
    二者的不同之处在于“权值最低”的定义不同,Prim的“权值最低”是相对于U中的任意一点而言的,也就是把U中的点看成一个整体,每次寻找V-U中跟U的距离最小(也就是跟U中任意一点的距离最小)的一点加入U;而Dijkstra的“权值最低”是相对于v0而言的,也就是每次寻找V-U中跟v0的距离最小的一点加入U。
    一个可以说明二者不等价的例子是有四个顶点(v0, v1, v2, v3)和四条边且边值定义为(v0, v1)=20, (v0, v2)=10, (v1, v3)=2, (v3, v2)=15的图,用Prim算法得到的最小生成树中v0跟v1是不直接相连的,也就是在最小生成树中v0v1的距离是v0->v2->v3->v1的距离是27,而用Dijkstra算法得到的v0v1的距离是20,也就是二者直接连线的长度。

3.算法实现

  • Prim算法

数据结构--图--最小生成树

  • Kruskal算法

数据结构--图--最小生成树

4.代码实现


#include<stdio.h>
#include<stdlib.h>
#define OK 1
#define ERROR 0
#define TURE 1
#define FALSE 0
#define OVERFLOW -2
#define MAX_VERTEX_SIZE 20
#define INFINITY 65535

typedef int Status;
typedef int VertexType;                 //顶点类型
typedef int VRType;                     //顶点关系类型
typedef enum{DG,DN,UDG,UDN} GraphKind;

typedef struct VNode
{
    VertexType data;
}VNode,AdjList[MAX_VERTEX_SIZE];

typedef struct ArcCell
{
    VRType adj;
}ArcCell,AdjMatrix[MAX_VERTEX_SIZE][MAX_VERTEX_SIZE];

typedef struct
{
    int vexnum,arcnum;
    AdjMatrix arcs;
    AdjList vertices;
    GraphKind kind;
}MGraph;

typedef struct Edge           // MiniSpanTree_Kruskal算法专用
{
    int begin;
    int end;
    int weight;
}Edge;

Status CreateGraph(MGraph *G){

    VNode v1,v2;
    int i,j,sign1,sign2;
    int value;

    (*G).kind = UDN;

    printf("输入顶点数、弧的数\n");
    scanf("%d%d",&(*G).vexnum, &(*G).arcnum);

    for(i = 0; i < (*G).vexnum; ++i){
        for(j = 0; j < (*G).vexnum; ++j){
            if(i == j)
                (*G).arcs[i][j].adj = 0;
            else
                (*G).arcs[i][j].adj = INFINITY;
        }
    }
    printf("初始化顶点\n");
    for(i = 0; i < (*G).vexnum; ++i){
        scanf("%d",&(*G).vertices[i].data);
    }
    printf("初始化弧\n");
    for(i = 0; i < (*G).arcnum; ++i){
        sign1 = -1;
        sign2 = -1;
        scanf("%d%d%d",&v1.data,&v2.data,&value);
        for(j = 0; j < (*G).vexnum; ++j){
            if(v1.data == (*G).vertices[j].data)
                sign1 = j;
            if(v2.data == (*G).vertices[j].data)
                sign2 = j;
        }
        (*G).arcs[sign1][sign2].adj = value;
        (*G).arcs[sign2][sign1].adj = value;
    }
    return OK;
}

void test(MGraph G){                    //测试函数,打印创建好的邻接矩阵
    int i,j;
    for(i = 0; i < G.vexnum; ++i){
        for(j = 0; j < G.vexnum; ++j)
            printf("%.6d ",G.arcs[i][j]);
        printf("\n");
    }
}

Status MiniSpanTree_Prim(MGraph G){

    int i,j,k,min;
    int adjvex[G.vexnum];
    int lowcost[G.vexnum];

    for(i = 0; i < G.vexnum; ++i){                      //初始化
        lowcost[i] = G.arcs[0][i].adj;
        adjvex[i] = 0;
    }

    lowcost[0] = 0;
    for(i = 1; i < G.vexnum; ++i){                      //找出lowcost[]中的最小值
        min = INFINITY;
        for(j = 1; j < G.vexnum; ++j){
            if(lowcost[j] != 0 && lowcost[j] < min){
                min = lowcost[j];
                k = j;
            }
        }
        lowcost[k] = 0;
        printf("(%d, %d)\n",adjvex[k],k);

        for(j = 1; j < G.vexnum; ++j){                   //更新lowcost[]和adjvex[]
            if(lowcost[j] != 0 && G.arcs[k][j].adj < lowcost[j]){
                lowcost[j] = G.arcs[k][j].adj;
                adjvex[j] = k;
            }
        }

    }
    return OK;
}

Edge* CreateEdges(MGraph G){    //MiniSpanTree_Kruskal算法子函数:对边按权值从小到大的顺序排序


    int i,j;
    int k = 0;
    Edge temp;
    Edge *p;

    p = (Edge *)malloc(G.arcnum * sizeof(Edge));
    if(!p)
        exit(OVERFLOW);

    for(i = 0; i < G.vexnum; ++i){                //初始化
        for(j = i; j < G.vexnum; ++j){
            if(G.arcs[i][j].adj != 0 && G.arcs[i][j].adj != INFINITY){
                p[k].begin = i;
                p[k].end = j;
                p[k].weight = G.arcs[i][j].adj;
                ++k;
            }
        }
    }

    for(i = 0; i < G.arcnum-1; ++i){             //按权值从小到大排序
        for(j = i + 1; j < G.arcnum; ++j){
            if(p[i].weight > p[j].weight){
                temp = p[i];
                p[i] = p[j];
                p[j] = temp;
            }
        }
    }
    return p;
}


Status MiniSpanTree_Kruskal(MGraph G){

    Edge *edges;
    int parent[G.vexnum];
    int i,m,n;

    edges = CreateEdges(G);

    for(i = 0; i < G.vexnum; ++i)           //初始化
        parent[i] = 0;

    for(i = 0; i < G.arcnum; ++i){          //循环每一条边
        n = Find(parent,edges[i].begin);
        m = Find(parent, edges[i].end);
        if(n != m){                         //若n与m不等,则说明此边没有与现有生成树形成回路
            parent[n] = m;
            printf("(%d,%d) %d\n",edges[i].begin, edges[i].end, edges[i].weight);
        }

    }
    return OK;
}

int Find(int *parent,int f){            //MiniSpanTree_Kruskal算法子函数 查找连线顶点的尾部下标
    while(parent[f] > 0)
        f = parent[f];

    return f;
}

int main(){

    MGraph G;
    Edge *edges;
    int i;

    CreateGraph(&G);
    printf("创建的邻接矩阵如下:\n");
    test(G);

    printf("MiniSpanTree_Prim:\n");
    MiniSpanTree_Prim(G);

    //MiniSpanTree_Kruskal算法
    printf("将边按权值从小到大排序如下\n");
    edges = CreateEdges(G); 
    for(i = 0; i < G.arcnum; ++i){
        printf("%d %d %d \n",edges[i].begin, edges[i].end, edges[i].weight);
    }
    printf("MiniSpanTree_Kruskal\n");
    MiniSpanTree_Kruskal(G);

    return 0;
}

参考资料:

1.严蔚敏 数据结构 清华大学出版社
2.程杰 大话数据结构 清华大学出版社
3.http://www.cnblogs.com/huiliu/archive/2011/04/15/2017228.html