数据基础及教程 10_第1页
数据基础及教程 10_第2页
数据基础及教程 10_第3页
数据基础及教程 10_第4页
数据基础及教程 10_第5页
已阅读5页,还剩26页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

一个有n个顶点的连通图的生成树是一个极小连通子图。生成树含有图中全部顶点,但只包含构成一棵树的n-1条边。如果在一棵生成树上添加一条边,必定构成一个环:因为这条边使得它依附的那两个顶点之间有了第二条路径。8.5.1生成树和最小生成树的概念8.5生成树和最小生成树1.什么是生成树1/31连通图:仅需调用遍历过程(DFS或BFS)一次,从图中任一顶点出发,便可以遍历图中的各个顶点。遍历中搜索边<v,w>时,若顶点w首次访问(该边也是首次搜索到),则该边是一条树边,所有树边构成一棵生成树。非连通图:需对每个连通分量调用一次遍历过程,所有连通分量对应的生成树构成整个非连通图的生成森林。2.连通图的生成树和非连通图的生成森林2/31由深度优先遍历得到的生成树称为深度优先生成树。由广度优先遍历得到的生成树称为广度优先生成树。无论哪种生成树,都是由相应遍历中首次搜索的边构成的。3.由两种遍历方法产生的生成树3/31

【例8.11】对于如下的无向图,画出其邻接表存储结构,并在该邻接表中,以顶点0为根,画出图G的深度优先生成树和广度优先生成树。02315647894/31邻接表中单链表按顶点编号递增排列!02315647890145237689DFS(0)0231564789DFS树5/31邻接表中单链表按顶点编号递增排列!0231564789BFS(0)BFS树014523768902315647896/314.什么是最小生成树一个带权连通图G(假定每条边上的权值均大于零)可能有多棵生成树.每棵生成树中所有边上的权值之和可能不同.其中边上的权值之和最小的生成树称为图的最小生成树。7/318.5.2普里姆算法1.普里姆算法过程

普里姆(Prim)算法是一种构造性算法。假设G=(V,E)是一个具有n个顶点的带权连通图,T=(U,TE)是G的最小生成树,其中U是T的顶点集,TE是T的边集,则由G构造从起始点v出发的最小生成树T的步骤如下:

(1)初始化U={v}。以v到其他顶点的所有边为候选边。

(2)重复以下步骤n-1次,使得其他n-1个顶点被加入到U中:

①从候选边中挑选权值最小的边加入TE(所有候选边一定是连接两个顶点集U和V-U的边),设该边在V-U中的顶点是k,将顶点k加入U中。

②考察当前V-U中的所有顶点j,修改候选边:若(k,j)的权值小于原来和顶点j关联的候选边,则用(k,j)取代后者作为候选边。8/31迭代思路vU其他顶点V-U移动顶点k,直到U=V将U和V-U之间的所有边称为割集移动的顶点k是割集中的最小边(*,k)9/3101243123546870124312354687UV-U示例10/310124312354687UV-U0124312354687UV-U11/310124312354687UV-U012431246构造的最小生成树12/312.普里姆算法设计closest[j]表示该最小边在U中的顶点。lowcost[j]表示该边的权值。lowcost[j]closest[j]vijUV-U采用邻接矩阵存储图。为了记录V-U中每个顶点j(j∈V-U)到U的最小边,建立两个数组closest和lowcost:lowcost[j]closest[j]=ivijUV-U表示为将V-U中顶点j到整个U的最小边表示为(j,closest[j]):lowcost[j]V-U中顶点j到U中的最小边为(i,j)13/31vijU={i|lowcost[i]=0}V-U={j|lowcost[j]≠0}对于任意顶点i,如何知道它属于集合U还是集合V-U呢?14/31初始时,U中只有一个顶点v,其他顶点i均在V-U中。viU={v|lowcost[v]=0}V-U={i|lowcost[i]≠0}如果(v,i)有一条边,它就是i到U的最小边,置closest[i]=v,lowcost[i]=g.edges[v][i]。如果(v,i)没有条边,不妨认为有一条权为∞的边,同样置closest[i]=v,lowcost[i]=g.edges[v][i]此时恰好有g.edges[v][i]为∞,看出邻接矩阵为什么这样表示的原因。初始化15/31jV-U={j|lowcost[j]≠0}查找V-U中最小边的顶点kintmin=INF;intk=-1; //k记录最近顶点的编号for(intj=0;j<g.n;j++){//在(V-U)中找出离U最近的顶点kif(lowcost[j]!=0&&lowcost[j]<min){min=lowcost[j];k=j;}}16/31V-Ulowcost[j]closest[j]U中k外的顶点kjUV-Ug.edges[k][j]更新:lowcost[j]=min(lowcost[j],g.edges[k][j])将顶点k移动到U后的调整jklowcost[j]V-UUclosest[j]17/31voidPrim(MatGraphg,intv){ //Prim算法输出的最小生树intlowcost[MAXV]; //建立数组lowcostintclosest[MAXV]; //建立数组closestfor(inti=0;i<g.n;i++){ //给lowcost[]和closest[]置初值lowcost[i]=g.edges[v][i];closest[i]=v;}初始化阶段18/31for(inti=1;i<g.n;i++){ //找出(n-1)个顶点intmin=INF;intk=-1; //k记录最近顶点的编号for(intj=0;j<g.n;j++) { //在(V-U)中找出离U最近的顶点kif(lowcost[j]!=0&&lowcost[j]<min){min=lowcost[j];k=j;}}cout<<"边(“<<closest[k]<<",”<<k<<"),权为”<<min<<endl;

lowcost[k]=0;

//标记k已经加入Ufor(intj=0;j<g.n;j++){ //修改数组lowcost和closestif(lowcost[j]!=0&&g.edges[k][j]<lowcost[j]){

lowcost[j]=g.edges[k][j];

closest[j]=k;}}}}lowcost[j]closest[j]U中k外的顶点kjUV-Ug.edges[k][j]19/31上述普里姆算法中有两重for循环,所以时间复杂度为O(n2),其中n为图的顶点个数。由于与e无关,所以普里姆算法特别适合于稠密图求最小生成树。算法特点20/318.5.3.克鲁斯卡尔算法1.克鲁斯卡尔算法过程

克鲁斯卡尔(Kruskal)算法是一种按权值的递增次序选择合适的边来构造最小生成树的方法。假设G=(V,E)是一个具有n个顶点的带权连通图,T=(U,TE)是G的最小生成树,则构造最小生成树的步骤如下:

(1)置U的初值等于V(即包含有G中的全部顶点),TE的初值为空集(即图T中每一个顶点都构成一个分量)。

(2)将图G中的边按权值从小到大的顺序依次选取:若选取的边未使生成树T形成回路,则加入TE;否则舍弃,直到TE中包含n-1条边为止。21/312.克鲁斯卡尔算法设计关键是如何判断选择的边是否与生成树中已有边形成回路?设置一个辅助数组vset[0..n-1],其元素vset[i]代表顶点i所属的连通分量的编号(同一个连通分量中所有顶点的vset值相同)。22/31初始时T中只有n个顶点,没有任何边,每个顶点i看成一个连通分量,该连通分量的编号就是i,即vset[i]=i。将图中所有边按权值递增排序,从前向后选边(保证总是选择权值最小的边),当选择一条边(u1,v1),求出这两个顶点所属连通分量的编号分别为sn1和sn2:如此这样直到在T中添加n-1条边为止。若sn1=sn2,说明顶点u1和v1属于同一个连通分量

不能添加该边。若sn1≠sn2,说明顶点u1和v1属于不同连通分量

添加该边。添加后原来的两个连通分量需要合并,即将两个连通分量中所有顶点的vset值改为相同(改为sn1或者sn2均可)。23/31构造的最小生成树01243123546871234601243012430000示例24/31从图G的邻接矩阵中获取所有边向量E:由于是无向图,将邻接矩阵上三角部分的所有边存放在边向量E中,每一条边对应的列表为[u,v,w],其中u、v分别为边的头尾顶点,w为边的权值。对E按权值w递增排序后做上述操作。E的元素类型如下:25/31structEdge{

//边向量元素类型

intu; //边的起始顶点

intv; //边的终止顶点

intw; //边的权值Edge(intu,intv,intw){ //构造函数this->u=u;this->v=v;this->w=w;}booloperator<(constEdge&s)const{ //重载<运算符returnw<s.w; //用于按w递增排序}};克鲁斯卡尔算法voidKruskal(MatGraph&g){ //Kruskal算法输出最小生成树intvset[MAXV]; //建立数组vsetvector<Edge>E; //建立存放所有边的向量Efor(inti=0;i<g.n;i++){ //由图的邻接矩阵g产生边向量Efor(intj=0;j<g.n;j++){if(g.edges[i][j]!=0&&g.edges[i][j]!=INF&&i<j)E.push_back(Edge(i,j,g.edges[i][j]));}}sort(E.begin(),E.end()); //对E按权值递增排序for(inti=0;i<g.n;i++)vset[i]=i; //初始化辅助数组26/31intk=1; //k是当前构造生成树第几条边,初值为1intj=0; //E中边的下标,初值为0while(k<g.n){ //生成的边数小于n时循环intu1=E[j].u;intv1=E[j].v; //取一条边的起始和终止顶点intsn1=vset[u1];intsn2=vset[v1]; //分别得到两个顶点所属的集合编号if(sn1!=sn2){ //两顶点属不同集合,取该边cout<<"边(“<<u1<<",”<<v1<<"),权为"<<E[j].w<<endl;k++; //生成边数增1for(inti=0;i<g.n;i++) //两个集合统一编号if(vset[i]==sn2) //集合编号为sn2的改为sn1

vset[i]=sn1;}j++; //扫描下一条边}}27/313*.改进的克鲁斯卡尔算法设计连通分量并查集中的一个子集28/31intparent[MAXV]; //并查集存储结构intrnk[MAXV]; //存储结点的秩voidInit(intn){ //并查集初始化for(inti=0;i<n;i++){ //顶点编号0到n-1parent[i]=i;rnk[i]=0;}}并查集初始化、查找和合并算法利用并查集的克鲁斯卡尔算法29/31voidKruskal1(MatGraph&g){ //改进的Kruskal算法输出最小生成树vector<Edge>E; //建立存放所有边的向量Efor(inti=0;i<g.n;i++){ //由图的邻接矩阵g产生边向量Efor(intj=i+1;j<g.n;j++){

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论