Algoritmos e estruturas de dados · Técnicas · 7 min de leitura
Recursão e programação dinâmica
Resolver um problema a partir de versões menores dele — e, quando os subproblemas se repetem, guardar as respostas. Do Fibonacci exponencial ao linear, e o troco com menos moedas.
Arraste para o lado para ver o diagrama inteiro.
Recursão
Uma função recursiva tem um caso base (a resposta direta, como fib(0) = 0 e fib(1) = 1) e um passo que chama a si mesma com uma entrada menor. Cada chamada ocupa um quadro na pilha: recursão muito profunda estoura a pilha — às vezes um laço é melhor.
Programação dinâmica
Quando os mesmos subproblemas se repetem e a resposta ótima se monta a partir das respostas ótimas dos menores, guarde-as. Duas formas:
- De cima para baixo (memoização): a recursão de sempre, com um mapa de respostas já calculadas.
- De baixo para cima (tabulação): um laço que preenche a tabela do menor para o maior, sem recursão.
Como reconhecer
Perguntas como "de quantas formas…", "qual o mínimo/máximo…" ou "é possível…" sobre sequências e escolhas costumam ser DP: troco com menos moedas, mochila, maior subsequência comum (o diff do Git), distância de edição (corretor ortográfico). Defina o estado, a transição e o caso base — o resto é preencher a tabela.
No código: antes e depois
// O(2ⁿ): recalcula os mesmos valores milhões de vezes
func fib(_ n: Int) -> Int {
n < 2 ? n : fib(n - 1) + fib(n - 2)
} // O(n) de tempo, O(1) de espaço: tabulação guardando só os dois últimos
func fib(_ n: Int) -> Int {
var (a, b) = (0, 1)
for _ in 0..<n { (a, b) = (b, a + b) }
return a
}
// Troco com menos moedas: menor[v] = 1 + min(menor[v - moeda])
func menosMoedas(_ valor: Int, _ moedas: [Int]) -> Int? {
var menor = [0] + Array(repeating: Int.max, count: valor)
for v in stride(from: 1, through: valor, by: 1) {
for m in moedas where m <= v && menor[v - m] != Int.max {
menor[v] = min(menor[v], menor[v - m] + 1)
}
}
return menor[valor] == Int.max ? nil : menor[valor]
} fib(40) ingênuo faz mais de 300 milhões de chamadas; a versão em laço faz 40 voltas. O troco preenche uma tabela de valor + 1 posições: O(valor × moedas).
Em resumo
- Recursão: caso base + passo menor.
- Recursão profunda estoura a pilha.
- DP = subproblemas repetidos + guardar respostas.
- Memoização (de cima) ou tabulação (de baixo).
- fib ingênuo O(2ⁿ) vira O(n) com DP.
Exercícios
Responda e confira na hora. Complexidades valem com ou sem espaços, como O(n log n).
-
Com fib(0) = 0 e fib(1) = 1, quanto vale fib(10)?
Ver a solução
Resposta: 55. 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 — o 11º termo, de índice 10.
-
Qual a complexidade do Fibonacci recursivo ingênuo (sem memo)?
Ver a solução
Resposta: O(2ⁿ). Cada chamada gera duas outras: a árvore de chamadas dobra a cada nível (o valor exato é cerca de 1,618ⁿ, ainda exponencial).
-
Com moedas de 1, 2 e 5, qual o menor número de moedas para formar 11?
Ver a solução
Resposta: 3. 5 + 5 + 1 = 11. Com 2 moedas o máximo é 10, então 3 é o mínimo.
Perguntar à IA
Monte um prompt pronto sobre Recursão e programação dinâmica na sua linguagem e no seu caso, e abra num chat gratuito.
O prompt vai para o site que você escolher. Não cole senhas, tokens nem dados reais de clientes. Para Gemini e outros, copie e cole.