DFS: Depth-First Search (pesquisa em profundidade) Percursos e Conectividade em Grafos Depth-First Search Fernando Lobo Algoritmos e Estrutura de Dados II Assim que se descobre um nó, começa-se logo a explorar os descendentes desse nó. Quando não é possível explorar mais, volta-se para trás (backtracking). Ao contrário de BFS, a pesquisa pode recomeçar de novo. Isto é, todos os nós do grafo são explorados, mesmo que o grafo seja desconexo. Em vez de uma BFS Tree, produz uma DFS Forest (várias árvores, uma para cada componente conexa). 1 / 23 2 / 23 Implementação de DFS Implementação de DFS Usa-se um stack em vez de uma fila (e até podemos nem usar o stack, se implementarmos o algoritmo de forma recursiva). Mantemos dois timestamps em cada nó: v.d discovery time, instante em que v é descoberto. v.f finish time, instante em que todos os descendentes de v já foram descobertos. Estes timestamps são inteiros distintos entre 1 e 2 V v.d < v.f, v Também usamos um atributo color para cada nó: white não descoberto. gray descoberto mas ainda não terminado (ainda há descendentes por explorar). black terminado. Para um determinado nó v: v é white entre os instantes de tempo 1 e v.d v é gray entre v.d e v.f v é black entre v.f e 2 V 3 / 23 4 / 23
Pseudocódigo Pseudocódigo DFS(G) for each u G.V u.color = white u.π = nil time = 0 for each u G.V if u.color == white DFS-Visit(u) DFS-Visit(u) u.color = gray // u acaba de ser descoberto time = time + 1 u.d = time for each v G.Adj[u] // explora o arco (u, v) if v.color == white v.π = u DFS-Visit(v) / terminou de explorar os descendentes de u u.color = black time = time + 1 u.f = time 5 / 23 6 / 23 Complexidade de DFS Exemplo Complexidade de DFS = Θ(V + E). Θ e não apenas O como em BFS, porque aqui todos os nós e todos os arcos são examinados. Θ(V ) da inicialização. Θ(E) porque para cada nó u, são examinados Adj[u] arcos. v V Adj[v] = Θ(E) Notação na figura: d/f discovery time / finish time. 7 / 23 8 / 23
Classificação de arcos num grafo Exemplo (retirado de CLRS) Tree edge: Se pertencer à DFS Forest. (u, v) se v é descoberto ao explorar (u, v). Back edge: (u, v) em que v é antecessor de u. Forward edge: (u, v) em que v é descendente de u, e não é um Tree edge. Cross edge: todos os outros arcos do grafo. Se o grafo for não orientado, apenas existem Tree edges e Back edges. Notação: B back edge, F forward edge, C cross edge. 9 / 23 10 / 23 Grafo redesenhado (retirado de CLRS) Teorema dos parêntesis (ver demonstração no livro) Dados dois nós, u e v, uma das três coisas ocorre: 1. u.d < u.f < v.d < v.f ou v.d < v.f < u.d < u.f (u não é descendente de v, v não é descendente de u). 2. u.d < v.d < v.f < u.f (v é descendente de u). 3. v.d < u.d < u.f < v.f (u é descendente de v). Tree edges e forward edges de cima para baixo. Back edges apontam para antecessores. 11 / 23 12 / 23
Teorema dos parêntesis Ilustração do teorema dos parêntesis Designando u.d ( u.f ) v.d [ v.f ] Caso 1: ( ) [ ] ou [ ] ( ) Caso 2: ( [ ] ) Caso 3: [ ( ) ] Coisas do estilo ( [ ) ] ou [ ( ] ) nunca acontecem em DFS. Este exemplo é com o mesmo grafo de há pouco (retirado de CLRS). 13 / 23 14 / 23 Teorema do Caminho Branco (White-Path Theorem) Grafos orientados sem ciclos v é descendente de u, se e só se, no instante u.d, existe um caminho u v constituído apenas por nós white (com excepção de u que acaba de ficar gray). Em Inglês, Directed Acyclic Graphs (DAGs). São normalmente usados para expressar dependências. 15 / 23 16 / 23
Exemplo: precedências de disciplinas DAGs - Ordenação topológica Aplica-se a DAGs É uma ordem linear dos nós de um DAG de tal forma que se (u, v) E, então u aparece antes de v. Topological-Sort(G) 1 Chamar DFS(G) para obter v.f, v 2 Assim que cada nó termina, insere-o na cabeça de uma lista. 3 retorna a lista de nós. Nota: Os passos 2 e 3 dão os nós do grafo por ordem decrescente de finish time. 17 / 23 18 / 23 Complexidade? Exemplo com DFS a começar em AED-I Θ(V + E) Idêntico à complexidade de DFS. Inserir na lista é Θ(1). O.T. = SD, PI, MD, ArqC, AED-I, SO, AED-II, Comp. 19 / 23 20 / 23
Redesenhando o DAG Lemma Um grafo orientado G não tem ciclos se e só se o algoritmo DFS não der origem a back edges. Ver demonstração no vosso livro. Grafo pode ser redesenhado com os arcos a irem todos da esquerda para a direira. 21 / 23 22 / 23 Correcção do algoritmo Seja G = (V, E) um DAG. Teremos de mostrar que após correr DFS em G, v.f < u.f, (u,v) E Quando (u, v) é explorado em DFS, quais as cores de u e v? u é gray. v é gray? Não. Caso contrário v era antecessor de u, o que implicaria que (u, v) daria origem a um ciclo, contrariando o facto de G ser um DAG. v é white? Se sim, então v é descendente de u, o que implica que v.f < u.f v é black? Se sim, então v já terminou. Como estamos a explorar (u, v), ainda não terminamos u. Logo, v.f < u.f 23 / 23