prim算法 + 最小生成树的打印 C语言

300 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

问题

要在n个城市之间假设网络,要求只架设n-1条线路,并保证个城市之间网络链接,已知城市间架设网络的统计数据,其中列出了有可能架设的网络和对应的成本求最低价格方案,以图形方式给出架设方案,并输出求解结果。
在这里插入图片描述

解题

很明显,最小生成树,客户(作业代做)要求用prim和邻接矩阵,有难度的是树形图终端打印。

  1. 宏定义和相关数组
#include <stdio.h>
#include <stdlib.h>

#define MAX_INT 65535//定义一个无穷大的值
#define MIN(x, y) ((x)>(y))?(y):(x)//比较大小的宏定义

#define MAX_N 1001  //城市数量上限

int n=7,m=11;
int v[MAX_N][MAX_N]; //邻接矩阵
int sons[MAX_N][MAX_N];//记录子节点编号
int num_sons[MAX_N],num_endsons[MAX_N],deep[MAX_N];//记录每个节点的儿子数目,每个节点末端儿子数
  1. 数据输入

客户要图形化,有模有样地给弄一个,哈哈,进行简单的数据采集。

int main()
{
    //下标1是第一个点
    printf("请输入总城市数量:\n");
    scanf("%d",&n);
    printf("请输入总的边数量:\n");
    scanf("%d",&m);
    for(int i=1;i<=n+1;i++){
        for(int j=1;j<=n+1;j++){
            if(i==j)v[i][j]=0;
            else v[i][j]=MAX_INT;//初始化邻接矩阵,所有路径都设为无穷大
        }
    }
    for(int i=0;i<m;i++){//两个城市间成本赋值
        int a,b,x;
        printf("请某两个城市之间的成本:(输入三个数,用空格分隔)\n");
        scanf("%d %d %d",&a,&b,&x);
        v[a][b]=v[b][a]=x;
    }
  1. prim算法
    开始我们的算法,算法原理很简单,网上一堆。
    要注意的是,我们以后要打印最小生成树,而prim算法最直接的是获取父节点矩阵,所以要在经典算法中加入树的构建,需要知道每个节点的子节点,需要记录每个节点儿子的个数。
    //开始prim算法
    printf("路径如下:");
    int lowcost[MAX_N];
    int adjvex[MAX_N];
    adjvex[1]=0;
    lowcost[1]=0;
    for (int i = 2; i <= n; ++i) {
        lowcost[i]=v[1][i];
        adjvex[i]=1;
    }
    for(int i=0;i<MAX_N;i++){
        num_sons[i]=0;//初始化儿子数量数组
        num_endsons[i]=0;
        deep[i]=1;
    }
    int min,j,k;
    for (int i = 2;  i<=n; i++) {//从二号点开始
        min = MAX_INT;//将最小值先设为最大
        j=1;k=1;
        //选出权值最小的,存储在k中
        while (j<=n){
            if(lowcost[j]!=0&&lowcost[j]<min){//lowcost中的0表示该点已经在集合当中
                min = lowcost[j];
                k=j;
            }
            j++;
        }
        printf("%d->%d ",adjvex[k],k);//从一个点到另一个点,k的父亲是adjvex[k]
        int f=adjvex[k];
        sons[f][num_sons[f]]=k;
        num_sons[f]++;
        lowcost[k]=0;//等于0的话代表这个顶点已经加入生成树 下次取权值不会在用到它。
        for (j = 1;  j<=n; j++) {//更新lowcost数组
            if(lowcost[j]!=0&&v[k][j]<lowcost[j]){
                lowcost[j]=v[k][j];
                adjvex[j]=k;//j节点的前述节点为k
            }
        }
    }
  1. 打印树
    这里打印树形结构采用比较简单的做法,形式比较简单,每一行是一层,节点直接用数字,某节点的第一个子节点在其正下方,第二个子节点在第一个右边,以此类推。需要知道的数据有,何时换行,每个数字空几个空格。
    采用广度优先搜索,换行用深度标记,记录每次的历史深度,发现深度有变化,输出换行,空格数量要根据该节点子孙叶子节点决定。
for(int i=1;i<=n;i++){
        if(num_sons[i]==0){//该节点是叶子节点(儿子)
            int f=adjvex[i];//idx指向直系祖宗点
            do{
                num_endsons[f]++;
                f=adjvex[f];
            }while(f!=0);//直到追溯带0点结束
        }
    }
    int p=0,q=1,last_deep=0;//队列指针,上次深度信息
    int queue[MAX_N];
    queue[0]=1;
    printf("\r\n最小生成树如下:");
    while(p!=q){
        int idx=queue[p];//队首出队,idx是当前操作的节点号
        p++;//队首指针前移
        if(deep[idx]>last_deep){
            printf("\r\n");
            last_deep=deep[idx];
        }
        printf("%d",idx);
        for(int i=0;i<num_endsons[idx]-1;i++)
            printf(" ");
        for(int i=0;i<num_sons[idx];i++){
            int s=sons[idx][i];
            queue[q++]=s;
            deep[s]=deep[idx]+1;
        }
    }
    return 0;
}

测试效果

测试用例
在这里插入图片描述
7
11
1 2 5
1 3 11
2 4 12
3 4 24
2 6 8
2 5 2
4 7 18
3 7 20
6 7 3
5 6 4
5 7 15
在这里插入图片描述