45. 数据结构笔记之四十五克鲁斯卡尔算法

45. 数据结构笔记之四十五克鲁斯卡尔算法

           “假如生活欺骗了你 , 不要忧郁 , 也不要愤慨 ! 不顺心的时候暂且容忍 : 相信吧 , 快乐的日子就会到来。-- 普希金”

           上两篇学习了弗洛伊德和迪杰特斯拉算法。这次来看下克鲁斯卡尔算法。

1.  克鲁斯卡尔算法

克鲁斯卡尔(Kruskal)算法是在剩下的所有未选取的边中,找最小边,如果和已选取的边构成回路,则放弃,选取次小边。是实现图的最小生成树最常用的算法。

1.1         生成树定义

对于有 n 个顶点的无向连通图 G, 把遍历过程中顺序访问的两个顶点之间的路径记录下来,这样G中的n个顶点以及由出发点一次访问其余 n-1 个顶点所经过的 n-1 条边就构成了G 的极小连通子图,也就是 G 的一棵生成树,出发顶点是生成树的根。

 

1.2         最小生成树

给定一个连通网络, 要求构造具有最小代价的生成树时, 也即使生成树的各边的权值总和达到最小。 把生成树各边的权值总和定义为生成树的权, 那么具有最小权值的生成树就构成了连通网络的最小生成树。

构造最小生成树的算法有很多种,其中大多数的算法都利用了最小生成树的一个性质,

简称为 MST 性质:假设 G=(V,E)是一个连通网络,U 是 V 中的一个真子集,若存在顶点u ∈U 和顶点v∈(V − U ) 的边(u,v)是一条具有最小权值的边,则必存在 G 的一棵最小生成树包括这条边(u,v)。

构造最小生成树的常用算法主要有:Prim(普里姆)算法和 Kruskal(克鲁斯卡尔)算法。

本篇将的是克鲁斯卡尔算法,下篇我们将普里姆算法。

1.3         克鲁斯卡尔算法

设 G=( V,E )是一个有 n 个顶点的连通图,则令最小生成树的初始状态为只有 n 个顶点而无任何边的非连通图 T={V,{Ø}},图中每个顶点自成一个连通分量。依次选择 E 中的最小代价边,若该边依附于 T 中的两个不同的连通分量,则将此边加入到 T 中;否则,舍去此边而选择下一条代价最小的边。以此类推,直到 T 中所有顶点都在同一连通分量上为止。这时的 T 就是 G 的一棵最小生成树。

1.4         典型案例

最典型的是一个题目如下:

如下图1所示的赋权图表示某七个城市及预算它们之间的一些某些直接通信道路造价(单位:万元),试给出一个设计方案,使得各城市之间既能够通信又使总造价最小并计算其最小值。

           该图2的邻接矩阵为


赋权图3中 i,j 两个城市之间的造价费用边记为S ij, ,则从小到大排序

l  步骤1

初始化如下图4

l  步骤2

图5中选择造价最小的边,S17,此时 T={{2,3,4,5,6},{ S 17 }},造价 Cost=1

 

 

 

l  步骤3

图6接着选择造价第二小的序号2 的边,即S 34 ,加入 T 中,此时 T={{2,5,6},{ S 17 , S34 }},造价Cost=1+3=4

l  步骤4

以此类推,直到选择造价第五小的序号为 5 的边,即S 23,由于加入后边S23,S27,S37 将构成回路,因此舍弃该边.图7

 

l  步骤5

以此类推,直到所有顶点已包含在树中,整棵最小生成树已经构造完成。如图8

2.  代码实现

2.1      结构体定义

typedefstruct

{

           intbegin;

           intend;

           intweight;

}edge;

typedefstruct

{

           intadj;

           intweight;

}AdjMatrix[MAX][MAX];

typedefstruct

{

           AdjMatrixarc;

           intvexnum, arcnum;

}MGraph;

 

定义边,包括开始、结束和权值。

定义一个矩阵,每个矩阵点包含是否邻接数值以及权值。

定义一个图,包括矩阵和顶点数量。

 

 

 

 

2.2      main

根据输入来创建图,

然后生成最小生成树。

如下图9

 

2.3      CreatGraph

构造图。

输入城市和边数。

城市数量就是顶点数量。

根据顶点的数量,将每个顶点间的距离初始化为0。

然后输入城市之间的费用。先输入两个城市的坐标然后输入城市的费用(权值)。

如下图10

 

2.4      sort

对权值进行排序

输入参数为一个边的数组,图指针。

根据图中边的总数进行处理,

从第一个边开始,和其余的边进行比较,如果大于其它边则调用Swapn函数进行互换。

实现所有边权值从小到大的排列。

2.5      Swapn

将两个边的信息进行互换,包括起点、终点和权值。

 

2.6      MiniSpanTree

该函数用于生成最小生成树。

定义一个临时边的数组和整型数组。

根据顶点数量(城市数量),来处理所有边的起始点和结束点,以及权值,并保持在临时边数组中。

调用sort函数进行排序。

然后初始化临时的整型数组都为0.

最后循环,根据边的总数量,以当前循环边的起点和终点为参数调用Find函数。

最后得到最小费用值。

2.7      Find

判断整型数值是否大于0。

如果不大于0 ,则返回输入参数的数组下标。

如果大于0 ,则以该值为数组下标继续在数组中查找。

 

int Find(int *parent, intf)

{

           while (parent[f]> 0)

           {

                     f =parent[f];

           }

           returnf;

}

 

3.  源码

#include<stdio.h>

#include<stdlib.h>

#defineM20

#defineMAX20

//结构体定义

typedefstruct

{

           intbegin;

           intend;

           intweight;

}edge;

typedefstruct

{

           intadj;

           intweight;

}AdjMatrix[MAX][MAX];

typedefstruct

{

           AdjMatrixarc;

           intvexnum, arcnum;

}MGraph;

 

//函数申明

void CreatGraph(MGraph*);

void sort(edge*,MGraph*);

void MiniSpanTree(MGraph*);

int Find(int*, int );

void Swapn(edge*, int, int);

 

void CreatGraph(MGraph *G)//构造图

{

           inti, j,n, m;

           printf("请输入城市数及边数:");

           scanf("%d%d",&G->vexnum,&G->arcnum);

           for(i = 1; i <= G->vexnum; i++)//初始化图

           {

                     for (j = 1; j <= G->vexnum; j++)

                     {

                                G->arc[i][j].adj= G->arc[j][i].adj = 0;

                     }

           }

           printf("请输入有道路连通的 2 个城市及他们之间的造价费用(城市 1 城市2 费用):\n");

                     for (i = 1; i <= G->arcnum; i++)//输入边和费用

                     {

                                scanf("%d%d",&n,&m);

                                G->arc[n][m].adj= G->arc[m][n].adj = 1;

                                scanf("%d",&G->arc[n][m].weight);

                     }

                     printf("邻接矩阵为:\n");

                     for (i = 1; i <= G->vexnum; i++)

                     {

                                for (j = 1; j <= G->vexnum; j++)

                                {

                                           if(G->arc[i][j].adj==1)

                                                     printf("%d",G->arc[i][j].weight);

                                          elseprintf("%d ",G->arc[i][j].adj);

                                          G->arc[j][i].weight=G->arc[i][j].weight;

                                }

                                printf("\n");

                     }

}

void sort(edgeedges[],MGraph *G)//对权值进行排序

{

           inti, j;

           for (i = 1; i < G->arcnum; i++)

           {

                     for (j = i + 1; j <= G->arcnum; j++)

                     {

                                if (edges[i].weight> edges[j].weight)

                                {

                                          Swapn(edges,i, j);

                                }

                     }

           }

           printf("按造价排序之后的边顺序为(序号费用):\n");

           for(i = 1; i <= G->arcnum; i++)

           {

                     printf("%d.< %d, %d > %d\n", i,edges[i].begin,edges[i].end,edges[i].weight);

           }

}

void Swapn(edge *edges,inti, intj)

{

           inttemp;

           temp= edges[i].begin;

           edges[i].begin= edges[j].begin;

           edges[j].begin= temp;

           temp= edges[i].end;

           edges[i].end= edges[j].end;

           edges[j].end= temp;

           temp= edges[i].weight;

           edges[i].weight= edges[j].weight;

           edges[j].weight= temp;

}

void MiniSpanTree(MGraph *G)//生成最小生成树

{

           inti, j, n, m,Mincost=0;

           int k= 1;

           intparent[M];

           edgeedges[M];

           for (i = 1; i < G->vexnum; i++)

           {

                     for(j = i + 1; j <= G->vexnum; j++)

                     {

                                if (G->arc[i][j].adj== 1)

                                {

                                          edges[k].begin= i;

                                          edges[k].end= j;

                                          edges[k].weight= G->arc[i][j].weight;

                                          k++;

                                }

                     }

           }

           sort(edges,G);

           for(i = 1; i <= G->arcnum; i++)

           {

                     parent[i]= 0;

           }

           printf("最小生成树为:\n");

           for(i = 1; i <= G->arcnum; i++)//核心部分

           {

                     n= Find(parent, edges[i].begin);

                     m= Find(parent, edges[i].end);

                     if(n != m)

                     {

                                parent[n]= m;

                                printf("<%d, %d > %d\n", edges[i].begin, edges[i].end,edges[i].weight);

                                Mincost+=edges[i].weight;

                     }

           }

           printf("使各城市间能够通信的最小费用为:Mincost=%d\n",Mincost);

}

int Find(int *parent, intf)

{

           while (parent[f]> 0)

           {

                     f =parent[f];

           }

           returnf;

}

void main()//主函数

{

           MGraph*G;

           G =(MGraph*)malloc(sizeof(MGraph));

           CreatGraph(G);

           MiniSpanTree(G);

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值