最小生成树

最小生成树    


(一)  Prim

   算法思路:

          首先建立邻接矩阵,这邻接矩阵用文件读取的方式读取。然后建立一个distance辅助数组,该数组是记录以生长好的生成树到其余顶点的距离(这数组非常类似于Dijkstra算法的辅助数组)。先是设定从标号为0 的节点开始生成生成树。然后

1.      从distance数组中找到weight最小的那一个节点。然后记录下这条边。

2.      接着将这边记录下,再更新distance数组的信息。

将上面1.2.步骤循环vexnum– 1次。 

源代码:

#include<stdio.h>

#include<string.h>

#define INF 100

#define MAXVEX 100

typedef struct edge

{

    int node0, node1;

    int weight;

}edge;    //用来储存生成树的数据结构

 

typedef struct distance

{

    int node;

    int weight;

}distance;        //辅助数组  储存到其他顶点的最小距离 很类似Dijkstra算法中的辅助数组

 

void read(int m[][MAXVEX], int *vexnum, int *arcnum); //读取邻接矩阵

int  min(distance d[],int vexnum);                  //找到的 d[] 权重最小的

 

int main(void)

{

 

    int vexnum,arcnum;

    intm[MAXVEX][MAXVEX];

    read(m, &vexnum,&arcnum);

 

    distanced[vexnum];

    for(int i = 0; i< vexnum; i++)     //distance初始化 以 节点0 来生最小生成树

    {

        d[i].node = 0;

        d[i].weight =m[0][i];

    }

 

    edge e[vexnum -1];

    for(int v = 0; v< vexnum - 1; v++)

    {

       int k;

       k = min(d,vexnum);

 

       e[v].node0 =d[k].node;

       e[v].node1 = k;

       e[v].weight =d[k].weight;    //记录下新的树枝

 

       for(int i = 0;i < vexnum; i++)  //更新distance (因为有新节点的加入 可能会对distance产生影响)

       {

           if(m[k][i]< d[i].weight)

           {

              d[i].node = k;          //记录下新起点

              d[i].weight =m[k][i];

           }

       }

 

    }

 

    for(int i = 0; i< vexnum - 1; i++)   //打印

           printf("<%d, %d> %d \n", e[i].node0, e[i].node1, e[i].weight);

 

}

 

int min(distance d[], int vexnum)

{

    int min_weight =INF;

    int next_vex;

    for(int i = 0; i< vexnum; i++)

    {

        if(d[i].weight< min_weight && d[i].weight != 0)

        {

            min_weight= d[i].weight;

            next_vex =i;

        }

    }

 

    return next_vex;

}

 

void read(int m[][MAXVEX], int *vexnum, int *arcnum)

{

    FILE *fp;

    fp =fopen("D:\\test.txt", "r");

    fscanf(fp,"%d %d", vexnum, arcnum); //文件读取节点数和边数

 

    for(int i = 0; i< *vexnum; i++)  //邻接矩阵的文件读取

    {

        for(int j = 0;j < *vexnum; j++)

        {

            fscanf(fp,"%d", &m[i][j]);

        }

    }

    fclose(fp);

}

程序示意图

//文件中的100代表的是INF 就是图中这两个顶点不相邻

 

 

(二)Kruskal

算法思路:这个算法图需要用边数组这数据结构来存储(具体定义在源代码中)。接着以便的权重为关键字,将边从小到大排序(这代码用的时堆排序)。接着从权重最小的边开始选取边,并对顶点建立并查集。用并查集来判断所选的边是否有圈。重复选取vexnum– 1次。

源代码:

#include<stdio.h>

#define INF 100

typedef struct edge

{

    int node0, node1;

    int weight;

    int flag = 0;

}edge;

int find_father(int f[], int t);    //并查集

void sift(edge a[], int k, int m);  //堆排序

void heapsort(edge a[], int n);     //堆排序

 

int main(void)

{

    int vexnum;

    int edgenum;

 

    FILE *fp;

    fp =fopen("D:\\kruskal.txt", "r");

 

    fscanf(fp, "%d %d", &vexnum, &edgenum);

 

    edge e[edgenum];

    int set_[vexnum];//并查集

    for(int i = 0; i< vexnum; i++)

        set_[i] = i;

 

 

    for(int i = 0; i< edgenum; i++)

    {

        fscanf(fp,"%d %d %d", &e[i].node0, &e[i].node1, &e[i].weight);

    }

    fclose(fp);

 

    heapsort(e,edgenum);

 

    for(int i = 0; i< edgenum; i++)

    {

       if(find_father(set_, e[i].node0) != find_father(set_, e[i].node1))

          {

              set_[find_father(set_, e[i].node1)] = set_[e[i].node0];

              e[i].flag = 1;

          }

    }

 

     for(int i = 0; i< edgenum; i++)

        if(e[i].flag)printf("<%d, %d> %d\n", e[i].node0, e[i].node1, e[i].weight);

 

 

}

 

 

 

void sift(edge a[], int k, int m)  //这里有一个前提就是 k+1 到 m 是堆 这才能保证调用这函数后k 到m是堆

{

      edge temp;

      temp = a[k];

      int i, j;

      i = k; j = 2 *i;

      int flag = 0;

      while(j <= m&& !flag)

      {

          if((j + 1)<= m && a[j + 1].weight > a[j].weight) j++;

 

         if(temp.weight < a[j].weight) {a[i] = a[j]; i = j; j *= 2;}

          else flag =1;

      }

      a[i] = temp;

}

 

void heapsort(edge a[], int n)

{

    int temp;

    temp = n / 2;    // temp的位置是最后一结点的双亲

    for(int i = temp;i >= 0; i--) sift(a, i, n);

 

    edge t;

    for(int i = n; i>= 1; i--)

    {

        t = a[i]; a[i]= a[0]; a[0] = t;   //交换

        sift(a, 0, i -1);

    }

}

 

 

int find_father(int f[], int t)      //并查集的经典写法 注意这时间复杂度是相当的小的,

{                                                     //因为这在查找的同时也在不断的修改其数据的逻辑关系

    return (f[t] == t)? t : f[t] = find_father(f, f[t]);//也就是提高下次的查找性能

}

程序示意图:

//文件数据依次是顶点数,边数。 然后是每条边的起点、终点和权重。

 

 

 

 

 

              

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值