Página preparada por Paulo
Feofiloff para a disciplina MAC 338 Análise de
Algoritmos, versão 1999.
O endereço original desta página é http://www.ime.usp.br/~pf/mac338/aulas/dijkstra.htm.
Esta página é um resumo do capítulo 25 (somente algoritmo de Dijkstra) de CLR.
Suponha que cada aresta (u,v) de um grafo tem um peso w(u,v). Para qualquer caminho C, vamos denotar por w(C) o peso do caminho, ou seja, a soma dos pesos das arestas que formam o caminho. Dados vértices s e t, um caminho C de s a t tem peso mínimo (ou é w-mínimo) se
w(C) <= w(C')
para qualquer caminho C' de s a t.
PROBLEMA: Dado um grafo, dois vértices s e t, e uma função w que atribui um peso a cada aresta, encontrar um caminho de peso mínimo de s a t.
O problema faz sentido com qualquer peso real (positivo ou negativo), mas fica mais difícil na presença de pesos negativos. Vamos supor, portanto, que
w(u,v) >= 0
para cada aresta (u,v).
Escreva um algoritmo que resolva o seguinte problema:
Encontrar a altura de cada um dos vértices do grafo.
A experiência mostra que não adianta preocupar-se apenas com os caminhos de s a t: é preciso considerar também os caminhos que vão de s aos demais vértices do grafo. Ou seja, é preciso encontrar uma árvore geradora com raiz s que tenha a seguinte propriedade: para cada vértice v, o único caminho de s a v na árvore é um caminho de peso mínimo (dentre os que começam em s e terminam em v).
Eis um algoritmo que resolve o problema quando não há arestas de peso negativo. O algoritmo recebe um grafo com vértices 1, 2, . . . . , n e arestas definidas por listas de adjacência Adj. Recebe também um vértice s e um peso não-negativo w(u,v) para cada aresta (u,v). O algoritmo determina um caminho w-mínimo de s a cada um dos demais vértices; os caminhos são representados por um vetor de predecessores.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
DIJKSTRA (n, Adj, w, s)
para u := 1 até n faça
cor[u] := BRANCA
d[u] := INFINITO
pred[u] := 0
d[s] := 0
Q := CRIE-FILA ()
enquanto Q não está vazia faça
u := RETIRE-MÍNIMO (Q)
cor[u] := NEGRA
para cada v em Adj[u] faça
se cor[v] = BRANCA e d[u]+w(u,v) < d[v] então
d[v] := d[u]+w(u,v)
pred[v] := u
devolva pred
O comando CRIE-FILA () cria uma "fila" com todos os vértices. A prioridade dos vértices na fila é dada pelo vetor d. Os detalhes de implementação da fila ficam por conta do leitor. O comando RETIRE-MÍNIMO (Q) retira de Q um vértice u para o qual d[u] é mínimo. A alteração do valor de d[v] na linha 12 pode afetar a estrutura da fila; isso deve ser levado em conta quando a fila for implementada pra valer.
Para entender por que o algoritmo funciona corretamente, é preciso observar que no início de cada iteração valem as seguintes propriedades:
Resumo de 2,3,4: d[x] é o peso de um caminho de peso mínimo dentre os que não têm vértices BRANCOs exceto talvez o último.
(Eu acho mais fácil entender o algoritmo através dessas propriedades que através da demonstração do nosso guru CLR.)
A operação nas linhas 11-13 é conhecida como relaxação da aresta (u,v). A idéia é óbvia: se existe um caminho de peso d[u] de s até u então também existe um caminho de peso d[u]+w(u,v) de s até v.
DIJKSTRA (n, Adj, w, s) para u := 1 até n faça cor[u] := BRANCA d[u] := INFINITO pred[u] := 0 d[s] := 0 Q := CRIE-FILA () // coloca todos os vértices na fila enquanto Q não está vazia faça u := RETIRE-MÍNIMO (Q) para cada v em Adj[u] faça se cor[v] = BRANCA e d[u]+w(u,v) < d[v] então d[v] := d[u]+w(u,v) pred[v] := u cor[v] := CINZA // atenção! cor[u] := NEGRA devolva pred
DIJKSTRA (n, Adj, w, s) para u := 1 até n faça cor[u] := BRANCA d[u] := INFINITO pred[u] := 0 d[s] := 0 Q := CRIE-FILA (s) // coloca só s na fila enquanto Q não está vazia faça u := RETIRE-MÍNIMO (Q) para cada v em Adj[u] faça se cor[v] = BRANCA e d[u]+w(u,v) < d[v] então d[v] := d[u]+w(u,v) pred[v] := u INSERE-NA-FILA (Q, v) // atenção! cor[u] := NEGRA devolva pred
O comando INSERE-NA-FILA(Q,v)
insere v na fila Q somente se v
ainda não estiver lá.
DIJKSTRA (n, Adj, w, s, t) para u := 1 até n faça d[u] := INFINITO d[s] := 0 Q := CRIE-FILA (s) // coloca todos os vértices na fila enquanto Q não está vazia faça u := RETIRE-MÍNIMO (Q) para cada v em Adj[u] faça se d[u]+w(u,v) < d[v] então d[v] := d[u]+w(u,v) se v = t então // atenção! devolva d[t] // atenção!
Comece considerando a possibilidade de implementar Q em uma fila SEM prioridades. Faça busca linear para achar o mínimo. Suponha matriz de adjacência no lugar de Adj. Temos n iterações. Cada iteração gasta n em retirar o mínimo. Cada vértice sai de Q e fica negro uma só vez. Logo, cada linha da matriz é examinada apenas uma vez. Temos aí n2. Total: n2+n2.
Se usarmos listas de adjacência, teremos O(n2+m).
As cores ajudam a entender o funcionamento do algoritmo mas são supérfluas. O teste da cor de v na linha 11 também é supérfluo (ainda que possa contribuir para fazer o algoritmo um pouquinho mais rápido).
DIJKSTRA-SECO (n, Adj, w, s)
para u := 1 até n faça
d[u] := INFINITO
pred[u] := 0
d[s] := 0
Q := CRIE-FILA ()
enquanto Q não está vazia faça
u := RETIRE-MÍNIMO (Q)
para cada v em Adj[u] faça
se d[u]+w(u,v) < d[v] então
d[v] := d[u]+w(u,v)
pred[v] := u
devolva pred
Mostre que esta versão (sem o teste de cor antes de "d[u]+w(u,v)<d[v]") não produz caminhos de peso mínimo quando há arestas de peso negativo.