图的应用-最小生成树

2022-07-10  本文已影响0人  皓似

概念

最小生成树

连通图的生成树是一个极小的连通子图,含有图中全部的n个顶点,但最多只有n-1条边。通常把构成连通图的最小代价的连通路径,称为最小生成树。

最小生成树的两个算法

Prim算法(普利姆)

图1.算法中的邻接矩阵

思路

1.定义两个数组:adjvex保存相关顶点下标,默认值为0;lowcost保存顶点之间的权值,默认值∞,其数组大小都为顶点数目
2.从邻接矩阵V0开始寻找连接顶点,默认它为最小生成树的第一个顶点;
3.遍历lowcost,根据权值最小,找到连接点k,然后更新k的权值为0,标识已访问过
4.遍历所有顶点,找到跟k相连且未访问过的顶点,记入adjvex,同时比较权值,较小值存入lowcost

//邻接矩阵结构
typedef struct {
    int arc[MAXVUE][MAXVUE];
    int numVertexes, numEdges;//顶点数和边数
} MGraph;

void MiniSpanTree_Prim(MGraph G) {
    int adjvex[MAXVUE];
    int lowcost[MAXVUE];
    
    lowcost[0] = 0;
    adjvex[0] = 0;
    
    int i, min, j, k;
    int sum = 0;
    
    for (i=1; i<G.numVertexes; i++) {
        lowcost[i] = G.arc[0][i];
        adjvex[i] = 0;
    }
    
    for (i=1; i<G.numVertexes; i++) {
        min = INFINITYC;
        j=1;
        k=0;
        
        //找到最小权值和下标
        while (j<G.numVertexes) {
            if (lowcost[j]!=0 && lowcost[j]<min) {
                min = lowcost[j];
                k = j;
            }
            j++;
        }
        
        printf("(V%d,V%d)=%d\n",adjvex[k],k,G.arc[adjvex[k]][k]);
        sum += G.arc[adjvex[k]][k];
        
        //已访问
        lowcost[k] = 0;
        
        //更新lowcost、adjvex
        //找到与k点相连的顶点
        for(j=1; j<G.numVertexes; j++) {
            if (lowcost[j]!=0 && G.arc[k][j] < lowcost[j]) {
                lowcost[j] = G.arc[k][j];
                adjvex[j] = k;
            }
        }
    }
    printf("sum=%d\n",sum);
}

Kruskal算法(克鲁斯卡尔)

思路

图2.Kruskal的边表数组

1.将邻接矩阵 转换为 边表数组

//边表数组的结构
typedef struct {
    int begin;
    int end;
    int weight;
}Edge;

2.对数组根据权值进行从小到大排序
3.初始化parent,默认为0,这里保存顶点之间的连接关系
4.遍历所有的边(begin-end),在parent中寻找begin和end的连接关系,并保存,同时保证它们不会形成闭环。通过parent寻找终点,终点一致则是闭环
5.(begin-end)没有闭环,则加入到最小生成树种,并更新parent数组

//值交换
void Swapn(Edge *edges,int i, int j) {
    int tempValue;
    
    tempValue = edges[i].begin;
    edges[i].begin = edges[j].begin;
    edges[j].begin = tempValue;
    
    tempValue = edges[i].end;
    edges[i].end = edges[j].end;
    edges[j].end = tempValue;
    
    tempValue = edges[i].weight;
    edges[i].weight = edges[j].weight;
    edges[j].weight = tempValue;
}
//排序
void Sort(Edge edges[],int numEdges) {
    int i, j;
    for (i=0; i<numEdges; i++) {
        for (j=i+1; j<numEdges; j++) {
            if (edges[i].weight>edges[j].weight) {
                Swapn(edges,i,j);
            }
        }
    }
    
    printf("边集数组根据权值排序之后的为:\n");
    for (i = 0; i < numEdges; i++)
    {
        printf("(%d, %d) %d\n", edges[i].begin, edges[i].end, edges[i].weight);
    }
}
//寻找顶点的终点,并返回
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 sum = 0;
    int k = 0;
    
    int parent[MAXVUE] = {0};
    Edge edges[MAXVUE];
    
    //构建边表数组
    for(i=0; i<G.numVertexes; i++) {
        for (j=i+1; j<G.numVertexes; j++) {
            if (G.arc[i][j]<INFINITYC) {
                edges[k].begin = i;
                edges[k].end = j;
                edges[k].weight = G.arc[i][j];
                
                k++;
            }
        }
    }
    
    //对边表数组排序
    Sort(edges, G.numEdges);
    
    printf("打印最小生成树:\n");
    
    //循环所有边
    for (i=0; i<G.numEdges; i++) {
        
        //获取begin,end 在parent 数组中的信息;
        //如果n = m ,将begin 和 end 连接,就会产生闭合的环.
        n = Find(parent,edges[i].begin);
        m = Find(parent,edges[i].end);
        
        if (n!=m) {
            /* 将此边的结尾顶点放入下标为起点的parent中。 */
            /* 表示此顶点已经在生成树集合中 */
            parent[n] = m;
            
            /*打印最小生成树路径*/
            printf("(%d, %d) %d\n", edges[i].begin, edges[i].end, edges[i].weight);
            sum += edges[i].weight;
        }
    }
    printf("sum = %d\n",sum);
}
上一篇 下一篇

猜你喜欢

热点阅读