Introdução à Teoria dos Grafos
1. O Mundo Conectado
Um Grafo \(G(V, E)\) é uma estrutura matemática composta por: - \(V\): Um conjunto de Vértices (ou nós). - \(E\): Um conjunto de Arestas (ou arcos) que conectam pares de vértices.
Grafos modelam relacionamentos: - Redes Sociais: Vértices = Pessoas, Arestas = Amizades. - Mapas: Vértices = Cidades, Arestas = Estradas. - Web: Vértices = Páginas, Arestas = Hyperlinks.
Terminologia Essencial
- Grau (Degree): Número de arestas conectadas a um vértice.
- Grafo Direcionado (Digrafo): Arestas têm sentido (\(A \rightarrow B\)). Ex: Twitter (Seguir).
- Grafo Não-Direcionado: O sentido é bidirecional (\(A - B\)). Ex: Facebook (Amizade).
- Ponderado (Weighted): Arestas têm um “peso” ou custo. Ex: Distância em km.
- Caminho (Path): Sequência de vértices conectados por arestas.
- Ciclo: Caminho que começa e termina no mesmo vértice.
2. Como representar no computador?
A escolha da representação afeta drasticamente a complexidade de tempo e memória. Seja \(V\) o número de vértices e \(E\) o número de arestas.
2.1. Matriz de Adjacência
Uma matriz \(M\) de tamanho \(V \times V\), onde: - \(M[i][j] = 1\) (ou peso) se existe aresta de \(i\) para \(j\). - \(M[i][j] = 0\) (ou \(\infty\)) se não existe.
| Vantagens | Desvantagens |
|---|---|
| Verificar se \(u\) conecta com \(v\) em \(O(1)\). | Gasta muita memória \(O(V^2)\). |
| Simples de implementar. | Iterar sobre vizinhos é lento \(O(V)\). |
Uso: Apenas para grafos pequenos (\(V \le 2000\)) ou muito densos (\(E \approx V^2\)).
2.2. Lista de Adjacência (Padrão Ouro)
Um vetor de listas. Para cada vértice \(u\), armazenamos uma lista com seus vizinhos.
| Vantagens | Desvantagens |
|---|---|
| Memória eficiente \(O(V + E)\). | Verificar aresta \((u,v)\) é \(O(Grau(u))\). |
| Iterar vizinhos é rápido \(O(Grau(u))\). | Leve overhead de ponteiros. |
Uso: Padrão para 99% dos problemas de competições e vida real (grafos esparsos).
3. Implementação em Java
Em C++, usamos vector<int> adj[]. Em Java, temos um pequeno problema: arrays de genéricos (ArrayList<Integer>[]) causam warnings. Soluções comuns: 1. ArrayList<ArrayList<Integer>> (Mais OO, levemente mais lento e verboso). 2. List<Integer>[] com casting (Mais compacto).
Modelo Padrão (Adjacency List)
import java.util.*;
public class Grafo {
// Vértices rotulados de 0 a V-1
private int V;
private List<List<Integer>> adj;
public Grafo(int V) {
this.V = V;
adj = new ArrayList<>(V);
// Inicializa as listas vazias
for (int i = 0; i < V; i++) {
adj.add(new ArrayList<>());
}
}
// Adiciona aresta não-direcionada
public void addEdge(int u, int v) {
adj.get(u).add(v);
adj.get(v).add(u); // Remova esta linha se for Direcionado
}
// Exibe o grafo
public void printGraph() {
for (int i = 0; i < V; i++) {
System.out.print("Vértice " + i + ":");
for (Integer vizinho : adj.get(i)) {
System.out.print(" -> " + vizinho);
}
System.out.println();
}
}
public static void main(String[] args) {
Grafo g = new Grafo(4);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 2);
g.addEdge(2, 3);
g.printGraph();
}
}Grafo Ponderado (Com Pesos)
Se o grafo tem pesos, a lista de adjacência precisa armazenar pares (vizinho, peso). Em Java, criamos uma classe Edge.
class Aresta {
int destino;
int peso;
public Aresta(int d, int p) {
this.destino = d;
this.peso = p;
}
}
// Na classe Grafo:
private List<List<Aresta>> adj;4. Edge List (Lista de Arestas)
Às vezes (ex: Kruskal para MST, Bellman-Ford), não precisamos saber quem são os vizinhos de \(u\). Só precisamos da lista de todas as arestas.
class Edge implements Comparable<Edge> {
int u, v, weight;
// construtor...
public int compareTo(Edge other) {
return this.weight - other.weight;
}
}
List<Edge> arestas = new ArrayList<>();Isso simplifica a ordenação de arestas por peso.
5. Dicas de Entrada (Input)
Em problemas de juiz online, a entrada geralmente é dada assim:
5 6 (5 vértices, 6 arestas)
0 1
0 2
1 3
...
Template de Leitura:
Scanner sc = new Scanner(System.in);
int V = sc.nextInt();
int E = sc.nextInt();
// Inicializa List<List<Integer>>...
for(int i=0; i<E; i++) {
int u = sc.nextInt(); // Se vértices forem 1-based, use (u-1)
int v = sc.nextInt();
addToGraph(u, v);
}Atenção: Se os vértices forem rotulados de 1 a N, lembre-se de subtrair 1 para usar índices 0 a N-1 nos Lists.