最小生成树——Prim算法

最小生成树在实际中具有重要用途:
1.设计通信网
2.设计图的顶点表示城市,边表示两个城市之间通信线路,边的权值表示建造通讯线路的费用
3.n个城市之间最多可以建n(n-1)/2条线路,如何选择其中n-1条,使总造价最低

Prim算法的步骤:
⑴若从顶点v0出发构造,U={v0},TE={};
⑵先找权值最小的边(u,v),其中u∈U且v∈V-U,并且子图不构成环,则U= U∪{v},TE=TE∪{(u,v)} ;
⑶重复⑵,直到U=V为止。则TE中必有n-1条边,T=(U,TE)就是最小生成树。

我们用邻接矩阵来存储图,求最小生成树。
最后求出的最小生成树用边表存储
首先把要用的结构体都定义好

#include<stdio.h>
#include<stdlib.h>
#define MAX_VEX 10
#define INFINITY 100000
typedef int InfoType;//结点的信息,如权值、造价等
typedef char VexType;//顶点的类型
typedef int WeightType;//权值的类型

typedef enum
{
    DG = 1, AG, WDG, WAG//有向图  无向图  加权有向图  加权无向图
}GraphKind;

//线性表的结构体
typedef struct VexLine
{
    VexType data;//顶点的值
    int indegree;//顶点的度
}VexLine;

//图的邻接矩阵的结构体
typedef struct MGraph
{
    GraphKind kind;
    VexLine vex[MAX_VEX];//线性表
    int adj[MAX_VEX][MAX_VEX];//矩阵
    int vexnum;//顶点个数
}MGraph;

typedef struct MSTEdge
{
    VexType v1, v2;//边所依附的两个顶点
    WeightType weight;//边的权值
}MSTEdge;

struct
{
    int adjvex;//边所依附于U中的顶点的下标
    int lowcost;//该边的权值
}closedge[MAX_VEX];

创建一个空图

MGraph CreateGraph()
{
    int i;
    int j;
    MGraph G;
    G.vexnum = 0;
    G.kind = WAG;
    for (i = 0; i < MAX_VEX; i++)
    {
        G.vex[i].indegree = 0;
    }
    for (i = 0; i < MAX_VEX; i++)
    {
        for (j = 0; j < MAX_VEX; j++)
        {
            G.adj[i][j] = INFINITY;
        }
    }
    printf("haha success!\n");
    return G;
}

定位结点是否存在
若存在返回1,若不存在返回-1

int LocateVex(MGraph *G, VexType u)
{
    int i;
    int a = -1;

    for (i = 0; i < G->vexnum; i++)
    {
        if (G->vex[i].data == u)
        {
            a = 1;
            break;
        }
    }

    return a;
}

在图中插入一个结点

void InsertVex(MGraph *G)
{
    int res = 0;
    if (G->vexnum + 1 == MAX_VEX)
    {
        printf("The graph is overflow!\n");
    }
    else
    {
        VexType u = '\0';
        printf("please enter data :\n");
        getchar();
        scanf("%c", &u);
        res = LocateVex(G, u);
        if (res != 1)
        {
            G->vex[G->vexnum].data = u;
            G->vex[G->vexnum].indegree = 0;
            G->vexnum++;
        }
        else
        {
            printf("insert vertex fail.\n");
        }
    }
}

在图中插入一条边

void InsertArc(MGraph *G)
{
    int tail;
    int head;
    int weight;
    printf("please enter tail:\n");
    scanf("%d", &tail);
    printf("please enter head:\n");
    scanf("%d", &head);
    printf("please enter weight\n");
    scanf("%d", &weight);
    if (head < G->vexnum && tail < G->vexnum)
    {
        G->adj[tail][head] = weight;
        G->adj[head][tail] = weight;
        printf("success\n");
    }
    else
    {
        printf("fail\n");
    }
}

重点来了,接下来是最小生成树的Prim算法

MSTEdge* PrimMiniSpanTree(MGraph G, int u) {
    //TE是存放最小生成树的n-1条边的数组指针
    MSTEdge *TE;
    TE = (MSTEdge *)malloc((G.vexnum - 1)*sizeof(MSTEdge));
    int i;
    int j;
    int v;
    int k;//权值最小的下标
    int min;//最小的权值

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

    //循环n次    每次找权值最小的边
    for (j = 0; j < G.vexnum; j++)
    {
        min = INFINITY;
        for (v = 0; v < G.vexnum; v++)
        {
            if ((closedge[v].lowcost != 0) && (closedge[v].lowcost < min))
            {
                min = closedge[v].lowcost;
                k = v;
            }
        }

        //在边表里加入权值最小的边
        TE[j].v1 = closedge[k].adjvex;
        TE[j].v2 = k;
        TE[j].weight = closedge[k].lowcost;

        //把权值最小的(刚添加到边表里的边)边的权值赋为0
        closedge[k].lowcost = 0;

        //修改closedge数组中的值
        for (v = 0; v < G.vexnum; v++)
        {
            if ((closedge[v].lowcost != 0) && (G.adj[v][k] < closedge[v].lowcost))
            {
                closedge[v].lowcost = G.adj[v][k];
                closedge[v].adjvex = k;
            }
        }
    }

    return TE;
}

再来一个输出邻接矩阵的方法

void OutPut(MGraph G)
{
    int i;
    int j;
    for (i = 0; i < G.vexnum; i++)
    {
        printf("%c\t", G.vex[i].data);
    }
    printf("\n");
    printf("------------------------------------\n");
    for (i = 0; i < G.vexnum; i++)
    {
        for (j = 0; j < G.vexnum; j++)
        {
            printf("%d\t", G.adj[i][j]);
        }
        printf("\n");
    }
}

main方法(这里的面方法只是测试用的,各位可根据自己的需要编写)

int main()
{
    int j;
    MGraph G;
    MSTEdge *TE;

    printf("1.create a graph\n2.insert a vertex\n3.insert an arc\n4.Output adjacency matrix\n5.minispantree\n");
    while (1)
    {
        int a = 0;
        printf("please enter the operation code:");
        scanf("%d", &a);

        switch (a)
        {
        case 1:
            G = CreateGraph();
            break;
        case 2:
            InsertVex(&G);
            break;
        case 3:
            InsertArc(&G);
            break;
        case 4:
            OutPut(G);
            break;
        case 5:
            TE = PrimMiniSpanTree(G, 1);
            printf("\tTE->v1\tTE->v2\tTE->weight\t\n");
            for (j = 0; j < G.vexnum - 1; j++)
            {
                printf("%d\t", j);
                printf("%d\t", TE[j].v1);
                printf("%d\t", TE[j].v2);
                printf("%d\t", TE[j].weight);
                printf("\n");
            }
            break;
        default:
            printf("Opcode error!\n");
            break;
        }
    }

    return 0;
}

例:
这里写图片描述
运行结果:
插入节点:
这里写图片描述
插入边:
这里写图片描述
邻接矩阵:自定义100000为无穷(两个节点之间没有边)
这里写图片描述
最小生成树的边表:
这里写图片描述

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值