算法导论22.3深度优先搜索 练习总结 (转载)
22.3-1 画一个 3*3 的网格,行和列的抬头分别标记为白色、灰色和黑色,对于每个表单元 (i, j),请指出对有向图进行深度优先搜索的过程中,是否可能存在一条边,链接一个颜色为 i 的结点和一个颜色为 j 的结点。对于每种可能的边,指明该种边的类型。另外,请针对无向图的深度优先搜索再制作一张这样的网格。
ANSWER:
22.3-2 给出深度优先搜索算法在图 22-6 上的运行过程。假定深度优先搜索算法的第 5~7 行的 for 循环是以字母表顺序依次处理每个结点,假定每条邻接链表皆以字母表顺序对立面的结点进行了排序。请给出每个结点的发信啊时间和完成时间,并给出每条边的分类。
ANSWER:
22.3-3 给出图 22-4 的深度优先搜索的括号化结构。
ANSWER:
22.3-4 证明:使用单个位来存放每个结点的颜色已经足够。这一点可以通过证明如下事实来得到:如果将 DFS-VISIT 的第 8 行删除,DFS 给出的结果相同。
ANSWER:和 22.2-3 类似,事实上,在 DFS-VISIT 中,在递归过程中,系统会调用栈存放未被检测的结点,并不一定需要将结点着黑色和灰色来区别,因为程序中并没有检测结点是否黑色的命令。着黑白灰是为了易于观察。
22.3-5 证明边 (u, v) 是:
a. 树边或前向边当且仅当 u.d < v.d < v.f < u.f。
b. 后向边当且仅当 v.d ≤ u.d < u.f ≤ v.f。
c. 横向边当且仅当 v.d < v.f < u.d < u.f。
ANSWER:由于深度优先搜索满足括号化结构,将结点的关系以括号化结构表示易证明此关系。
22.3-6 证明:在无向图中,根据深度优先搜索算法是先搜索 (u, v) 还是先搜索 (v, u) 来将边 (u, v) 分类为树边或者后向边,与根据分类列表中的 4 种类型的次序进行分类是等价的。
ANSWER:有向图是将无向图的一条边变成有方向的两条边。所以本质上分类标准是等价的。
22.3-7 请重写 DFS 算法的伪代码,以便使用栈来消除递归调用。
ANSWER:
DFS-VISIT: STACK.push(u) while ! STACK.empty u = STACK.top if u.color == GRAY u.color = BLACK time = time + 1 u.f = time STACK.pop continue if u.color == WHITE u.color = GRAY time = time + 1 u.d = time for each v ∈ G:Adj[u] v.π = u STACK.push(u)
22.3-8 请给出如下猜想的一个反例:如果有向图 G 包含一条从结点 u 到结点 v 的路径,并且在对图 G 进行深度优先搜索时有 u.d < v.d,则结点 v 是结点 u 在深度优先森林中的一个后代。
ANSWER:
22.3-9 请给出如下猜想的一个反例:如果有向图 G 包含一条从结点 u 到结点 v 的路径,则任何对图 G 的深度优先搜索都将导致 v.d ≤ u.f。
ANSWER:和 22.3-8 反例的图一样。
22.3-10 修改深度优先搜索的伪代码,让其打印出有向图 G 的每条边及其分类。并指出,如果图 G 是无向图, 要进行何种修改才能达到相同的效果。
ANSWER:在完成 DFS 之后对有向图 G 的边进行遍历,按照 22.3-5的结论的规则。
CLASSIFY-EDGE(G): for each vertex u ∈ G.V for each v ∈ G:Adj[u] if v.π == u: edge(u, v).c = T else if u.d < v.d < v.f < u.f : edge(u, v).c = F else if v.d ≤ u.d < u.f ≤ v.f: edge(u, v).c = B else: edge(u, v).c = C
无向图:只需要把三个 else 改成一个 else,分类为后向边即可。
22.3-11 请解释有向图的一个结点 u 怎样才能成为深度优先树中的唯一结点,即使结点 u 同时有入边和出边。
ANSWER:
22.3-12 证明:我们可以在无向图 F 上使用深度优先搜索来获得图 G 的连通分量,并且深度优先森林所包含的数的棵数与 G 的连通分量数量相同。更准确地说,请给出如何修改深度优先搜索来让其每个结点赋予一个介于 1 和 k 之间的整数值 v.cc,这里 k 是 G 的连通分量数,使得 u.cc = v.cc 当且仅当结点 u 和结点 v 处于同一个连通分量中。
ANSWER:当执行 DFS 的第 5、6 行时,每跳入一次第 7 行,连通分量数加 1,在 DFS-VISIT 里遇到的结点的联通分量数相同。
联通分支的数量用ceil表示,代码修改如下
void Link_Graph::DFS() { int u, ceil = 0; //对每个顶点初始化 for(u = 1; u <= n; u++) { V[u].color = WHITE; V[u].p = NULL; } //时间戳初始化 time = 0; //依次检索V中的顶点,发现白色顶点时,调用DFS_Visit访问该顶点 for(u = 1; u <= n; u++) { if(V[u].color == WHITE) { ceil++; DFS_Visit(u, ceil); } } } void Link_Graph::DFS_Visit(int u, int ceil) { int v; Edge *e; //将u置为灰色 V[u].color = GRAY; //使全局变量time增值 time++; //将time的新值记录为发现时间 V[u].d = time; e = V[u].head; while(e) { v = e->end; //如果顶点为白色 if(V[v].color == WHITE) { //递归访问顶点 V[v].p = u; DFS_Visit(v, ceil); //树边 e->type = TREE; } else if(V[v].color == GRAY) { //反向边 e->type = BACK; } else if(V[v].color == BLACK) { //正向边 if(V[u].d < V[v].d) e->type = FORWARD; //交叉边 else e->type = CROSS; } e = e->next; } //以u为起点的所有边都被探寻后,置u为黑色 V[u].color = BLACK; V[u].ceil = ceil; //并将完成时间记录在f[u]中 time++; V[u].f = time; }
*22.3-13 对于有向图 G = (V, E) 来说,如果 u ~ v 以为这图 G 至多包含一条从 u 到 v 的简单路径,则图 G 是单连通图。请给出一个有效算法来判断一个有向图是否单连通图。
ANSWER:
单连通图没有正向边(向前边)