您的位置:首页 > 其它

《算法导论(第二版)》习题22.1-6:图的通用汇点(Universal Sink)

2013-10-11 20:47 363 查看

问题

 

  如果我们用邻接矩阵来存储图,那么绝大多数图算法的运行时间都是Ω(|V|2)(V为一个图的顶点集),但还是有些例外。比如,给定一个有向图G的邻接矩阵A,我们可以在Ο(|V|)时间内判断图G是否包含一个通用汇点,即一个入度为|V|-1出度为0的顶点。请给出这样的算法。


教师手册的解法

 

  (注:教师手册的解法和我的基本一样,只不过在细节上有略微差别。在我的算法中,“光标”始终在A的对角线和上三角区域中活动,而教师手册的算法则没有这个特点。另外教师手册的阐述貌似更通俗易懂一些。我的论述可能太数学化了。)

  首先实现一个子过程IS-SINK。该过程判断一个给定的顶点k是不是通用汇点,是则返回TRUE,否则返回FALSE:



[align=left] [/align]

  因为这个子过程的运行时间是O(|V|),所以如果要在O(|V|)时间内判断图G是否包含一个通用汇点,我们就只能对该子过程调用O(1)次。

  同时我们可以注意到,一个有向图最多只能包含一个通用汇点。这是因为,如果某个顶点j是通用汇点,那么对所有的i ≠ j都有(i, j) ∈ E,这样其它顶点i都不可能是通用汇点了。

  以下算法以邻接矩阵A作为输入,如果A含有通用汇点,该算法会将通用汇点的标识输出,否则会输出“没有通用汇点”的消息。



[align=left] [/align]

 

  UNIVERSAL-SINK从邻接矩阵的左上角开始,根据当前元素A[i, j]是0还是1,将“光标”向右或向下移动一个位置。当i或j大于|V|时则停止迭代。

  和我的解法类似,为了证明UNIVERSAL-SINK的正确性,我们需要证明:当while循环终止时,i是唯一有可能是通用汇点的顶点。随后对IS-SINK的调用就是检查i是否符合通用汇点的定义。

  我们先把while循环终止时i和j的值固定下来。所有符合1 ≤ k < i的顶点k都不可能是通用顶点。这是因为在while循环中,i的值被增大的唯一原因就是当前元素为1,于是在while循环终止时,第i行上方的每一行都包含至少一个1。而根据我们一开始阐述的思路,若第k行有至少一个元素为1,那么k就不可能是通用汇点。

  如果循环终止时i > |V|,那我们就将所有顶点都排除了,所以G里面不可能有通用汇点。而如果循环终止时i ≤ |V|,我们就需要证明序号大于i而小于等于|V|的顶点都不可能是通用汇点。如果循环终止时i ≤ |V|,那就肯定有j > |V|。这就意味着我们在每一列都找到了0。回顾之前的思路,如果第k列在非对角线位置上有一个0,那么顶点k就不可能是通用汇点。我们在所有的第k(i < k ≤ |V|)列中都发现了0;而我们在while循环中还从没检查过第i行以下的任何一行,也就是说,我们还没检查过这些第k列对角线上的元素,因此这些第k列所包含的0都是在非对角线的位置上。因此所有满足i
< k ≤ |V|的顶点k都不是通用汇点。

  因此,所有小于i的顶点和所有大于i的顶点都不是通用汇点。唯一可能是通用汇点的就是i。while循环之后对IS-SINK的调用就是检查i是否为通用汇点。

  和我的算法同理,while循环每次迭代时i或者j都会增加(不过在这里要么是i增加,要么是j增加,且增量始终为1,因此在某些情况下,速度上估计比我的算法稍微慢一丁点儿)。因此,while循环最多只迭代2|V| - 1次,每次迭代的时间都是O(1),因此while的总时间为O(|V|)。再加上时间复杂度同样为O(|V|)的IS-SINK,UNIVERSAL-SINK的时间复杂度就是O(|V|)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法导论 算法 数学