数据结构—图II

来源:互联网 发布:js 读取ios沙盒文件 编辑:程序博客网 时间:2024/06/07 03:32

图的应用

Prim算法

1.        初始化:向空树T=(Vt,Et)中添加图G=(V,E)的任一顶点u0, 使Vt={u0}, E1=

2.        循环(重复下列操作至Vt=V):从图G中选取满足{<u,v>|u且具有最小权值的边<u,v>,并置Vt=Vt    [居然不会有回路的情况?原因在于u,v一定是属于不同的集合]







伪代码:

Void Prim(G,T)

{

         T=;

         U={w};                                  //初始化

         While((V-U)!=)

         {

                   设<u,v>是使u

                   Vt=Vt                                                    //边和顶点归入树

                  

                  

         }

}

 

Prim算法时间复杂度为O(V^2),不依赖于E,适合求稠密图的最小生成树。


克鲁斯卡尔算法(kruskal)

 

:是一种按权值的递增次序选择合适的边来构造最小生成树。

 

【步骤】

1.        初始化:Vt=V,  Et=。即每个顶点构成一棵独立的树。T此时是一个仅含V个顶点的森林

2.        循环(重复下列操作至T是一棵树):按G的边的权值递增顺序依次E-Et选择一条边,如果这条边加入T后不构成回路,则将其加入Et,否则舍弃,直到Et中含有n-1条边。

 

【伪代码】

Void Kruskal(V,T)

{

         T=V;

         Nums=n;                                                                            //不连通分量数

         While(numS>1)

         {

                   从E中取出权值最小的边<v,e>

                   If(v和u属于T中不同的连通分量)                //即不构成回路

                   {

                            T=T

                            numS--;                                                            //不连通分量减1

                   }

         }

}

 

Kruskal算法时间复杂度为O(ElogE),适合求边稀疏而顶点较多的图。 用堆来存放边集合。



最短路径

 

Dijkstra算法求单源最短路径

【步骤】

1.        初始化:集合S初试为{0}dist[]初始值dist[i]=arcs[0][i],i=1,2…….n-1

2.        从顶点集合V-S中选出Vj,满足dist[j]=Min{ dist[i]|ViV-S } ,Vj就是当前求得的一条从V0出发的最短路径的终点,令S=S{j}

3.        修改从V0出发到集合V-S上任一顶点Vk的可达的最短路径:如果 dist[j]+arcs[j][k]<dist[k],则令dist[k]=dist[j]+arcs[j][k].

4.        重复2-3)操作共n-1次,直到所有顶点都包含在S中。









关于记录路径上相应结点的直接前驱顶点的编号:如果d[j]+e[j][k]<d[k], p[k]=j,否则 p[k]=V0;

 

 

 

【代码】

Void shortestpath_Dij()

{

         For(v=0;v<n;v++)

                   S[],d[],p[]初始化

         D[v0],s{0},                                                                       //V0加入集合S中

 

         //开始主循环,每次求得V0到某个顶点V的最短路径,就把v加入到S 中。

         For(i=1;i<n;i++)                                     {

         {

                   For(w=0;w<n;w++)

                            If(w在V-S中)

                                     找到当前离V0最近的点 min=d[w],v=w

                   将找到的点v加入集合中

 

                   For(w=0;w<n;w++)

                   {

                            if(w在V-s中且min+e[v][w]<d[w])

                            {

                                     D[w]=min+e[v][w]                                //更新改点当前到V0的最短距离

                                     P[w]=v

                            }

                            //else d[w],p[w]都是原来的,不用再改变。

                   }       

 

         }

}

 

 

【算法评价】

l  时间复杂度为O(N^2)。求解源点到某一特定顶点的最短路径,同求解源点到所有顶点最短路径一样复杂。(贪心算法:基于当前最优解)。如果要找出所有结点对之间的最短距离,需对每个结点运行一次dijstra算法,时间复杂度为O(n^3)

l  空间复杂度:S[],D[],P[],也就是O(3N)

 

【注】 dijstra算法边权值不允许为负。因为已加入最短路径顶点集S的顶点不再变更。而负值的情况却可能出现min+e[v][w] <d[w],而此时dijstra是无法更新的。.  



Floyd算法

 

 

【算法描述】

         定义一个n阶方阵序列:,……..,其中:

                                     [i ,j] =arcs [ i , j ]

[ i , j ] = Min { [ i , j ] ,[ i ,k ] + [ k , j ] } 

 

其中:[ i , j ] 是从顶点Vi到Vj,中间顶点是V0的最短路径的长度,[ i , j ] 是从Vi到Vj,中间顶点的序号不大于K(原因在于:如果[ i , j ] =[ i , j ],即中间顶点序号为k-1,同理往后递推,可知,这里表示的是中间结点序号不大于K)的最短路径的长度。

 

经过n次(0….n-1)迭代后,所得到的 就是Vi 到Vj的最短路径长度,即方阵

 就保存了任意一对顶点之间的最短距离。

 

【注】基于上一轮的最短路径对,再逐个加入中间点,判断是否有必要更新,直到中间点为n-1.

 

 

【示例】








【性能分析】

l  时间复杂度 O(n^3),[外层n个中间点的遍历,内层N阶方阵的遍历N*N,总的为N*N*N]

l  空间复杂度O(n^2),n阶最短路径方阵A

 

【特点】允许带负权值的边(中间点可替换),但不允许有包含带负权值的边组成的回路。    ??????




拓扑排序[h1] 

 

【引】

         有向无环图(DAG - directed acylinegraph)

 

         AOV网(activity onvertex):如果用DAG图表示一个工程,其顶点表示活动,用有向边< Vi ,Vj >表示活动Vi         必须先于活动Vj进行的这样一种关系,则将这种有向图称为顶点表示活动的网络,记    为AOV网。 

 

 

 

【定义】

         在图论中,由一个有向无环图的顶点组成的序列,当且仅当满足下列条件时,称为该图的一个拓扑序列。

1.        每个顶点出现且只出现一次

2.        若顶点A在序列中排在顶点B前面,则在图中不存在从顶点B到A的路径。

 

         【每个DAG图都有一个或多个拓扑序列(不免存在多个入度为0的点)】

 

 

【算法步骤】对一个DAG图来说

A.       从DAG图中选择一个没有前驱结点的顶点并输出

B.       从图中删除该顶点的所有以它为起点的有向边      【逻辑删除】

C.       重复A,B直到当前DAG图为空或当前图中不存在无前驱结点的顶点为止(即每个顶点都有前驱,表明图中有环

 

【伪代码】

         while(!IsEmpty(S))

         {

                   Pop(S,i);                                                  //栈顶元素出栈

                   print[count++]=i;                                  //输出顶点i

                   for(p=G.vertice[i].firstarc;p;p=p->nextarc)

                   {

                            //将所有i指向的顶点的入度减1,并且将入度为0的顶点压入栈S

                            v=p->adjverx;

                            if(!(--indegree[v]))                 //入度为0,压入栈

                                     Push(S,v);

                   }

         }

 

【性能分析】

l  时间复杂度:由于输出每个顶点的同时还要删除以它为起点的边,故时间复杂度为O(V+E)(基本操作数)

l  空间复杂度:借助了一个辅助栈来记录运行时入度为0的顶点,长度为V的个数,故空间复杂度为O(V)

 

 

【小结】

1.        深度优先遍历可以实现拓扑排序?   咋实现的呢

2.        由于DAG图给顶点地位平等,每个顶点编号是人为的,因此可以按照拓扑排序的的结果重新安排顶点序号,生成的DAG图的新的邻接矩阵表示,这种邻接矩阵可以使三角矩阵(按照拓扑序列来编号,则邻接矩阵元素是有序排列的

3.        对于一般的图,如果它的邻接矩阵是三角矩阵,则存在拓扑序列(但不唯一)。反之,则不一定成立。






关键路径

【引】

         在带权有向图中,顶点表示事件,有向边表示活动,边上的权值表示该活动的开销,则称这种有向图为用边表示活动的网络,简称AOE(activity on edge)网。       【活动在边上】

        

         AOE的性质:

1.        只有在某顶点所代表的事件发生后,从该顶点出发的各个有向边所代表的活动才能开始       【顶点   —>  有向边】



2       只有在进入某一顶点的各有向边所代表的活动都已经结束,该顶点所代表的事件才能发生。   【有向边–->   顶点】




3     AOE网中,有且分别仅有一个源点(入度为0,表示工程开始)和汇点(出度为0,表示工程结束)。

4        在AOE网中,有些活动可以同时进行(不在同一路径上的)。从源点到汇点的有向路径可能多条,并且长度可能不同。 但是只有所有路径上的活动都完成了,整个工程才算结束了。



【定义】

         关键路径和关键活动:

                   从源点到汇点的所有路径中,具有最大路径长度的路径称为关键路径。把关键路径上的活动成为关键活动。

 

         【注】 整个工程的最短时间就是关键路径上各活动的开销的总和。因此,只要找到了关键活动,就找到了关键路径,也就可以得出最短完成时间。

 

1.        事件Vk的最早发生时间Ve(k)

它是指从开始顶点V到Vk的最长路径长度(刚好,所有的前驱活动都完成了)

事件的最早发生时间决定所有从Vk开始的能够开工的最早时间。 (这也就是为什么 e(i)=Ve(k)  <Vk,Vj>的活动为ai

                    

                   Ve(源点)=0

                   Ve(k)=MAX{Ve( j ) + weight( Vj , Vk)},

                   【注】计算Ve(k)时,是从前往后的顺序来计算的            

 

2.        事件Vk的最迟发生时间Vl(k)

它是指在不推迟整个工程完成的前提下,即保证它所指向的事件Vi在Ve(i)时刻能够发生时,该事件最迟必须发生的时间。

 

Vl(汇点)=Ve(汇点)  首先汇点的最早完成时间保证了不推迟整个工程的完成

 

Vlj)=Min{ Vl(k) – weight( Vj , Vk) }    Vlk- weight( vj , vk )保证了每一步都不推迟整个工程,但是取最小值(针对不同的终点Vk)?越早开始越不会推迟整个工程。

                   【注】计算Ve(j)时,是从后往前的顺序来计算的    

 

3.        活动ai的最早开始时间

它是指该活动起点所表示的事件最早发生时间。如果边< Vk , Vj >表示活动ai,则有

e(i) = Ve(k)

 

4.        活动ai的最晚开始时间

它是指该活动的终点所表示的事件最迟发生时间与该活动所需时间之差。如果边< Vk , Vj >表示活动ai,则有 l(i)= Vl(j) – weight( Vk , Vj )

 

5.        一个活动ai的最迟开始时间l(i)和其最早开始时间e(i)的差额 d(i)=l(i)-e(i)

它是指在不增加完成整个工程所需的总时间(不延迟整个工程)的情况下,活动ai可以拖延的时间。

如果一个活动的时间余量为0,说明该活动必须如期完成,否则就好拖延完成整个工程进度,所以称d(i)为0 的活动为关键活动。

 

 

【小结】

l  关键路径上的活动都是关键活动,它是决定整个工程的关键因素,因此可以通过加快关键活动来缩短整个工程周期。但也不能任意缩短关键活动,因为一旦缩短到一定程度,该关键活动可能就变成了非关键活动。【只能相对的缩减】

l  网中的关键路径并不一定是唯一的。对于有几条关键路径的网,只有提高关键路径上公共关键活动才能达到缩减工期的目的。

                  

【注】对于有多条关键路径的情况,那么它们的长度一定相同(如果存在不相同的情况,那么一定是最大路径长度的那条为关键路径,这就和已知矛盾了。),所以计算关键路径长度的时候,只需要选择其中一个路径长度即可。

 

 

 

【疑问】

1.        关键路径中,如何缩短工程周期,加快公共活动吗?为什么呢?

答:只能是相对缩短关键活动如果网中有多条关键路径,则缩短公共的关键活动,对于缩短独立的关键活动,由于会改变网中关键路径,产生不可预知的效果。

如果只有一条,则相对缩短关键路径中任意一个关键活动

 

2.        当改变网上某一关键路径上任一关键活动会有什么影响?

答:如果改变的是关键路径上的公共活动,则不一定会产生不同的关键路径(延长一定不会【关键路径就是最大路径长度】,而缩短有可能导致【缩短过度的话】)

如果改变的是关键路径上的非公共活动,则一定会产生不同的关键路径(延长或者缩短都会减少关键路径条数)

 

3.        当改变关键路径上的任一活动时会有什么影响?

答:延长的话关键路径不会改变,而缩短的话,相对范围内可以起到加快整个工程进度的效果,但如果缩短过度的话,则有可能产生不同的关键路径




0 0
原创粉丝点击