第三章的主要思想就是DFS。讲了图上的DFS操作,然后讲了各种应用。这章默认图都是用邻接矩阵存的。

  1. procedure explore(G, v)
  2. Input: G = (V, E) is a graph;
  3. Output: visited(u) is set to true for all nodes u reachable from v
  4. visited(v) = true
  5. previstit(v)
  6. for each edge(v,u) in E:
  7. if not visited(u); explore(u)
  8. postvisit(v)
  9. procedure dfs(G)
  10. for all v in V
  11. visited(v) = false
  12. for all v in V
  13. if not visited(v) explore(v)
  14. procedure previsit(v)
  15. pre[v] = clock
  16. clock = clock+1
  17. procedure postvisit(v)
  18. post[v] = clock
  19. clock = clock+1

上面的代码就是这章里面所用到的所有代码了,也是DFS的整个代码,首先对于每个点,如果没有被访问过,那么就从这个点开始进行DFS,也就是explore过程,explore过程中,会访问和当前点相连的所有点[即当前点的可达点],previst和postvisit过程只是记录每个点的pre值和post值,这个值在某些方面还是很有用的,后面会说到。这整个DFS的时间复杂度是O(V+E)的,每个点和每条边都只访问一次。

在有向图中,对边进行了一些其他的定义
Tree edges: 指向儿子节点的边
Forward edges:从一个点指向非儿子后继节点的边
Back edges: 从一个点指向其祖先节点的边
Cross edges: 既不指向后继节点也不指向祖先节点的边

对于不同的边,这些点的pre值和post值的关系如下所示
pre/post ordering for(u,v) Edge type
pre[u] pre[v] post[v] post[u] tree/forward
pre[v] pre[u] post[u] post[v] back
pre[v] post[v] pre[u] post[u] cross

接下来将的是DAG[directed acyclic graphs]也就是有向无环图。在DAG中,每个点的后继节点的post值都会比当前节点的小,每个DAG至少有一个源点,一个汇点。DAG延伸出来的就是强联通分量了。

对于有向图,对所有的对 in V,都有从u到v的路径,那么就是强联通图,这里<1,2>和<2,1>表示不同的顶点对。如果我们从一个有向图的汇联通分量中的某个点开始进行DFS搜索的话,那么最后会且仅会把整个汇联通分量的点都找出来。这样的话我们如果知道某个汇联通分量里面的一个点,那么就可以知道整个汇联通分量,现在的问题是(I)怎么知道汇联通分量里面的一个点,和(II)求得一个汇联通分量之后要怎么做?

我们知道对于汇点[把联通分量缩成一个点]来说,我们没有好的方法可以容易,轻松的求得,但是对于源点我们就有比较简单的方法可以得到。首先我们可以知道,如果一个点的post值最高的话,那么这个点一定是源点,这个可以从两方面来证明,如果C和C’是图的两个联通分量,且有一条从C到C‘的边,那么C中的所有点的post值的最大值一定回比C’中所有点的post值的最大值要大。因为,如果我们的DFS是从C中开始的话,那么开始的那个点的post值比C‘中所有点的post值都要大;如果是从C’中开始搜索的话,那么先搜索完整个C‘联通分量,然后再搜索C联通分量,这样的话,所有在C中的点的p 大专栏  Algorithms第3章及少量习题ost值比在C’中的点的post值都要大。到此得证。知道这个之后,我们就可以解决问题(I)了,我们可以把有向图反向之后,进行一次DFS,这样的话,post值最大的一定是反向之后的源点,也就是原图的汇点了。对于问题(II)那么我们可以先把找出来的联通分量去掉,然后再在剩余的点中找post值最大的,这个点一定是剩下的图中的汇点,上面的证明可以保证这一点。

所以对于寻找一个图的强联通分量来说,我们只需要对图反向,然后在反向图中进行一次DFS,得到所有点的post值,然后根据post值从大到小的顺序在原图上进行一次DFS,这样就可以得到整个图的所有联通分量了。总时间还是线性的,就相当于2次DFS所用的时间。

接下来是后面几个习题,这几个习题是在这本书的网站上找的几个习题,并不是书中所有的习题。

1.给出一个图,要标出每个点的pre/post值。
这个只需要细心一点就行来

2.对于一个给定的图,用线性的方法把图进行反向。
这个可以循环所有点v,对 in E把v加到u的出边就行了,这样我们只需要循环每个点,每条边1次,所以是线性的

3.证明,无向图中,所有点的度数和是边数的2倍
每条边对两个点共享来度数,总的来说就变成来所有点的度数和是边数的2倍

4.在无向图中,计算每个点的邻接点的数目
这个直接循环所有点就行来,对于边那么u点的邻接点和v点的邻接点都+1就行来。

5.用非递归的方法实现explore

  1. procedure explore(G, u)
  2. S = (empty stack)
  3. push(S, u)
  4. while S is not empty:
  5. v = top(S)
  6. if not visited[v]:
  7. visited[v] = true
  8. previsit(v)
  9. if there is an edge (v, w) in E with visited[w] = false:
  10. push(S, w)
  11. else: (we’re done with v, the node at the top of the stack)
  12. pop(S)
  13. postvisited(v)

6.某学校需要对课表进行安排,某些课必须在另外一些课的前面上。每个学生每学期选多少门课都没关系,问对于给定的所有课之间的关系,同一个人至少需要多少个学期,才能把所有的课修完。
首先我们可以用线性时间计算出每个点的入度,并把入度为0 的点加到队列currL中。然后利用如下过程可以处理剩下的问题,下面的过程中in表示每个点的入度,currL表示当前入度为0的点

  • time = 0

  • repeat until currL is empty:
  • time = time + 1
  • nextL = empty list
  • for all u in currL:
  • for all (u, w) in E
  • in[w] = in[w] - 1
  • if(in[w] is 0)
  • add w to nextL
  • currL = nextL
  • output time
  • 这样,我们得到的还是线性的时间复杂度。
    另外还想了一种为经过验证的方法[私以为是正确的],先对图跑一次DFS,然后把所有点然post值降序排列起来,然后,对排好序的点进行扫描,如果相邻的两个点u,v之间有一条边u->v的话,那么学期数就需要加1[初始化为1],这样的话,最后的数目就是所有的学期数了。

    Algorithms第3章及少量习题的更多相关文章

    1. 《Python核心编程》 第六章 序列 - 课后习题

      课后习题 6–1.字符串.string 模块中是否有一种字符串方法或者函数可以帮我鉴定一下一个字符串是否是另一个大字符串的一部分? 答:成员关系操作符(in.not in) import string ...

    2. 「学习记录」《数值分析》第二章计算实习题(Python语言)

      在假期利用Python完成了<数值分析>第二章的计算实习题,主要实现了牛顿插值法和三次样条插值,给出了自己的实现与调用Python包的实现--现在能搜到的基本上都是MATLAB版,或者是各 ...

    3. 《Python核心编程》 第五章 数字 - 课后习题

      课后习题  5-1 整形. 讲讲 Python 普通整型和长整型的区别. 答:普通整型是绝大多数现代系统都能识别的. Python的长整型类型能表达的数值仅仅与你机器支持的(虚拟)内存大小有关. 5- ...

    4. UVa第五章STL应用 习题((解题报告))具体!

      例题5--9 数据库 Database UVa 1592 #include<iostream> #include<stdio.h> #include<string.h&g ...

    5. C和指针 第十二章 结构体 习题

      12.3 重新编写12.7,使用头和尾指针分别以一个单独的指针传递给函数,而不是作为一个节点的一部分 #include <stdio.h> #include <stdlib.h> ...

    6. APUE第一章_课后习题

      /* 未完成的:1.5 不过在下文中已经给出了解答. */ 1.1 在系统上查证,除根目录外,目录.和..是不同的 ans:这个很容易,用vim打开.和..就可以看到区别. 1.2 分析程序清单1-4 ...

    7. 【原创】《算法导论》链表一章带星习题试解——附C语言实现

      原题: 双向链表中,需要三个基本数据,一个携带具体数据,一个携带指向上一环节的prev指针,一个携带指向下一环节的next指针.请改写双向链表,仅用一个指针np实现双向链表的功能.定义np为next ...

    8. 「学习记录」《数值分析》第三章计算实习题(Python语言)

      第三题暂缺,之后补充. import matplotlib.pyplot as plt import numpy as np import scipy.optimize as so import sy ...

    9. java编程思想第四版第十四章 类型信息习题

      fda dfa 第三题u package net.mindview.typeinfo.test4; import java.util.ArrayList; import java.util.Array ...

    随机推荐

    1. E - Two Arithmetic Progressions(CodeForces - 710D)(拓展中国剩余定理)

      You are given two arithmetic progressions: a1k + b1 and a2l + b2. Find the number of integers x such ...

    2. oracle的用户、权限、表空间的管理

      1.创建表空间 create tablespace test1_tablespace datafile 'test1file.dbf' size 10m; 2.创建临时表空间 create tempo ...

    3. java时区问题设置,new Date()和系统时间相差8个小时

      出现这种问题有可能是服务时间没有修改. import java.text.DateFormat;import java.text.ParseException;import java.text.Sim ...

    4. Filter过滤器的应用

      Filter过滤器作用:在每次请求服务资源时做过滤处理. 原理:Filter接口中有一个doFilter方法,当开发人员编写好Filter类实现doFilter方法,并配置对哪个web资源进行拦截后, ...

    5. TPO5-3 The Cambrian Explosion

      At one time, the animals present in these fossil beds were assigned to various modern animal groups, ...

    6. flash插件的安装——网页视频无法播放

      1.从官网下载Adobe flash player 安装包.官方网址:https://get.adobe.com/cn/flashplayer/ 或者从我的网盘下载:链接:https://pan.ba ...

    7. Mybatis+Druid多数据源配置

      在日常开发中我们可能会用到多数据源开发,什么是多数据源? 简单来讲的话,就是一个项目连接多个数据库.当然只是可能会用到,我暂时没见过应用场景,但是还是了解学习一下 此项目可以基于上一个简单集成项目进行 ...

    8. 定时任务--Timer()实现

      Java的Timer以及TimerTask类可以帮助我们实现定时器功能,利用servlet监听程序可以实现WEB服务启动之后执行某些工作.两者结合就可以再web应用中实现定时器功能. 1.计划类代码S ...

    9. PAT甲级——1061 Dating

      1061 Dating Sherlock Holmes received a note with some strange strings: Let's date! 3485djDkxh4hhGE 2 ...

    10. 002.前端开发知识,前端基础HTML(2020-01-07)

      一.列表标签 1.无序列表 ul <ul> <li>列表项1</li> <li>列表项2</li> <li>列表项3</l ...