第三章的主要思想就是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. JVM内存结构图表展示

      1.理解的JVM内存结构  2.对于垃圾回收问题 垃圾的回收只在堆和永久区(方法区)中,因为对于线程而言,私有存储空间如栈.本地方法区.程序计数器等,会随着方法的加载完成而直接释放空间,因此不需要进行 ...

    2. Java创建文件夹、创建文件、上传文件,下载文件

      1.创建文件夹 /** * 判断文件夹是否存在 * @param myPath */ public static void judeDirExists(File myPath) { if (!myPa ...

    3. 黑马eesy_15 Vue:04.Vue案例(ssm环境搭建)

      黑马eesy_15 Vue:02.常用语法 黑马eesy_15 Vue:03.生命周期 黑马eesy_15 Vue:04.Vue案例(ssm环境搭建) 黑马eesy_15 Vue:04.综合案例(前端 ...

    4. Linux c 操作MySQL

      #include <mysql/mysql.h>#include <stdio.h>#include <stdlib.h>int main() { MYSQL *c ...

    5. XEN 3166

      XEN 3166 这题原题是spj,校oj上只用判断yes no,不过也差不多 题意分析之后就是求两个东西: 字典序最小的长度为m的子序列 同时这个字典序严格大于某个字符串 用序列自动机 先尽量相同, ...

    6. 大道至简伪代码读后感java为代码形式

      //愚公移山 import.java.大道至简.*; import.java.愚公移山.*; public class yishan //定义一个名为yishan的类 {//类定义的开始 public ...

    7. 有关于i++,i=i++等符号的笔记

      最近在看一些基础知识,发现自己以前忽略掉了很多东西,而这些东西恰恰是面试笔试中最常考到的 1.i=i+1 这个是最简单,最明了的一个表达式 2.有关于i++和++i的区别 i++和++i都是代表i=i ...

    8. Excel-DNA项目只用1个文件实现Ribbon CustomUI和CustomTaskpane定制【VB.Net版】

      Excel-DNA项目中的自定义功能区和自定义任务窗格需要用到各种命名空间.添加所需文件,才能实现.后来我发现可以把所有代码都写在Class1.vb这个默认文件中. 大家可以在Visual Studi ...

    9. CLOUD信用管理设置

      1.参数设置(管理员账户) 2.客户管理-信用管理设置 3.信用检查规则设置 4.信用档案设置 5.涉及集团公司,母公司与子公司的设置 6.信用档案-对象类型可为客户及集团客户 7.信用特批权限设置 ...

    10. [LC] 19. Remove Nth Node From End of List

      Given a linked list, remove the n-th node from the end of list and return its head. Example: Given l ...