Algoritmo de Dijkstra (Aulas 20 e 21)
Parte 1: A Mecânica do Algoritmo (Aula 20)
1. O GPS dos Grafos
Se você abre o Google Maps e pede uma rota, ele usa uma variante do algoritmo de Edsger W. Dijkstra (1956). Diferente da BFS (que só serve para arestas de peso 1), o Dijkstra encontra o Caminho Mínimo em grafos onde as arestas têm pesos variados não-negativos.
Analogia da Água: Imagine que a água começa a fluir da origem. Ela percorre canos (arestas) de comprimentos diferentes. O algoritmo determina em que “momento” a água chega em cada cidade.
Restrição de Ouro: Funciona APENAS para grafos onde todos os pesos são não-negativos (\(w \ge 0\)).
Por que não usar BFS?
A Busca em Largura garante o menor número de arestas, mas ignora completamente os pesos. Se uma aresta direta custa 100 e um caminho alternativo por 2 arestas custa 25, a BFS escolheria o caminho direto (1 aresta) em vez do caminho mais barato (2 arestas, custo 25).
2. Conceito de Relaxamento
A base de todos os algoritmos de menor caminho é a operação de Relaxamento (Relaxation). Seja D[v] a melhor distância conhecida até agora da origem até v.
Imagine que conhecemos um caminho para \(v\) que custa \(10\). Descobrimos uma aresta \(u \to v\) com peso \(2\), e sabemos que chegamos em \(u\) com custo \(5\). Novo custo potencial via \(u\): \(D[u] + peso(u, v) = 5 + 2 = 7\). Como \(7 < 10\), nós relaxamos a aresta: atualizamos D[v] = 7 e dizemos que o pai de \(v\) agora é \(u\).
A Fórmula do Relaxamento
if (dist[u] + w(u,v) < dist[v])
dist[v] = dist[u] + w(u,v)
pai[v] = u
3. O Algoritmo Guloso
- Inicialização:
dist[S] = 0, todos os outrosdist[v] = ∞. - Priority Queue: Coloque par
(0, S)na fila. - Enquanto a PQ não estiver vazia:
- Extraia o vértice \(u\) com a menor distância atual.
- Se essa distância for maior que
dist[u](info obsoleta), ignore (Lazy Deletion). - Para cada vizinho \(v\) de \(u\), tente relaxar a aresta \((u,v)\).
- Se relaxar, empurre
(novo_dist, v)para a PQ.
Por que Guloso?
O algoritmo assume que, se extraímos \(u\) da fila com distância \(X\), não existe nenhum outro caminho no mundo que chegue em \(u\) com custo menor que \(X\). Isso é verdade porque arestas negativas não existem — qualquer caminho alternativo passando por outros nós da fila só pode ter custo \(\ge X\).
4. Trace Animado (Exemplo Passo a Passo)
Considere o grafo com 4 vértices e origem = 0:
0 --(4)--> 1
| |
(1) (1)
| |
v v
2 --(2)--> 1
2 --(5)--> 3
Arestas: 0→1 (peso 4), 0→2 (peso 1), 2→1 (peso 2), 1→3 (peso 1), 2→3 (peso 5).
| Nó Extraído | Vizinhos (Relaxamento) | Fila (PQ) |
|---|---|---|
| — | — | (0, custo:0) |
| Visita 0 | 1 (custo:4), 2 (custo:1) | (2, c:1), (1, c:4) |
| Visita 2 | 1 (1+2=3), 3 (1+5=6) | (1, c:3), (1, c:4), (3, c:6) |
| Visita 1 | 3 (3+1=4) | (1, c:4), (3, c:4), (3, c:6) |
| Ignora 1! | (já dist[1]=3 < 4) | (3, c:4), (3, c:6) |
| Visita 3 | nenhum | (3, c:6) |
| Ignora 3! | (já dist[3]=4 < 6) | vazia |
Resultado Final: dist[0]=0, dist[1]=3, dist[2]=1, dist[3]=4
5. A Árvore de Caminhos Mínimos
Ao final da execução, se seguirmos de trás para frente usando o vetor de pais, formamos a Árvore de Caminhos Mínimos (Shortest Path Tree).
Para reconstruir o caminho até o vértice 3: \(3 \leftarrow 1 \leftarrow 2 \leftarrow 0\). Logo, a rota é \(0 \rightarrow 2 \rightarrow 1 \rightarrow 3\) com custo total 4.
Parte 2: Implementação e Casos Especiais (Aula 21)
6. Implementação em Java
Atenção: A PriorityQueue do Java guarda objetos. Devemos criar uma classe ou usar array.
6.1 Estrutura Node
import java.util.*;
class Node implements Comparable<Node> {
int id;
int distance;
public Node(int id, int dist) {
this.id = id;
this.distance = dist;
}
@Override
public int compareTo(Node other) {
return Integer.compare(this.distance, other.distance); // Min-Heap
}
}6.2 O Algoritmo
public static int[] dijkstra(int V, List<List<Edge>> adj, int startNode) {
int[] dist = new int[V];
Arrays.fill(dist, Integer.MAX_VALUE); // Tudo infinito no início
dist[startNode] = 0;
PriorityQueue<Node> pq = new PriorityQueue<>();
pq.add(new Node(startNode, 0));
while (!pq.isEmpty()) {
Node current = pq.poll();
int u = current.id;
// Lazy Deletion: Se já achamos um caminho melhor pra `u` e
// este nó na fila ficou obsoleto, apenas ignore.
if (current.distance > dist[u]) continue;
// Relaxa vizinhos
for (Edge e : adj.get(u)) {
if (dist[u] + e.peso < dist[e.destino]) {
dist[e.destino] = dist[u] + e.peso;
// Adiciona o novo caminho relaxado na fila
pq.add(new Node(e.destino, dist[e.destino]));
}
}
}
return dist;
}6.3 Reconstrução do Caminho
Para reconstruir o caminho, basta manter um array pai[] e atualizá-lo durante o relaxamento:
// Durante o relaxamento:
if (dist[u] + e.peso < dist[e.destino]) {
dist[e.destino] = dist[u] + e.peso;
pai[e.destino] = u; // Lembra de onde veio
pq.add(new Node(e.destino, dist[e.destino]));
}
// Para reconstruir o caminho até o destino:
List<Integer> path = new ArrayList<>();
for (int v = destino; v != -1; v = pai[v]) {
path.add(v);
}
Collections.reverse(path);7. O Calcanhar de Aquiles: Pesos Negativos
O que acontece se uma aresta tiver custo \(-10\)?
A Quebra do Paradigma Guloso
O Dijkstra sela a distância de um vértice quando o tira da fila, assumindo que nenhum caminho futuro será mais barato. Arestas negativas permitem “voltar no tempo” e criar um atalho milagroso, burlando a premissa fundamental do algoritmo e gerando respostas erradas.
Ciclo Negativo
Pior ainda: se as arestas formarem um ciclo com soma total negativa, o algoritmo vai girar nele reduzindo o custo infinitamente rumo ao \(-\infty\).
A Solução Definitiva
Para grafos com pesos negativos ou para detectar ciclos negativos de forma segura, usamos outro algoritmo clássico: Bellman-Ford com complexidade \(O(V \times E)\).
8. Complexidade Computacional
| Implementação | Complexidade |
|---|---|
| Com Binary Heap (PriorityQueue Java) | \(O((V + E) \log V) \approx O(E \log V)\) |
| Sem Heap (Busca Linear / Matriz) | \(O(V^2)\) |
| Com Fibonacci Heap | \(O(E + V \log V)\) (teórico) |
- A versão com Binary Heap é perfeita para quase qualquer cenário real.
- A versão sem Heap (\(O(V^2)\)) é surpreendentemente mais rápida apenas se o grafo for extremamente denso (\(E \approx V^2\)).
- A Fibonacci Heap tem o limite teórico ótimo, mas constante muito alta na prática.
9. Aplicações no Mundo Real
- Protocolos de Roteamento de Internet: OSPF e IS-IS calculam a árvore de rotas mais curtas nas entranhas da Internet usando o Dijkstra.
- Pathfinding em Mapas e Games: O motor de rotas do Google Maps e movimentos de I.A. em jogos usam o A* (A-Star), que é matematicamente um Dijkstra guiado por uma Heurística.
- Logística e Transporte: Otimização de rotas de entrega e distribuição.
- Telecomunicações: Roteamento de chamadas em redes telefônicas.
10. Exercícios
10.1. Exercícios Conceituais
- Por que Dijkstra não funciona com arestas de peso negativo? Dê um contraexemplo.
- Explique o conceito de “lazy deletion” na implementação de Dijkstra com Priority Queue. Por que é necessário?
- Compare Dijkstra com BFS. Quando BFS é suficiente e quando precisamos de Dijkstra?
10.2. Exercícios Analíticos
Simule Dijkstra passo a passo no grafo:
0 --(1)--> 1 --(3)--> 3 | | (4) (2) | | v v 2 --(1)--> 4A partir do vértice 0, mostre a fila de prioridade e distâncias em cada iteração.
Para um grafo completo \(K_n\) com pesos unitários, compare o número de operações de Dijkstra vs BFS. Qual é mais eficiente?
Analise a complexidade de Dijkstra quando:
- Usamos Binary Heap (PriorityQueue padrão).
- Usamos array simples para a fila (sem heap).
Quando cada implementação é preferível?
10.3. Exercícios de Programação
- Implemente Dijkstra completo com:
- Cálculo de distâncias mínimas.
- Reconstrução de caminhos usando array de pais.
- Suporte a grafos direcionados e não direcionados.
- Compare o desempenho de Dijkstra usando Binary Heap vs array simples em grafos densos e esparsos.
- Resolva problemas de juiz online relacionados a caminhos mínimos, como:
- Caminho mínimo entre dois vértices específicos.
- Caminhos mínimos de um vértice para todos os outros.
- Problemas de roteamento em redes.
![Relaxamento: se dist[u] + peso(u,v) < dist[v], atualizar dist[v] e pai[v].](figuras/fig-relaxamento.jpg)

