Matlab中求解最小生成树

合集下载

求最小生成树问题,常用的方法

求最小生成树问题,常用的方法

求最小生成树问题,常用的方法最小生成树(Minimum Spanning Tree)问题是一个经典的图论问题,其涉及到给定一个加权无向图,求其最小的生成树。

在实际问题中,求解最小生成树问题非常重要。

例如,最小生成树问题被广泛应用于网络设计、电路布线、机器学习等众多领域。

本文将介绍求解最小生成树问题的常用方法,包括Kruskal算法、Prim算法和Boruvka算法。

我们将详细介绍这些算法的原理和步骤,并给出各种算法的优缺点和适用情况。

1. Kruskal算法Kruskal算法是一种基于贪心策略的算法。

它首先将所有边按照权重大小排序,然后从小到大遍历边。

对于每个边,如果它连接了两个不同的连通块,则将这两个连通块合并成一个。

重复这个过程,直到所有的边都被考虑完。

最终的联通块就构成了最小生成树。

Kruskal算法具有简单、高效、容易实现的特点。

它的时间复杂度为O(E log E),其中E为边的数量。

Kruskal 算法的实现需要使用并查集。

Kruskal算法的优点是它是一种局部最优的策略,因此它能够在众多情况下得到最优解。

另外,它能够处理稀疏图和稠密图,因为它不需要全局访问图的结构。

2. Prim算法Prim算法也是一种基于贪心策略的算法。

它从一个任意的节点开始,不断加入与已经加入的节点相邻的最短边,直到所有节点都被加入。

这个过程类似于将一个连通块逐渐扩张为最小生成树。

Prim算法的时间复杂度为O(E log V),其中E为边的数量,V为节点的数量。

Prim算法的实现需要使用堆数据结构来进行边的最短距离的管理。

Prim算法的优点是它比Kruskal算法更加容易实现和理解。

另外,Prim算法能够处理不连通图,因为它从任意一个节点开始加入边。

此外,Prim算法也能够处理含有负权重的边的图。

3. Boruvka算法Boruvka算法是一种基于分治策略的算法。

它首先将所有的节点看作单独的连通块,然后每个连通块都选择当前权重最小的边加入。

最小生成树matlab代码

最小生成树matlab代码

最小生成树简介最小生成树(Minimum Spanning Tree)是图论中的一个重要概念。

它是一种用于连接所有节点的树,同时使得树中边的权值之和最小。

最小生成树在许多领域中都有广泛的应用,例如网络设计、电力传输、交通规划等。

算法Kruskal算法Kruskal算法是一种常用的解决最小生成树问题的算法。

它的基本思想是通过不断选择图中权值最小的边,并且保证这些边不会形成环,直到选择了足够多的边为止。

具体步骤如下:1.初始化一个空的最小生成树集合。

2.将图中所有边按照权值从小到大排序。

3.遍历排序后的边,如果当前边不会导致最小生成树形成环,则将该边添加到最小生成树集合中。

4.最终得到的最小生成树集合即为所求。

Prim算法Prim算法是另一种解决最小生成树问题的经典算法。

它的基本思想是从一个起始节点开始,逐步扩展最小生成树,直到覆盖所有节点。

具体步骤如下:1.初始化一个空的最小生成树集合。

2.随机选择一个起始节点,并将其加入到最小生成树集合中。

3.在最小生成树集合和图的剩余节点之间找到连接两部分的最小权值边,将该边和连接的节点加入到最小生成树集合中。

4.重复步骤3,直到最小生成树覆盖了所有节点。

应用场景网络设计最小生成树在网络设计中有着广泛应用。

例如,在计算机网络中,我们希望通过最小的成本将所有节点连接起来。

最小生成树提供了一种方法来实现这一目标,通过构建一个权值最小的树形网络,可以节省物理资源,提高网络传输效率。

电力传输在电力传输领域,最小生成树被用于设计最优的电力传输网络。

通过选择最小的成本边连接所有电力站点,可以减少电力传输的总成本,并优化电力的分布。

交通规划最小生成树在交通规划中也有着广泛的应用。

例如,在城市道路规划中,我们希望通过最小的道路建设成本将所有地区连接起来。

最小生成树算法可以帮助我们找到连接所有区域的最短路径,从而实现高效的交通规划。

MATLAB代码实现function [T] = minSpanningTree(G)% 输入参数:% G: 输入图的邻接矩阵表示% 输出参数:% T: 最小生成树的邻接矩阵表示n = size(G, 1); % 节点个数visited = zeros(1, n); % 记录节点是否被访问过T = zeros(n); % 初始化最小生成树的邻接矩阵visited(1) = 1; % 从第一个节点开始构建最小生成树while sum(visited) < n % 当所有节点都被访问过时停止minEdge = inf; % 最小权值边的权值minIndex = 0; % 最小权值边的终点for i = 1:nif visited(i) == 1for j = 1:nif visited(j) == 0 && G(i, j) < minEdgeminEdge = G(i, j);minIndex = j;endendendendvisited(minIndex) = 1; % 将新节点标记为已访问T(minIndex, :) = G(minIndex, :); % 将该边添加到最小生成树中T(:, minIndex) = G(:, minIndex);endend总结最小生成树是一种重要的图论概念,常用于解决连接所有节点的问题。

数学建模-最小生成树-kruskal算法及各种代码

数学建模-最小生成树-kruskal算法及各种代码

kruskal算法及代码---含伪代码、c代码、matlab、pascal等代码K r u s k a l算法每次选择n- 1条边,所使用的贪婪准则是:从剩下的边中选择一条不会产生环路的具有最小耗费的边加入已选择的边的集合中。

注意到所选取的边若产生环路则不可能形成一棵生成树。

K r u s k a l算法分e 步,其中e 是网络中边的数目。

按耗费递增的顺序来考虑这e 条边,每次考虑一条边。

当考虑某条边时,若将其加入到已选边的集合中会出现环路,则将其抛弃,否则,将它选入。

目录Kruskal算法Kruskal算法的代码实现Kruskal算法Kruskal算法的代码实现算法定义克鲁斯卡尔算法假设 WN=(V,{E}) 是一个含有 n 个顶点的连通网,则按照克鲁斯卡尔算法构造最小生成树的过程为:先构造一个只含 n 个顶点,而边集为空的子图,若将该子图中各个顶点看成是各棵树上的根结点,则它是一个含有 n 棵树的一个森林。

之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,也就是说,将这两个顶点分别所在的两棵树合成一棵树;反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。

依次类推,直至森林中只有一棵树,也即子图中含有n-1条边为止。

举例描述克鲁斯卡尔算法(Kruskal's algorithm)是两个经典的最小生成树算法的较为简单理解的一个。

这里面充分体现了贪心算法的精髓。

大致的流程可以用一个图来表示。

这里的图的选择借用了Wikipedia上的那个。

非常清晰且直观。

首先第一步,我们有一张图,有若干点和边如下图所示:第一步我们要做的事情就是将所有的边的长度排序,用排序的结果作为我们选择边的依据。

这里再次体现了贪心算法的思想。

资源排序,对局部最优的资源进行选择。

排序完成后,我们率先选择了边AD。

这样我们的图就变成了第二步,在剩下的变中寻找。

(原创)最小生成树之Prim(普里姆)算法+代码详解,最懂你的讲解

(原创)最小生成树之Prim(普里姆)算法+代码详解,最懂你的讲解

(原创)最⼩⽣成树之Prim(普⾥姆)算法+代码详解,最懂你的讲解Prim算法(哈⽋)在创建最⼩⽣成树之前,让我们回忆⼀下什么是最⼩⽣成树。

最⼩⽣成树即在⼀个待权值的图(即⽹结构)中⽤⼀个七拐⼋绕的折线串连起所有的点,最⼩嘛,顾名思义,要权值相加起来最⼩,你当然可以拿起笔来就算你脑中的每⼀种可能,但是如果你了解了这种算法,你就能跟我⼀样,⼀次画出完美答案。

上个栗⼦:我先说⼀哈这个算法的⽅法论,然后我们来代码实现⼀下,在讲解开始之前,敲⿊板,记得我们要⽣成⼀个权值最⼩的树,所以每⼀步都要考虑到树的每⼀个结点,不要孤⽴地⽤⼀个结点来对⽐从⽽⾛上死路,我们任选⼀个点开始⽣成,教材⾥选的 v0,那我们就选 v8,战⽃开始v8 有三条路,分别通往v1 v2 v3,v2那条路权值最⼩,ok, v2→v8,然后我们该看什么,如果你说找和 v2 相邻的 v8 以外的边,那我刚才的强调就gg了,我们找v2 和 v8除相连的线之外的所有分⽀,易得 v8→v1的权值最⼩,ok,下⼀步找哪⼏个点?v2 v1 v8这三个点除两条连接线以外的所有分⽀,挑最⼩的那⼀条,后⾯重复前⾯的操作,每次都把新加⼊的伙伴算在找线之内才对,⾃⼰画⼀下给答案:操作⼀遍是不是发现还真的跟哪个点开始没鸡⼉关系,因为每个点都要连到,关键就在于沿最⼩分⽀找点的时候⼀定要把它看成⼀个树结构来找,才算是最⼩⽣成树。

还是给⼀下标准定义:我们把构造连通⽹的最⼩代价(权值)⽣成树称为最⼩⽣成树(Minimum Cost Spanning Tree)。

⽅法论就到这⾥,相信下⼀次看到同样的现实问题,你也应该能在第⼀时间⽤正确的思路找到合适的路。

在代码实现之前,我们先请来连通图的好基友——邻接矩阵我们发现⼀⾏⼀⾏的矩阵很容易显⽰权值,这样就可以快速对⽐权值的⼤⼩,只要在循环的每⼀步留存下权值较⼩的边权值和顶点下标,就可以实现。

和以前⼀样,我们还是⽤ INFINITY 来表⽰⽆限⼤,即不存在该边代码如下:1void MiniSpanTree_Prim(MGragh G)2 {3int mini,i,j,k;4int adjvex[MAXVEX]; //保存相关顶点下标5int lowcost[MAXVEX]; //保存相关顶点间边的权值6 lowcost[0] = 0;//这⾥把第0位的权值置0表⽰v0已加⼊⽣成树7//ps:lowcost[i] = 0 表⽰i那个下标的顶点加⼊⽣成树8 adjvex[0] = 0; //初始化第⼀个顶点的下标为09for(i = 0; i < G.numVertexes; i++)10 {11 lowcost[i] = G.arc[0][i];//将vo相关顶点的权值存⼊lowcost数组12 adjvex[i] = 0;//置所有下标为v013 }14for(i = 1; i < G.numVertexes; i++) //最⼩⽣成树开始辽15 {16 mini = INFITINY; //先把权值的最⼩值置为⽆限⼤17 j = 1;18 k = 0;19while(j < G.numVertexes)20 {21if(lowcost[j] != 0 && lowcost[j] < mini)//判断并向lowcost中添加权值22 {23 mini = lowcost[j];24 k = j;25 }26 j++;27 }28 printf("(%d %d)",lowcost[k],k);29 lowcost[k] = 0;//置0表⽰这个定点已经完成任务,找到最⼩权值分⽀30for(j = 1; j < G.numVertexes; j++)31 {32if(lowcost[j] != 0 && G.arc[k][j] < lowcost[j])33 {34 lowcost[j] = G.arc[k][j];35 adjvex[j] = k;36 }37 }38 }39 }简单讲解⼀哈:4~5⾏,先说 adjvex[] ,这个数组要解决的问题就是存⼊已经安排好的那些顶点的下标,什么叫安排好了呢,⽐如我已经找到了 v0→v1 ,v1就可以算是安排好了,⽽v0点置0则算做初始化的操作;再说 lowcost[] 这个数组,听名字就是最⼩权值的意思,下⾯讲循环的时候详解这个东西到底储存了些什么,然后每次更新之后能做什么6~13⾏完全是初始化,要注意的是就是 lowcost[] 储存了邻接矩阵 v0 这⼀⾏的权值14~38⾏是最⼩⽣成树的整体代码16⾏就是每次都把最⼩值重置19~27⾏,从 1 开始遍历完全,找到现在这个状态下的最⼩权值数,并且把这个下标⽤ k 存住,28⾏就是把权值和下标打印出来,当然也可以换成别的操作,这⾥不再赘述然后29⾏,看看他都⼲了些什么,它把 adjvex[ k ] 置0,看⼀下第⼀点,这⾥表⽰ v1 完成任务,没有利⽤价值了然后30~37这个循环,看看循环的条件,条件⼀: lowcost[ j ] != 0 ,这是啥意思,表⽰在没有完成任务的顶点中选择,条件⼆: G.arc[k][j] < lowcost[j] 这表⽰在刚才找到的新顶点的矩阵那⼀⾏去对应,如果有更⼩的权值就把 lowcost[] 更新掉,这样就保证了这个数组中同时存在好⼏个顶点的权值信息,还是择优录⽤的,然后返回循环头,再找这次的最⼩权值点,周⽽复始。

Kruskal算法寻找最小树的matlab程序

Kruskal算法寻找最小树的matlab程序

Kruskal算法寻找最小树的matlab程序function tree=kruskal(d) %矩阵d为无向图的权矩阵,且是对称矩阵N=size(d,1);k=0; %记录图中边的条数I=max(max(d)); %I作为无穷大edge=zeros(N*(N-1)/2,3);%用于存储图中所有的边,边的条数最多为n*(n-1)/2for i=1:N-1 %因为权矩阵是对称阵,所以只用上三角矩阵。

for j=i+1:Nif d(i,j)<Ik=k+1;edge(k,1)=i;edge(k,2)=j;edge(k,3)=d(i,j);endendendedge=edge(1:k,:); %删除多余的行。

o_edge=edge; %用于存储排序后的边及边的端点。

for i=1:k-1 %用选择排序法进行升序排序for j=i+1:kif o_edge(i,3)>o_edge(j,3)temp=o_edge(i,:);o_edge(i,:)=o_edge(j,:);o_edge(j,:)=temp;endendendtree=zeros(N-1,3); %用于存放最小树中的边及边的端点,最小树中的边数为节点数减1 tree(1:2,:)=o_edge(1:2,:); %两条边一定不能构成圈,所以前面最小的两条边一定在最小树中。

line=3;for i=3:kif line==N %如果line=N说明tree矩阵已填满,最小树已经找到,跳出循环break;elseif line<Nif isempty(find(tree(:,1:2)==o_edge(i,1), 1))||isempty(find(tree(:,1:2)==o_edge(i,2), 1))%判断tree中已经确定的的所有边中的节点是否和新增加的边的两个端点都重复,若新边的两个端点不都重复,则为真%直接说明新边符合条件,加入tree中。

shortestpathtree matlab 函数

shortestpathtree matlab 函数

shortestpathtree matlab 函数矩阵中的所有节点之间都有一些路径连接着。

某些路径可能是直接的,而有些可能需要经过其他节点。

寻找从一个节点到另一个节点的最短路径是图论中一个经典的问题。

在 MATLAB 中,可以使用 shortestpathtree 函数来解决这个问题。

首先,让我们了解一下最短路径树(Shortest Path Tree)是什么。

最短路径树是一个有向图,它以某个源节点为根节点,并将所有其他节点连接到根节点的最短路径。

最短路径树通常用于网络中的路由选择和通信网络中的拓扑控制。

在 MATLAB 中,你可以使用 shortestpathtree 函数来计算具有有向边的有向图的最短路径树。

这个函数采用一个邻接矩阵作为输入,并返回一个有向图的最短路径树。

邻接矩阵是一个 N×N 的矩阵,其中 N 是图中节点的数量。

邻接矩阵的第 i 行第 j 列的元素表示从节点 i 到节点 j 是否有一条边。

如果有边连接,则该元素的值为非零,否则为零。

在有向图中,边是有方向的,所以邻接矩阵是对称的。

为了使用 shortestpathtree 函数,我们首先需要创建一个邻接矩阵。

我们可以使用 MATLAB 中的矩阵操作来实现这一点。

例如,我们可以使用 zeros 函数创建一个全零矩阵,然后使用索引操作将适当的元素设置为非零值,从而表示边的存在。

例如,假设我们有一个图,其中有四个节点,从节点 1 到节点 3 有一条权重为2 的边,从节点 1 到节点 4 有一条权重为 5 的边,从节点 2 到节点 3 有一条权重为 1 的边,从节点 2 到节点 4 有一条权重为 3 的边。

我们可以通过以下方式创建邻接矩阵:matlabadjacency_matrix = zeros(4);adjacency_matrix(1, 3) = 2;adjacency_matrix(1, 4) = 5;adjacency_matrix(2, 3) = 1;adjacency_matrix(2, 4) = 3;现在我们已经创建了邻接矩阵,我们可以使用 shortestpathtree 函数来计算最短路径树。

最小生成树matlab实现

最小生成树matlab实现

primclear all;close all;Graph1;%调用Graph1M文件,产生图1的邻接矩阵%Graph2;%调用Graph2M文件,产生图2的邻接矩阵len=length(graph_adjacent);%求图中有多少个顶点k=sprintf('please input the point where you want to start ,do rem ember it must be between 1 and %d ',len);start_point=input(k);%输入最小生成树产生起点while((start_point<=0)|(start_point>len))%如果输入的结点位置不合法即:小于等于零,或大于结点数,则重新输入disp('bad positon,please input again!');start_point=input(k);end;%************************************下面完成prim算法************** **************%相关变量初始设置tree=zeros(len-1,2);%用于保存选入最小生成树的边lowcost=zeros(1,len);%用来保存集合V-U与集合U中顶点的最短边权值,lowcost [v]=0表示顶点v已经%加入最小生成树中adjvex=zeros(1,len);%用来保存依附于该边在集合U中的节点,U集合为生成最小生成树的辅助集合,%首先U={start_point},之后依次确定为把最小生成树的一边的另一节点加入U%依次下去,直到图的全部顶点都在U中能找到lowcost=graph_adjacent(start_point,:);%lowcost(i)的值为节点i与start _point的权值;adjvex=start_point.*ones(1,len);%adjvex中所有元素的值都为初始节点%以下循n-1次,用于找出最小生成树的len-1条边for i=1:len-1k=lowcost>0;%k为一逻辑数组,它和lowcost同维,对于每一个位置i1lowcos t(i)>0则k(i)=1%否则k(i)=0;稍候将用这个数组进行辅助寻址cost_min=min(lowcost(k));%找出lowcost中除0外的最小值index=find(lowcost==cost_min);%找出此最小值在lowcost中的下标,即找到相应的节点index=index(1);%因为最小值的下标可能不止一个,这里取第一个下标进行处理 lowcost(index)=0;%表明该节点已经加入了最小生成树中tree(i,:)=[adjvex(index),index];%对lowcost和adjvex进行更新for j=1:lenif lowcost(j)>graph_adjacent(j,index);lowcost(j)=graph_adjacent(j,index);adjvex(j)=index;endendend;%*************************结果显示模块***************************** *******s=0;for ii=1:len-1k=sprintf('最小生成树第%d条边:(%d,%d),权值为%d',ii,tree(ii,1),tr ee(ii,2),graph_adjacent(tree(ii,1),tree(ii,2)));%格式化字符串%disp(k);%显示%disp(' ');%空一行s=s+graph_adjacent(tree(ii,1),tree(ii,2)); %求最小生成树的代价end%显示最小生成树的代价disp('最小生成树的总代价为:')disp(s);kruskalclear all;close all;Graph11;%调用以邻接矩阵储存的图所在的M文件%Graph22;len=length(graph_adjacent);%计算图中的顶点数temp=graph_adjacent;%将原图内容拷贝到temp中,以防对原图做改动superedge=zeros(len-1,2);%用于保存生成最小生成树的边i=1;%指向superedge的下标for j=1:lentag(j)=j;%关联标志初始化,将每个顶点的关联标志设为其本身end;%以下的循环完成kruskal算法while(superedge(len-1,1)==0)[Y,I]=sort(temp);%将temp的每列按从小到大排序,数组Y保存temp 排序后的结果,I中保存相应结果对应的在temp中的下标cost_min=min(Y(1,:));%找出权值最小的边index=find(Y(1,:)==cost_min);%找出权值最小的边对应的顶点index=index(1);%一条边对应两个节点,且不同的边的权值可能一样,这里为了方便处理人为规定了顺序,取标号最小的顶点进行处理anotherpoint=I(1,index);%找到该边对应的另一个顶点%将该边对应的权值修改为最大,防止该边在下次循环中再次被选为最优边temp(index,anotherpoint)=100;temp(anotherpoint,index)=100;if(tag(anotherpoint)~=tag(index))%当两个点不属于一个连通集时,这两个点之间的边为最小生成树的边superedge(i,:)=[index,anotherpoint];%将其加入最小生成树的边集中i=i+1;%下标加1%下面的语句的作用是将两个连通分支变成一个连通分支,即tag值一样for j=1:len%以index的tag值为标准if((tag(j)==tag(anotherpoint))&(j~=anotherpoint))%遍搜tag数组,先将和anotherpoint tag值一样的点的tag值变为index的tag值tag(j)=tag(index);endendtag(anotherpoint)=tag(index);%将anotherpoint的tag值变为in dex的tag值endend%*************************结果显示模块**************************** ********s=0;for ii=1:len-1k=sprintf('最小生成树第%d条边:(%d,%d),权值为%d',ii,superedge(ii,1),superedge(ii,2),graph_adjacent(superedge(ii,1),superedge(ii,2)));%格式化字符串%disp(k);%显示%disp(' ');%空一行s=s+graph_adjacent(superedge(ii,1),superedge(ii,2)); %求最小生成树的代价end%显示最小生成树的代价disp('最小生成树的总代价为:')disp(s);。

基于MATLAB的最短路径算法分析

基于MATLAB的最短路径算法分析

基于MATLAB的最短路径算法分析周志进(贵阳学院贵州贵阳550005)摘要:随着社会快速发展,人们生活水平提高,很多需求都在向着最优化、最快捷、最高效的方向延伸,而最短路径算法则是图论研究中的典型问题。

该文简要概述MATLAB软件,分析基于MATLAB的4种用于解决最短路径问题的算法,并研究基于MATLAB的最短路径算法的实际应用状况,以期对最短路径算法的应用提供一定借鉴意义。

关键词:MATLAB最优路径Dijkstra算法Floyd算法Bellman-Ford算法SPFA算法中图分类号:TP301.6文献标识码:A文章编号:1672-3791(2022)08(a)-0217-03最短路径算法就是用于计算一个节点到其他节点的最短路径问题,一般是指确定起点的最短路径问题,求起始节点到某一终点的最短路径问题,也常用于已知起点和终点,求解两节点之间的最短路径。

1MATLAB程序概述MATLAB是由美国MathWorks公司出品的数学软件,MATLAB意为矩阵工程,将用于一维、二维与三维数值积分的函数进行了统一,并经过基本数学和内插函数的辅助,提供数值分析、矩阵计算等诸多功能,为应用数学、工程设计和数值计算提供全方位的解决方案,很大程度上摆脱了传统程序设计语言的编辑模式。

其高效的数值及符号计算功能,可以帮助用户快速处理繁杂的数学运算问题,具备的图形处理功能可以实现计算结果和编程的可视化。

MATLAB本身是一个高级的矩阵语言,包括诸多算法、控制语句、函数等面向基本对象或问题的应用程序[1]。

比如:在最短路径计算中可以利用矩阵运算和线性方程组的求解或是数据的统计分析来优化相关问题。

2基于MATLAB的4种最短路径算法2.1Dijkstra算法Dijkstra(迪杰斯特拉)算法是最经典的单源最短路径算法,也就是用于计算一个节点到其他所有节点最短路径的算法。

Dijkstra算法采用贪心算法策略,每次遍历与起点距离最近且未访问过的节点,直至扩展到终点。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

数学建模——Matlab中求解最小生成树
关键词:Matlab、最小生成树、破圈法、避圈法、运筹学
要求:选择一道编程题自己独立完成,必须自己编写源代码,不能从网上下载。

先编写算法的通用程序,然后以例子运行,论文内容包括程序代码、程序说明、例子运行结果,最终程序文件连同论文一起发至e-mail,便于老师运行程序是否正确。

编程使用C或MATLAB。

选择题目:编写实现生成树、最小生成树的程序(包括避圈法、破圈法)。

题目分析:本题要求编写实现生成树、最小生成树的程序,首先来了解一下关于关于生成树的概念:
1、树的概念:
树是无向图的特殊情况,即对于一个N个节点的无向图,其中只有N-1条边,且图中任意两点间有且只有一条路径,即图中不存在环,这样的图称为树。

2、生成树的概念:
对于一个无向连通图G=(V,E),其中V代表顶点,E代表边,对它做一次遍历,每个节点经过一次,那么图中的N个节点再加上遍历过程中经过的N-1条边所构成的子图就是图G的一个生成树。

3、最小生成树的概念:
对于一个无向连通图G=(V,E),给它的每条边(u,v)赋一个权值w(u,v)。

若图G 的生成树不止一个,那么其中包含的N-1条边的权值之和的最小的生成树就是图G的最小生成树。

4、关于运筹学中最小生成树有2种不错的算法,即避圈法和破圈法,下面来看一下求解最小生成树的算法:
先看一下图示法表示的最小生成树:
<1>避圈法求解上面无向带权连通图的基本步骤是:
每步从未选的边中选取边e,使它与已选边不构成圈,且e是未选边中的最小权边,直到选够n-1条边为止。

<2>破圈法基本思想如下:
(1) 每次从图中选取任意一个圈, 然后去掉该圈中权值最大的边(如果存在多条相同权值的最大边,可以任意选择一条去掉即可) 使之不构成圈.
(2) 重复上述过程. 直到图中不再含圈且所有顶点均包含在图中为止, 就构成最小生成树.
它的算法图解如下:
5、我们在用matlab程序求解最小生成树的时候需要用到无向图的邻接矩阵,首先来了解一下邻接矩阵的概念。

邻接矩阵(Adjacency Matrix):是表示顶点之间相邻关系的矩阵。

设G=(V,E)是
一个图,其中V={v1,v2,…,vn}。

G的邻接矩阵是一个具有下列性质的n阶方阵:①对无向图而言,邻接矩阵一定是对称的,而且对角线一定为零,有向图则不一定如此。

②在无向图中,任一顶点i的度为第i列所有元素的和,在有向图中顶点i的出度为第i行所有元素的和,而入度为第i列所有元素的和。

③用邻接矩阵法表示图共需要n^2个空间,由于无向图的邻接矩阵一定具有对称关系,所以扣除对角线为零外,仅需要存储上三角形或下三角形的数据即可,因此仅需要n(n-1)/2个空间。

看一个例子:
上面的邻接矩阵是带权邻接矩阵,有两种表示方法,我们往往只使用第一种,它的正对角线上全部是0,方便在程序中求解。

编程内容:
有了以上的数学基础,就可以在Matlab中编写适当的程序来求解无向带权图的最小生成树。

下面先用避圈法实现求解,代码中有详细的解析说明:
<1>避圈法Matlab程序代码:
%编程工具Matlab;
%这是一个通过避圈法求解连通带权图的最小生成树的程序.
n=input('请输入图的顶点数目:n= ')
W=input('请输入图的加权邻接矩阵:[W(1,1),..,W(1,n);..;W(n,1),..,W(n,n)]=')
%用W(i,i)="inf" 代替 "=0"
%准备工作
T=zeros(n); %最小生成树的加权邻接矩阵
WW=W;
for i=1:n
for j=1:n
if W(i,j)==inf WW(i,j)=0;
end
end
end
m=((nnz(WW))/2); %图的边线的数目
j=0; %最小生成树的边线的数目
%主要步骤
for i=1:m %被选择边线的数目
if j<(n-1) %算法的终止条件是 |E|=|V|-1.
%步骤0: 挑选出权值最小的边 W(a,b)
min=inf; a=0; b=0;
for k=1:n
for l=(k+1):n
if W(k,l)<=min min=W(k,l); a=k; b=l; end
end
end
%步骤0 结束
%步骤1
%T=T+e(a,b)
T(a,b)=W(a,b); T(b,a)=W(a,b);
%检查是否有环的出现
f=0; %没有环的出现
P=zeros(2,m); y=0;
for i=1:n
for v=(i+1):n
if T(i,v)~=0 y=y+1; P(1,y)=i; P(2,y)=v; end
end
end
for y=1:m
if P(1,y)<P(2,y)
for l=(y+1):m
if P(1,l)==P(2,y) P(1,l)=P(1,y);
elseif P(2,l)==P(2,y) P(2,l)=P(1,y);
end
end
P(2,y)=P(1,y);
elseif P(2,y)<P(1,y)
for l=(y+1):m
if P(1,l)==P(1,y) P(1,l)=P(2,y);
elseif P(2,l)==P(1,y) P(2,l)=P(2,y);
end
end
P(1,y)=P(2,y);
elseif (P(1,y)+P(2,y))~=0 f=1; %出现一个环
break
end
end
if f==1 T(a,b)=0; T(b,a)=0; %转到步骤2
else j=j+1; %转到步骤3
end
W(a,b)=inf;
else %如果条件|E|=|V|-1成立
MST=T;
input('这个图的最小生成树的加权邻接矩阵是:')
MST
break
end
end
if j<(n-1) %如果条件|E|<|V|-1成立
input('这个图没有最小生成树.')
end
代码完毕。

上面是全部的源代码,现在来实际求解上面无向连通图的最小生成树。

这里只需要输入一些数据即可得到最小生成树的邻接矩阵:
首先输入无向图的顶点数目:
然后输入无向图的加权邻接矩阵,注意其中inf代表0,并且输入时注意区分“,”和“;”的区别:
输入如下图:
即输入:
W=[inf,6,1,5,inf,inf;6,inf,5,inf,3,inf;1,5,inf,5,6,4;5,inf,5,inf,inf, 2;inf,3,6,inf,inf,6;inf,inf,4,2,6,inf]
然后按回车键即可得到答案:
从已经分析的最小生成树我们可以知道这个结果是正确的。

<2>破圈法Matlab程序:
破圈法
function A = fun(W)
[m, n] = size(W);
e = 0;
for i = 1 : n
for j = i : n
if W(i, j) ~= 0
e = e + 1;
E(e, :) = [i, j, W(i, j)];
end
end
end
% 按权值大小排列边的顺序
for i = 1 : e - 1
for j = i + 1 : e
if E(i, 3) > E(j, 3)
temp = E(j, :);
E(j, :) = E(i, :);
E(i, :) = temp;
end
end
end
A = zeros(1, 3);
S = 1 : n;
for i = 1 : e
% if find-set(u) ~= find-set(v)
if S(E(i, 1)) ~= S(E(i, 2))
% A = A + (u, v)
A = cat(1, A, E(i,:));
%union(u, v)
indicator = S(E(i, 1));
for j = 1 : n
if S(j) == indicator
S(j) = S(E(i, 2));
end
end
end
end
A(1, :) = [];
代码完毕。

输入无向图的带权邻接矩阵后,按回车再输入fun(W)即可。

我们仍然使用最开始的例子来验证一下,输入如下:
>>W=[inf,6,1,5,inf,inf;6,inf,5,inf,3,inf;1,5,inf,5,6,4;5,inf,5,inf,in f,2;inf,3,6,inf,inf,6;inf,inf,4,2,6,inf]
输入fun(W),得到结果如下图:
结果也是完全正确的。

题目展望:最小生成树在求解最短路径最小费用等最优化问题上具有十分重要的作用,而随着科技的进步和发展,需要我们处理的最优化问题可能涉及大量的数据,这时利用先进的计算机工具来求解这样的问题可以使问题变得简单,所以掌握最小生成树的编程算法,也显得十分必要。

相关文档
最新文档