22.2-1 请计算出在有向图22-2(a)上运行广度优先搜索算法后的d值和Π值。这里假定结点3为算法所用的源结点。
邻接矩阵为:
1 2 3 4 5 6
1 0 1 0 1 0 0
2 0 0 0 0 1 0
3 0 0 0 0 1 1
4 0 1 0 0 0 0
5 0 0 0 1 0 0
6 0 0 0 0 0 1
结点 | d | Π |
1 | ∞ | NIL |
2 | 3 | 4 |
3 | 0 | NIL |
4 | 2 | 5 |
5 | 1 | 3 |
6 | 1 | 3 |
22.2-2 请计算出在图22-3所示无向图上运行广度优先搜索算法后的d值和Π值。这里假定结点u为算法所用的源结点。
邻接矩阵为:
u t y x w s r v
u 0 1 1 1 0 0 0 0
t 1 0 0 1 1 0 0 0
y 1 0 1 1 0 0 0 0
x 1 1 1 0 1 0 0 0
w 0 1 0 1 0 1 0 0
s 0 0 0 0 1 0 1 0
r 0 0 0 0 0 1 0 1
v 0 0 0 0 0 0 1 0
结点 | d | Π |
u | 0 | NIL |
t | 1 | u |
y | 1 | u |
x | 1 | u |
w | 2 | t |
s | 3 | w |
r | 4 | s |
v | 5 | r |
22.2-3 证明:使用单个位来存放每个结点的颜色即可。这个论点可以通过证明将算法第18行的伪代码删除后,BFS过程生成的结果不变来得到。
BFS(G, s)
1 for each vertex u ∈ G.V - {s}
2 u.color = WHITE
3 u.d = ∞
4 u.Π = NIL
5 s.color = GREY
6 s.d = 0
7 s.Π = NIL
8 Q = empty
9 ENQUEUE(Q, s)
10 while Q != empty
11 u = DEQUEUE(Q)
12 for each v ∈ G.Adj[u]
13 if v.color == WHITE
14 v.color = GRAY
15 v.d = u.d + 1
16 v.Π = u
17 ENQUEUE(Q, v)
18 u.color = BLACK
为了证明使用单个位来存储每个节点的颜色足够,我们需要证明从BFS算法中删除第18行伪代码(u.color = BLACK)后,BFS过程生成的结果不会改变。
首先,让我们了解第18行的目的。这一行将当前节点\(u\)的颜色设置为BLACK,表示它的所有邻居已经被探索过。通常这样做是为了防止在BFS遍历过程中重新访问相同的节点。
现在,让我们考虑移除第18行的影响。没有这一行,节点将在其所有邻居被访问后仍然保持GRAY状态。这可能导致多次重新访问相同的节点。
关键观察是,在典型的BFS中,一旦一个节点被出队,它就被完全探索,它的所有邻居都已经被添加到队列中。因此,在后续步骤中,节点的颜色变得无关紧要,因为算法不会再次处理它。
为了正式化证明,我们可以论证节点颜色仅用于管理队列并防止重新访问相同的节点。如果我们能够证明删除BLACK颜色赋值不会影响算法的正确性,那么使用单个位来表示节点颜色的确足够。
证明:
初始时,所有节点都被设置为WHITE。
当一个节点被出队(第11行),它被完全探索,它的所有邻居都已经被添加到队列中。
出队后,节点的颜色对后续步骤变得无关紧要。
由于队列和WHITE/GRAY颜色方案的特性,算法只处理每个节点一次且仅一次。
因此,删除BLACK颜色赋值不会改变BFS算法的正确性。这支持了使用单个位来存储每个节点颜色(WHITE/GRAY)足够用于BFS的论点。
22.2-4 如果将输入的图用邻接矩阵来表示,并修改算法来应对此种形式的输入,请问BFS的运行时间将是多少?
BFS的运行时间取决于图的表示方式以及算法的实现。对于邻接矩阵表示法,其中|V|是节点数,|E|是边数,BFS的运行时间是 O(|V|^2)。
在邻接矩阵中,每个节点对应矩阵的一行和一列。BFS算法需要检查每个节点和其邻居的关系,这涉及到访问整个邻接矩阵。对于每个节点,算法需要检查其所有邻居,这在最坏情况下可能需要 O(|V|) 的时间。因此,总的运行时间是 O(|V|^2)。
在修改BFS算法以适应邻接矩阵输入时,主要的变化在于访问邻居的方式。对于邻接链表,我们可以直接迭代节点的邻居。而在邻接矩阵中,我们需要检查与节点相连的所有列。
22.2-5 证明:在广度优先搜索算法里,赋给结点u的u.d值与结点在邻接链表里出现的次序无关。使用图22-3作为例子,证明:BFS所计算出的广度优先树可以因邻接链表中的次序不同而不同。
邻接矩阵为:
u t y x w s r v
u 0 1 1 1 0 0 0 0
t 1 0 0 1 1 0 0 0
y 1 0 1 1 0 0 0 0
x 1 1 1 0 1 0 0 0
w 0 1 0 1 0 1 0 0
s 0 0 0 0 1 0 1 0
r 0 0 0 0 0 1 0 1
v 0 0 0 0 0 0 1 0
BFS算法的核心思想是按层次遍历图,因此无论结点在邻接链表中的次序如何,BFS都会按照层次逐步更新 u.d 值。这是因为队列的FIFO性质确保了相同层次结点的 u.d 值相同,不受邻接链表中次序的影响。
为了证明BFS所计算出的广度优先树可以因邻接链表中的次序不同而不同,我们可以通过具体的例子来展示。
考虑下面的图的邻接链表表示:
A: B C
B: A D E
C: A F
D: B
E: B
F: C
首先,我们执行BFS,并按照结点的访问次序构建广度优先树。假设初始结点是A。这里,我们使用BFS的过程,但不考虑具体的 u.d 值,只关注结点的访问顺序。
BFS过程:
1. A (初始结点)
2. B, C (A的邻居)
3. D, E, F (B和C的邻居)
得到的广度优先树为:
A
| \
B C
| \
D F
\
E
现在,我们改变图的邻接链表中结点的次序,仅交换B和C的位置:
A: C B
C: A F
B: A D E
F: C
D: B
E: B
执行相同的BFS过程,我们得到的广度优先树为:
A
| \
C B
| \
F D
|
E
可以看到,尽管图的拓扑结构没有改变,但由于邻接链表中结点的排列次序不同,BFS计算出的广度优先树的形状也不同。
这就证明了BFS所计算出的广度优先树可以因邻接链表中的次序不同而不同。这是因为BFS的结果受到邻接链表中结点处理的顺序影响,而不仅仅取决于图的拓扑结构。
22.2-6 举出一个有向图G = (V, E)的例子,对于源结点s∈V和一组树边E’包含于E,使得对于每个结点v∈V,图(V, E’)中从源结点s到结点v的唯一简单路径也是图G中的一条最短路径,但是,不管邻接链表里结点之间的次序如何,边集E’都不能通过在图G上运行BFS来获得。
证明:
1. 存在一组树边E',使得对于每个结点v,图G(V, E')中从源结点s到结点v的唯一简单路径也是图G中的一条最短路径。
构建 E' 集合:选择一种方式来构建 E' 集合,使得从源结点 s 到每个结点 v 的唯一简单路径都包含在 E' 中。
证明 E' 中的路径是最短路径:证明对于每个结点 v,从源结点 s 到 v 的唯一简单路径也是图 G 中的一条最短路径。反证法: 假设存在一个结点 v,从源结点 s 到 v 的唯一简单路径不是图 G 中的最短路径。由于图中的边权重都是非负的,我们可以通过替换路径中的边来得到一条更短的路径。但这与 E' 中路径的唯一性矛盾,因为它们被认为是唯一简单路径。
因此,通过构建 E' 集合并证明其路径是最短路径,我们可以得出存在一组树边 E',使得对于每个结点 v,图 G(V, E') 中从源结点 s 到结点 v 的唯一简单路径也是图 G 中的一条最短路径。
2. 无论如何排列邻接链表的顺序,边集E'都不能通过在图G上运行BFS来获得。
假设可以通过某种邻接链表的排列顺序获得边集E'。考虑图G中的两个相邻结点u和v,且存在一条边e连接它们。由于图G是无向图,u和v的邻接链表中都包含边e。
如果BFS首先探索了u的邻接链表,那么e将成为BFS树的一部分。但由于图G中的路径是无向的,e也将包含在v的邻接链表中。如果BFS现在转向v的邻接链表,它将遇到e,但由于e已经在BFS树中,BFS将不再探索e。
反之亦然,如果BFS首先探索了v的邻接链表,同样的逻辑也适用。这就导致BFS不能同时包含u和v之间的边e,而这与假设的边集\(E'\)中包含从源结点s到每个结点v的唯一简单路径矛盾。
因此,无论如何排列邻接链表的顺序,BFS都无法获得包含在假设的边集\(E'\)中的所有边,从而证明了这一点。
综上所述,这证明了存在一组树边E',使得对于每个结点v,图G(V, E')中从源结点s到结点v的唯一简单路径也是图G中的一条最短路径,但无论如何排列邻接链表的顺序,边集\(E'\)都不能通过在图G上运行BFS来获得。