大话西游之王道考研数据结构第九讲---最短路、拓扑排序、关键路径

 

                             第九讲--最短路、拓扑排序、关键路径

复习

1.n个结点的无向图,如果他是连通的,最少有多少条边?最多有多少条边?

2.图有哪几种存储结构?

3.图的遍历方式(把上节课的例子再练习一下,无向图和有向图的遍历方式都得会)。

4.如何判断一个图是连通的?如何判断一个图里有多少个强连通子图?

6.无向图中删除某个顶点的复杂度是多少(用邻接表表示)?

5.最小生成树很重要!两种算法,算法之间的区别是什么?什么时候用哪种算法?具体的实现方

式是什么?

一、最短路(必考大题)

最短路主要求的是从一个特定的结点开始,分别到其他结点的最短路径是多少。

比如我们要求北京分别到其他城市的最短路径,我们最后应该求出一个一维数组dist[6]。这里我们对城市编号北京1,西安2,青岛3,武汉4,拉萨5,福州6。那么dist[5]表示北京到拉萨的最短距离,其他的都一样。

1.初始化,首先把北京到其他城市的距离进行初始化,也就是 dist[1] = 0,dist[2] = 6,dist[3] = 5,dist[4] = 1,dist[5] = 正无穷,dist[6] = 正无穷。然后把北京标记为1,代码中是flag[1] = 1(0代表还没更新),代表北京已经被更新了,并且是最短的。

for(int i = 1;i<=n;i++)//初始化
{
    //1表示北京  n表示一共有多少个城市
    dis[i] = a[1][i];  //a是邻接矩阵
}

2.选取距离最近的城市,并且这个城市是没有被更新的。

  minn = 9999999;
 for(int j = 1;j<=n;j++) //找到一个最小的
 {
      if(dis[j]<minn&&flag[j]==0)  //如果他是最小的,并且他还没有加入到北京的怀抱中
      {
           minn = dis[j];  //最小值替换
           min_r = j;  //最小值对应的城市替换
      }
 }

3.找到这个城市以后,更新dist,然后把这个城市标记为已更新。

 flag[min_r] = 1;
 for(int j = 1;j<=n;j++)
 {
     if(flag[j]==0&&dis[j]>(dis[min_r]+a[min_r][j]))
     {
          dis[j] = dis[min_r]+a[min_r][j];
     }
}

          然后1,2,3我们一共需要做n-1步,整体的代码是:

void Dijkstra()
{
    int sum = 0;
    int min_r,minn;
    for(int i = 1;i<=n;i++)//初始化
    {
        dis[i] = a[1][i];  //a是邻接矩阵
    }
    flag[1] = 1;
/*每次找最小距离的那个点 然后遍历那个点看有没有其他点更小 重复N-1次*/
    for(int i = 1;i<n;i++)
    {
        minn = 9999999;
        for(int j = 1;j<=n;j++) //找到一个最小的
        {
            if(dis[j]<minn&&flag[j]==0)
            {
                minn = dis[j];
                min_r = j;
            }
        }
        flag[min_r] = 1;
        for(int j = 1;j<=n;j++)
        {
            if(flag[j]==0&&dis[j]>(dis[min_r]+a[min_r][j]))
            {
                dis[j] = dis[min_r]+a[min_r][j];
            }
        }
    }

}

举个栗子

我们每一步操作转化为表格形式: 

可以看到,一共有5个点,我们需要遍历4趟,每一趟确定一个最短路(图中标红)。最下面一行是当前确定的结点。这个图得会画,并且得知道到某个结点的最短路分别由那些路组成。

二、Prim,Kruskal,Dijkstra三种算法的对比

1.prim和kurskal两个算出来的最小生成树一样吗?

2.为什么会造成最小生成树不一样?

3.最小生成树的代价一样吗?

4.是否所有权值最小的边都会出现在最小生成树中?

二、拓扑排序(考纲有,但是好像不考)

举个栗子

唐僧师徒五人要从大唐到达天竺取经,其中,其有好多条路供他们选择,但是每到达一个地方,必须有通过这个地方所需的通关文牒,比如通过拉萨,必须要有成都和深圳(连像拉萨箭头的那两个城市),而通过成都必须要有大唐的通关文牒。这里一条可行的路可以是:大唐->成都->深圳->拉萨->天竺。

1.选取一个不需要通关文牒的城市,比如大唐,获取他的通关文牒,然后把其所连接的成都和深圳的那两条线删除,代表这两个城市所需要的大唐通关文牒已获取。

2.重复步骤1,知道没有结点。

如果我们把成都->深圳的路删除了,会出现什么情况?

拓扑排序要求是不存在环路!

是什么原因造成拓扑排序不唯一?

三、 关键路径

这个东西有点像小学里面的最短时间花费题。比如小明早上起床,煮饭10分钟,洗菜5分钟,刷牙5分钟,吃饭半小时,洗完五分钟,听天气预报5分钟。问小明最短需要花费多长时间,哪些活动影响着小明的最短花费时间?而这些影响最短花费时间的活动就组成了关键路径。

 

在带权有向图中,以顶点表示事件,有向边表示活动,边上权值表示完成该活动的开销。

这算是我能想到的一个符合现实生活的例子(绞尽脑汁......),问从起床到走人最少需要多长时间?如果想提升总时间,我们应该加快那些步骤?

我们需要知道的是,穿衣的前提是洗完,而洗完必须执行上面的叠被+刷牙 = 5,以及下面的听天气+洗澡 = 6.所以洗完的最早时间应该是6分钟。而叠被和刷牙中有1分钟是可以任意安排的。也就是我们可以在叠被刷牙时候停1分钟也无所谓。而听天气和洗澡是一直要执行不能有间隔的,也就是他们最早发生时间和最晚发生时间是一致的。所以我们需要减少的是听天气和洗澡的时间,这样就会降低洗完所需要的总时间。但是也不能减少太多,比如听天气和洗澡时间减少为4时候,关键路径就改成叠被和刷牙了。

我们要求的就是最早发生时间和最晚发生时间之差为0 的活动组成的路径就是关键路径。

而活动又在边上,边的左面代表活动开始时间,边的右面代表活动的结束时间。所以我们应该先把边两边的结点时间求出来。

先求结点(事件)

1.求AOE网中所有事件的最早发生时间ve()

2.求AOE网中所有事件的最迟发生时间vl()

 

V1

V2

V3

V4

V5

V6

ve(i)

0

3

2

6

6

8

vl(i)

0

4

2

6

7

8

再求边(活动)

3.求AOE网中所有活动的最早开始时间e()

4.求AOE网中所有活动的最迟开始时间l()

再算算差值(差值为0 的就是关键活动,关键活动组成关键路径)

5.求AOE网中所有活动的差额d(),找出所有d()=0的活动构成关键路径

 

a1

a2

a3

a4

a5

a6

a7

a8

e(i)

0

0

3

3

2

2

6

6

l(i)

1

0

4

4

2

5

6

7

l-e

1

0

1

1

0

3

0

1

猜你喜欢

转载自blog.csdn.net/zhangbaodan1/article/details/81515165