图的最小生成树算法

来源:互联网 发布:c语言中a=3是什么意思 编辑:程序博客网 时间:2024/06/05 03:52

目录

  1. 图的最小生成树算法之Prim算法简介
  2. Prim算法举例及其详解
  3. Prim代码块
  4. 图的最小生成树算法之Kruskal算法简介
  5. Kruskal算法举例及其详解
  6. Kruskal代码块

图的最小生成树算法之Prim算法简介

1、生成树
 如果连通图G的一个子图是一棵包含G的所有顶点的树,则该子图称为G的生成树(SpanningTree)。
 生成树是连通图的包含图中的所有顶点的极小连通子图。
 图的生成树不惟一。从不同的顶点出发进行遍历,可以得到不同的生成树。
2.算法简单描述

1).输入:一个加权连通图,其中顶点集合为V,边集合为E;

2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;

3).重复下列操作,直到Vnew = V:

a.在集合E中选取权值最小的边(u, v),其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);

b.将v加入集合Vnew中,将(u, v)边加入集合Enew中;

4).输出:使用集合Vnew和Enew来描述所得到的最小生成树。
这里写图片描述


Prim代码块

#include<stdio.h>#include<stdlib.h>#define MAXV 100#define LIMITLESS 9999typedef struct{    int no;  //顶点编号     int info; //顶点其他信息 } VertexType;typedef struct{    int n;    int e;//定点数,边数     int edges[MAXV][MAXV];//邻接矩阵的数组表现    VertexType vexs[MAXV]; //顶点信息 }MGraph;void creat(MGraph *G){    int i, j, k, w;    int start, end;    printf("请输入顶点数和边数:\n");     scanf("%d%d", &(G->n), &(G->e));    getchar();    printf("请输入顶点信息:\n");    for (i = 0; i<G->n; i++)    {        scanf("%d%d", &(G->vexs[i].no), &(G->vexs[i].info));    }    for (i = 0; i<G->n; i++)    {        for (j = 0; j<G->n; j++)        {            if (i == j)            {                G->edges[i][j] = 0;            }            else            {                G->edges[i][j] = LIMITLESS;            }        }    }    printf("请输入 图的顶点 边和它的权值:\n");    for (k = 0; k<G->e; k++)    {        scanf("%d%d%d", &start, &end, &w);        G->edges[start][end] = w;        G->edges[end][start] = w;//无向图时用到    }}void print(MGraph *G){    int i, j;    printf("顶点数:%d, 边数:%d\n", G->n, G->e);    printf("%d个顶点的信息(顶点下标必须从0开始):\n", G->n);    for (i = 0; i<G->n; i++)    {        printf("%5d%5d", G->vexs[i].no, G->vexs[i].info);    }    printf("\n各个顶点的连接情况:\n");    printf("\t");    for (i = 0; i<G->n; i++)    {        printf("[%d]\t", i);    }    printf("\n");    for (i = 0; i<G->n; i++)    {        printf("[%d]\t", i);        for (j = 0; j<G->n; j++)        {            if (G->edges[i][j] == LIMITLESS)            {                printf("oo\t");            }            else            {                printf("%d\t", G->edges[i][j]);            }        }        printf("\n");    }}void Prim(MGraph *g, int v){    int i, j, k;    int lowcost[MAXV];    int min;    int closest[MAXV];    for (i = 0; i < g->n; i++)    {        lowcost[i] = g->edges[v][i];        closest[i] = v;    }    for (i = 1; i < g->n; i++)//因为前面已经有了一次初始化,所以剩下的次数为n-1    {        min = LIMITLESS;        //找到权值最小的邻接点        for (j = 0; j < g->n; j++)        {            if (lowcost[j] != 0 && lowcost[j] < min)            {                min = lowcost[j];                k = j;            }        }        printf("边(%d,%d):%d\n", closest[k], k, min);        lowcost[k] = 0;        //调整lowcost[]和closest[];        for (j = 0; j < g->n; j++)        {            if (g->edges[k][j] != 0 && g->edges[k][j] < lowcost[j])            {                lowcost[j] = g->edges[k][j];                closest[j] = k;            }        }    }}int main(void){    MGraph *g;    g = malloc(sizeof(MGraph));    creat(g);    print(g);    Prim(g,0);    system("pause");    return 0;}

测试结果:
这里写图片描述
这里写图片描述


参考图纸:
这里写图片描述
这里写图片描述


图的最小生成树算法之Kruskal算法简介

假设连通网N=(V,{E})。则令最小生成树的初始状态为只有n个顶点而无边的非连通图T=(V,{}),图中每个顶点自成一个连通分量。

在E中选择最小代价的边,若该边依附的顶点落在T中不同的连通分量中,则将该边加入到T中,否则舍去此边而选择下一条代价最小的边,依次类推,直到T中所有顶点都在同一连通分量上为止。
这里写图片描述

Kruskal代码块

卡鲁斯卡尔的最小生成树算法要求求得的最小生成树不能出现回路

#include<stdio.h>#include<stdlib.h>#define MAXV 100#define LIMITLESS 9999typedef struct{    int no;  //顶点编号     int info; //顶点其他信息 } VertexType;typedef struct{    int n;    int e;//定点数,边数     int edges[MAXV][MAXV];//邻接矩阵的数组表现    VertexType vexs[MAXV]; //顶点信息 }MGraph;typedef struct{    int u;//边的起始顶点    int v;//边的终止顶点    int w;//边的权值}Edge;void creat(MGraph *G){    int i, j, k, w;    int start, end;    printf("请输入顶点数和边数:\n");    scanf("%d%d", &(G->n), &(G->e));    getchar();    printf("请输入顶点信息:\n");    for (i = 0; i<G->n; i++)    {        scanf("%d%d", &(G->vexs[i].no), &(G->vexs[i].info));    }    for (i = 0; i<G->n; i++)    {        for (j = 0; j<G->n; j++)        {            if (i == j)            {                G->edges[i][j] = 0;            }            else            {                G->edges[i][j] = LIMITLESS;            }        }    }    printf("请输入 图的顶点 边和它的权值:\n");    for (k = 0; k<G->e; k++)    {        scanf("%d%d%d", &start, &end, &w);        G->edges[start][end] = w;        G->edges[end][start] = w;    }}void print(MGraph *G){    int i, j;    printf("顶点数:%d, 边数:%d\n", G->n, G->e);    printf("%d个顶点的信息:\n", G->n);    for (i = 0; i<G->n; i++)    {        printf("%5d%5d", G->vexs[i].no, G->vexs[i].info);    }    printf("\n各个顶点的连接情况:\n");    printf("\t");    for (i = 0; i<G->n; i++)    {        printf("[%d]\t", i);    }    printf("\n");    for (i = 0; i<G->n; i++)    {        printf("[%d]\t", i);        for (j = 0; j<G->n; j++)        {            if (G->edges[i][j] == LIMITLESS)            {                printf("oo\t");            }            else            {                printf("%d\t", G->edges[i][j]);            }        }        printf("\n");    }}void InsertSort(Edge E[], int n){    int i, j;    Edge temp;    for (i = 1; i < n; i++)    {        temp = E[i];        j = i - 1;        while (j >= 0 && temp.w < E[j].w)        {            E[j + 1] = E[j];            j--;        }        E[j + 1] = temp;    }    for (i = 0; i < n; i++)    {        printf("%d %d %d\n", E[i].u, E[i].v, E[i].w);    }}void Kruskal(MGraph *g){    int i, j, u1, v1, sn1, sn2, k;    int vset[MAXV];    Edge E[MAXV];    //存放所有边    k = 0;                //E数组的下标从0开始计    for (i = 0; i<g->n; i++)   //由g产生的边集E        for (j = 0; j<g->n; j++)            if (g->edges[i][j] != 0 && g->edges[i][j] != LIMITLESS)            {                E[k].u = i;                E[k].v = j;                E[k].w = g->edges[i][j];                k++;            }    InsertSort(E, k);      //采用直接插入排序对E数组按权值递增排序    for (i = 0; i<g->n; i++)   //初始化辅助数组        vset[i] = i;    k = 1;    //k表示当前构造生成树的第几条边,初值为1    j = 0;    //E中边的下标,初值为0    while (k<g->n)       //生成的边数小于n时循环    {        u1 = E[j].u;        v1 = E[j].v;      //取一条边的头尾顶点        sn1 = vset[u1];        sn2 = vset[v1];   //分别得到两个顶点所属的集合编号        if (sn1 != sn2)   //两顶点属于不同的集合        {            printf("  (%d,%d):%d\n", u1, v1, E[j].w);            k++;                     //生成边数增1            for (i = 0; i<g->n; i++)   //两个集合统一编号                if (vset[i] == sn2)   //集合编号为sn2的改为sn1                    vset[i] = sn1;        }        j++;               //扫描下一条边    }}int main(void){    MGraph *g;    g = malloc(sizeof(MGraph));    creat(g);    print(g);    Kruskal(g);    system("pause");    return 0;}

测试结果:
这里写图片描述
这里写图片描述
参考图纸:
这里写图片描述

0 0