Busca em profundidade

Este capítulo discute uma segunda implementação clássica do algoritmo genérico de busca. Nessa implementação, o conjunto de vértices ativos é administrado como uma pilha (= stack): cada iteração escolhe o vértice ativo que foi marcado mais recentemente. O resultado dessa política é conhecido como busca em profundidade (= depth-first searchDFS).

O algoritmo genérico de busca pode então ser reescrito assim:

enquanto a pilha não está vazia faça

seja v o vértice no topo da pilha

se A(v) não está vazio

então retire um arco (v, w) de A(v)

então se w não está marcado

então então marque w

então então coloque w no topo da pilha

senão retire v do topo da pilha

Exercícios 1

  1. A tabela abaixo define um grafo com vértices 1, 2, 3, 4, 5, 6. Uma busca em profundidade é executada a partir do vértice 6 e os vértices são encontrados na ordem 6, 1, 4, 3, 5, 2.

    ponta inicial 6 6 1 2 5 1 4 1
    arco a b c d e f g h
    ponta final 1 4 2 5 3 4 3 5

    Se o grafo estivesse representado na estrutura de dados do SGB, qual seria a ordem dos arcos em cada lista varcs?

Implementação

A função abaixo recebe um grafo g e um vértice r e marca o território de r. A pilha de vértices ativos é

ativo, ativoprox, ativoproxprox, … , r.

O fundo da pilha é sempre r. O conjunto A(v) é representado pelo utility field arcocorrente: para cada vértice v, varcocorrente é o próximo arco, dentre os que saem de v, que deve ser examinado.

#define arcocorrente w.A

#define prox y.V

void busca_em_profundidade (Graph *g, Vertex *r) {

Vertex *v, *w, *ativo;  Arc *a;

for (v = gvertices; v < gvertices + gn; v++) {

vmarca = 0;

varcocorrente = varcs;

}

rmarca = 1;

ativo = rativoprox = NULL;

while (ativoNULL) {

v = ativo;

a = varcocorrente;

if (aNULL) {

w = atip;

varcocorrente = anext;

if (wmarca ≡ 0) {

wmarca = 1;

wprox = ativo/* empilha w */

ativo = w;

}

}

else ativo = ativoprox/* desempilha */

}

}

Implementação com predecessores

A busca em profundidade fica melhor se escrita com predecessores no lugar das marcas. Os predecessores são representados por um campo pred e um vértice v é considerado marcado se vpred for diferente de NULL. Ademais, os próprios predecessores podem representar a pilha de vértices ativos: a pilha é

v, vpred, vpredpred, … , r.

Essa sequência descreve, de trás-pra-diante, um caminho de rv.

#define arcocorrente w.A

#define pred x.V

void busca_em_profundidade (Graph *g, Vertex *r) {

Vertex *v, *wArc *a;

for (v = gvertices; v < gvertices + gn; v++) {

vpred = NULL;

varcocorrente = varcs;

}

v = rpred = r;

while (1) {

a = varcocorrente;

if (aNULL) {  /* avança */

w = atip;

varcocorrente = anext;

if (wpredNULL) {

wpred = v/* empilha w */

v = w;

}

}

else/* volta */

if (vr) break;

v = vpred/* desempilha */

}

}

}

O código fica ligeiramente mais simples se trocarmos o while (1) por while (rarcocorrenteNULL) (isso mesmo: r e não v):

while (rarcocorrenteNULL) {

a = varcocorrente;

if (aNULL) {

w = atip;

varcocorrente = anext;

if (wpredNULL) {

wpred = v;

v = w;

}

}

else v = vpred;

}

Exercícios 2

  1. Mostre que variante de código exibida acima está correta.
  2. Onde está o erro na seguinte tentativa de implementacão da busca em profundidade?

    void errado (Graph *g, Vertex *r) {

    Vertex *v;

    for (v = gvertices; v < gvertices + gn; v++) {

    vpred = NULL;

    varcocorrente = varcs;  }

    v = r;

    while (vNULL) {

    Arc *a = varcocorrente;

    if (aNULL) {

    Vertex *w = atip;

    varcocorrente = anext;

    if (wpredNULL) {

    wpred = v;

    v = w;  }  }

    else v = vpred;  }

    rpred = r;  }

  3. Escreva uma variante da função busca_em_profundidade que receba um vértice r e imprima os nomes de todos os vértices do território de r. O nome de cada vértices deve ser impresso uma e uma só vez.
  4. Escreva uma variante da função busca_em_profundidade que marque os vértices do terrítório de r com 1, 2, 3, etc. à medida que eles forem sendo encontrados.

Versão recursiva

A maneira mais cômoda de escrever o algoritmo de busca em profundidade é recursiva. A pilha de vértices ativos (e portanto o ponteiro arcocorrente) não aparece explicitamente: ela é criada e administrada pelo mecanismo de recursão.

#define pred x.V

void busca_em_profundidade_rec (Graph *g, Vertex *r) {

Vertex *v;

for (v = gvertices; v < gvertices + gn; v++)

vpred = NULL;

rpred = r;

dfs (r);

}

void dfs (Vertex *v) {

Vertex *wArcs *a;

for (a = varcs; aNULL; a = anext) {

w = atip;

if (wpredNULL) {

wpred = v;

dfs (w);

}

}

}