Pular para o conteudo principal

Recursão e Backtracking: quando Usar e quando Parar

Recursão não é um ritual elegante. É uma forma de resolver problemas que se repetem em subproblemas menores com condição clara de parada.

Andrews Ribeiro

Andrews Ribeiro

Founder & Engineer

O problema

Recursão costuma parecer inteligência de elite ou bruxaria ruim.

Para muita gente, as duas coisas ao mesmo tempo.

O resultado é previsível:

  • ou a pessoa evita toda recursão
  • ou usa recursão onde não precisava
  • ou escreve algo que até compila, mas não consegue explicar por que para ali

Backtracking costuma piorar ainda mais a ansiedade, porque adiciona árvore de decisão em cima disso.

Só que, no fundo, as duas ideias são menos místicas do que parecem.

Modelo mental

Recursão funciona quando um problema pode ser descrito em termos de versões menores dele mesmo.

Quase toda recursão saudável tem três partes:

  1. caso base
  2. redução para um subproblema menor
  3. combinação do resultado

Se uma dessas peças está nebulosa, a solução costuma ficar frágil.

Backtracking adiciona uma quarta peça:

  1. desfazer a escolha para testar a próxima

Em frase curta:

Recursão resolve um problema menor. Backtracking explora alternativas e volta quando uma escolha não fecha.

Quebrando o problema

Comece pelo caso base

Essa é a pergunta que muita gente pula cedo demais:

Quando eu paro de chamar a função?

Sem isso, a recursão fica parecendo um loop infinito vestido de função elegante.

Casos base comuns:

  • nó nulo
  • índice fora do limite
  • caminho completo
  • combinação pronta
  • profundidade zerada

Depois nomeie o subproblema

Outra pergunta forte:

O que exatamente a chamada recursiva menor resolve?

Exemplos:

  • “a profundidade máxima da subárvore esquerda”
  • “o resultado para o restante do array”
  • “todas as combinações a partir desta posição”

Quando você consegue dizer isso em voz alta, metade da confusão some.

O valor volta ou o estado é compartilhado?

Nem toda recursão funciona igual.

Algumas devolvem valor:

  • soma
  • altura
  • booleano

Outras usam estrutura compartilhada:

  • lista de respostas
  • caminho atual
  • conjunto parcial

Backtracking normalmente trabalha bastante com estado compartilhado mais desfazer.

A lógica do backtracking

Em backtracking, o ciclo mental costuma ser:

  1. escolher
  2. avançar
  3. voltar
  4. tentar outra escolha

O “voltar” é a parte que muita gente esquece.

Você adiciona algo ao caminho atual, chama a próxima etapa, e depois precisa desfazer para não contaminar a próxima ramificação.

Sem isso, a árvore de decisão fica errada.

Exemplo simples

Pegue a pergunta:

Gerar todos os subconjuntos de um array.

Esse é um caso clássico de backtracking.

Para cada posição, você tem duas opções:

  • incluir o elemento
  • não incluir o elemento

O caminho atual representa a escolha parcial.

O caso base acontece quando você passou por todos os elementos.

O raciocínio vira:

  1. se cheguei ao fim, salvo a combinação atual
  2. escolho incluir o elemento
  3. avanço
  4. desfaço
  5. escolho não incluir
  6. avanço

Veja como a estrutura aparece:

  • caso base
  • escolha
  • avanço
  • desfazer

Agora compare com um caso mais simples de recursão pura:

Profundidade máxima de árvore binária.

Aqui não há árvore de escolhas para explorar.

Só subproblemas menores que retornam valor.

Isso ajuda a separar bem recursão de backtracking.

Erros comuns

  • Escrever a chamada recursiva antes de definir o caso base.
  • Não conseguir explicar o que a função retorna.
  • Usar recursão em problema que ficaria mais claro iterativo.
  • Em backtracking, esquecer de desfazer o estado.
  • Tratar a pilha de chamadas como mágica e não como sequência de chamadas em aberto.

Como um senior pensa

Quem tem mais prática costuma reduzir recursão para peças bem concretas.

O pensamento é:

  • qual é o menor caso que já sei responder?
  • como faço o problema andar para lá?
  • o que cada chamada devolve?
  • estou carregando estado compartilhado ou devolvendo valor?

No caso de backtracking:

  • qual é a escolha?
  • qual é o próximo estado?
  • o que preciso desfazer na volta?

Essa clareza é mais útil do que tentar parecer sofisticado com recursão compacta demais.

Em trabalho real, esse cuidado aparece em árvore de menus, navegação hierárquica, parser, exploração de dependências, busca e geração de combinações.

Então, de novo, não é só teatro de entrevista.

O que o entrevistador quer ver

Ele quer enxergar se você:

  • sabe definir o caso base
  • entende qual subproblema a chamada resolve
  • consegue acompanhar o estado sem se perder
  • não usa backtracking como palavra mágica

Se você disser “essa função resolve o restante do problema a partir desta posição, e eu paro quando chego no fim”, já está mostrando entendimento de verdade.

Recursão fica assustadora quando parece mágica. Fica tratável quando vira contrato claro.

Backtracking não é adivinhar. É explorar possibilidades com disciplina para voltar limpo.

Resumo rápido

O que vale manter na cabeça

Checklist de pratica

Use isto ao responder

Você concluiu este artigo

Próximo artigo Dynamic Programming: como Identificar o Problema antes de Atacar Artigo anterior Árvores e grafos: como percorrer

Continue explorando

Artigos relacionados