Como Encontrar a Complexidade de Relações de Recorrência Passo a Passo
Se você estuda algoritmos e estruturas de dados, em algum momento se deparou com códigos recursivos (como o famoso Merge Sort ou a busca binária). Calcular o tempo de execução desses algoritmos exige resolver algo chamado relação de recorrência.
Talvez você tenha tentado decorar o Teorema Mestre ou se assustado com expansões matemáticas gigantes. A boa notícia é que você não precisa de nada disso. O segredo está em entender o modelo mental da árvore de recursão.
Neste artigo, vamos desmistificar o cálculo da complexidade assintótica ($\Theta$), mostrando passo a passo como você pode encontrar a resposta apenas observando como o trabalho é distribuído.
1. A Introdução: O que é e o Modelo Mental
Uma relação de recorrência é uma equação matemática que descreve o tempo de execução de um algoritmo recursivo em função do tamanho da sua entrada ($n$).
Em geral, algoritmos de divisão e conquista seguem um formato muito característico:
\[T(n) = a T(n/b) + f(n)\]Para não se perder na sopa de letrinhas, entenda o papel de cada termo no código:
- $T(n)$: É o tempo total necessário para processar uma entrada de tamanho $n$.
- $a$ (Número de subproblemas): Representa quantas chamadas recursivas a função faz. Na árvore, isso define o fator de ramificação (quantos galhos saem de cada nó).
- $n/b$ (ou $n - c$): É o tamanho da entrada que passamos para as chamadas filhas. Ele dita a velocidade com que o problema encolhe a cada passo.
- $f(n)$ (Trabalho local): É o esforço feito no nó atual fora das chamadas recursivas — ou seja, o custo de preparar a divisão dos dados antes da chamada e de juntar/combinar os resultados depois que elas retornam.
O Modelo Mental da Árvore de Recursão
A forma mais transparente e intuitiva de resolver qualquer recorrência é desenhar a sua árvore de recursão. Imagine o algoritmo como uma árvore crescendo de cima para baixo:
- O topo (Nível 0) é a chamada inicial com o problema original de tamanho $n$.
- Cada chamada gera filhos (subproblemas menores) no nível de baixo.
- Na base da árvore estão as folhas, que representam as chamadas no caso base (quando o tamanho do problema chega a $1$ e a recursão para).
Para descobrir a complexidade de qualquer recorrência, você só precisa responder às mesmas 4 perguntas em cada nível:
- Quantos nós temos neste nível? (Quantos subproblemas estão rodando simultaneamente).
- Qual é o tamanho da entrada em cada nó? (Quanto dado cada chamada filha recebeu).
- Qual é o custo de trabalho local de cada nó? (Quanto esforço individual aquele nó faz).
- Qual é o custo total somado deste nível? ($\text{Quantidade de nós} \times \text{Custo de cada nó}$).
No final, o tempo total de execução do algoritmo será simplesmente a soma de duas contas fundamentais:
\[\text{Tempo Total} = \sum (\text{Custo de todos os nós internos}) + (\text{Custo de todas as folhas})\]A complexidade assintótica final ($\Theta$) será decidida por quem vencer essa disputa:
- O trabalho pode estar concentrado nas folhas (na base da árvore);
- O trabalho pode estar concentrado na raiz (no topo da árvore);
- O trabalho pode estar perfeitamente equilibrado em todos os andares da árvore.
2. A Grande Bifurcação: Divisão ($n/b$) vs. Subtração ($n - c$)
Antes de somarmos os custos, precisamos saber a profundidade (altura) da árvore. Isso depende inteiramente de como o tamanho da entrada é reduzido a cada chamada recursiva:
A Mágica da Divisão ($n/b$)
Quando dividimos a entrada por um fator constante (ex: $n/2$, $n/3$), o tamanho do problema encolhe geometricamente.
Quantas vezes precisamos dividir um número $n$ pela metade até chegar a $1$? Exatamente $\log_2 n$ vezes.
- Profundidade (altura) da árvore: $\log_b n$.
- Impacto no desempenho: Como o logaritmo cresce de forma incrivelmente lenta (por exemplo, para $n = 1.000.000$, $\log_2 n \approx 20$), a árvore tem pouquíssimos andares! É por isso que algoritmos do tipo divisão e conquista são tão velozes.
O Caminho Longo da Subtração ($n - c$)
Quando reduzimos a entrada subtraindo um valor constante (ex: $n - 1$), o problema encolhe de forma linear e vagarosa.
Quantas vezes você precisa subtrair $1$ de $n$ até chegar a zero? Exatamente $n$ vezes.
- Profundidade (altura) da árvore: $n$.
- Impacto no desempenho: A árvore tem uma quantidade enorme de andares. Se cada nó se ramificar em duas chamadas, teremos uma árvore binária com $n$ níveis de profundidade, resultando em $2^n$ folhas. Para $n = 100$, $2^{100}$ já é um número com mais de 30 dígitos! É a receita clássica para explosões exponenciais.
3. A Matemática Essencial (Sem Mistério)
Ao calcular o custo total de cada nível da árvore, nós quase sempre encontramos uma Progressão Geométrica (PG). Você não precisa se assustar com fórmulas: o que realmente importa é entender a razão $q$, que é a taxa pela qual o trabalho de um nível varia para o nível seguinte:
\[q = \frac{\text{Custo do Nível } (k+1)}{\text{Custo do Nível } k}\]Existem apenas três cenários possíveis para $q$:
1. A PG Crescente ($q > 1$): A árvore pesa para baixo (As Folhas Vencem)
Se $q > 1$, o trabalho está aumentando a cada andar que descemos. O nível mais pesado de todos é o último andar (o nível das folhas).
A fórmula da soma dos termos de uma PG finita é:
\[S_k = a_1 \frac{q^k - 1}{q - 1}\]O que importa aqui? Como $q > 1$, o termo dominante que explode e dita o resultado é $q^k$ (o trabalho lá na base). O custo das folhas domina o algoritmo, e o trabalho feito nos níveis superiores se torna desprezível.
2. O Equilíbrio ($q = 1$): Todos os andares trabalham igual
Se $q = 1$, cada nível da árvore realiza exatamente a mesma quantidade de trabalho.
Aqui você nem precisa de fórmula de PG: basta multiplicar o custo de um nível pela quantidade total de níveis da árvore:
\[\text{Custo Total} = (\text{Custo de 1 nível}) \times (\text{Altura da árvore})\]Como a altura em divisões é $\log n$, o resultado costuma ser algo como $\Theta(n \log n)$.
3. A PG Decrescente ($q < 1$): A raiz domina tudo (O Topo Vence)
Se $q < 1$, o trabalho está diminuindo a cada nível. Conforme descemos, os subproblemas ficam tão pequenos que o custo local despenca. O maior esforço do algoritmo inteiro acontece logo no Nível 0 (a raiz).
A soma de uma PG decrescente pode ser limitada pela soma infinita:
\[S = \frac{a_1}{1 - q}\]Como $q$ é uma fração fixa, o termo $\frac{1}{1-q}$ é apenas uma constante numérica. Isso significa que a soma total é simplesmente uma constante multiplicada pelo custo da raiz ($a_1$). Em notação assintótica, o custo final é simplesmente a ordem de grandeza da raiz!
4. Estudos de Caso Resolvidos Passo a Passo
Agora vamos aplicar esse método nível por nível em cinco casos fundamentais. Em cada um, vamos responder às 4 perguntas, mostrar de onde vem cada número e somar o resultado.
Caso 1: Divisão com custo constante
\(T(n) = 2T(n/2) + 1\) (Exemplo prático: Percorrer todos os nós de uma árvore binária)
Identificando os parâmetros da equação:
- Cada chamada gera $a = 2$ chamadas filhas.
- Cada subproblema recebe metade do tamanho ($n/2$).
- O trabalho local fora da recursão é $f(n) = 1$ (tempo constante).
Construindo a árvore nível por nível:
- Nível 0 (Raiz):
- Quantidade de nós: $1$ nó.
- Tamanho da entrada: $n$.
- Custo local do nó: $1$.
- Custo total do Nível 0: $1 \times 1 = 1$.
- Ramificação: Esta raiz faz $2$ chamadas recursivas, cada uma recebendo tamanho $n/2$.
- Nível 1:
- Quantidade de nós: $2$ nós (os 2 filhos gerados pela raiz).
- Tamanho em cada nó: $n/2$.
- Custo local de cada nó: $1$ (pois $f(n/2) = 1$).
- Custo total do Nível 1: $2 \text{ nós} \times 1 = 2$.
- Ramificação: Cada um desses $2$ nós faz $2$ novas chamadas. Portanto, teremos $2 \times 2 = 4$ nós no próximo nível!
- Nível 2:
- Quantidade de nós: $4$ nós ($2^2$).
- Tamanho em cada nó: $(n/2)/2 = n/4$.
- Custo local de cada nó: $1$.
- Custo total do Nível 2: $4 \text{ nós} \times 1 = 4$.
- Ramificação: Cada um dos $4$ nós faz $2$ chamadas $\implies 4 \times 2 = 8$ nós ($2^3$) no Nível 3.
- Nível $k$ (Genérico):
- Quantidade de nós: $2^k$.
- Tamanho em cada nó: $n/2^k$.
- Custo local de cada nó: $1$.
- Custo total do Nível $k$: $2^k \times 1 = 2^k$.
Altura da árvore e Condição de parada: A recursão atinge o caso base quando o tamanho do subproblema chega a $1$: \(\frac{n}{2^k} = 1 \iff 2^k = n \iff k = \log_2 n\) Portanto, a árvore tem altura $h = \log_2 n$ (vai do nível $0$ ao nível $\log_2 n$).
O nível das folhas ($k = \log_2 n$):
- Quantas folhas temos no último nível? $2^{\log_2 n} = n$ folhas!
- Cada folha executa o caso base $T(1)$, que custa tempo constante $1$.
- Custo total das folhas: $n \times 1 = n$.
Soma total de todos os níveis: A sequência de custos nível a nível é: $1, 2, 4, 8, \dots, n$. Trata-se de uma PG com primeiro termo $a_1 = 1$, razão $q = 2$, e último termo $n$. Pela fórmula da soma da PG: \(S = \sum_{k=0}^{\log_2 n} 2^k = 1 \cdot \frac{2^{\log_2 n + 1} - 1}{2 - 1} = 2 \cdot 2^{\log_2 n} - 1 = 2n - 1\)
- Veredito Final: As folhas dominam o trabalho. A complexidade é $\Theta(n)$.
- Lição didática: Por que deu linear se a árvore tem altura logarítmica? Porque a cada nível que descemos, o trabalho dobra. O nível das folhas sozinho concentra $n$ operações, o que é mais do que a soma de todos os níveis anteriores juntos ($n - 1$).
Caso 2: Subtração com custo constante
\(T(n) = 2T(n-1) + 1\) (Exemplo prático: Algoritmo ingênuo da Torre de Hanói ou Fibonacci recursivo simples)
Identificando os parâmetros da equação:
- Cada chamada gera $a = 2$ novas chamadas.
- O tamanho do problema diminui por subtração: $n - 1$.
- O trabalho local fora da recursão é constante: $f(n) = 1$.
Construindo a árvore nível por nível:
- Nível 0 (Raiz):
- Quantidade de nós: $1$ nó, tamanho $n$.
- Custo local do nó: $1$.
- Custo total do Nível 0: $1$.
- Ramificação: Gera $2$ chamadas com tamanho $n-1$.
- Nível 1:
- Quantidade de nós: $2$ nós, cada um com tamanho $n-1$.
- Custo local de cada nó: $1$.
- Custo total do Nível 1: $2 \times 1 = 2$.
- Ramificação: Cada um dos $2$ nós faz $2$ chamadas $\implies 2 \times 2 = 4$ nós de tamanho $n-2$ no Nível 2.
- Nível 2:
- Quantidade de nós: $4$ nós ($2^2$), cada um com tamanho $n-2$.
- Custo local de cada nó: $1$.
- Custo total do Nível 2: $4 \times 1 = 4$.
- Nível $k$ (Genérico):
- Quantidade de nós: $2^k$.
- Tamanho em cada nó: $n - k$.
- Custo total do Nível $k$: $2^k \times 1 = 2^k$.
Altura da árvore e Condição de parada: Como subtraímos $1$ por nível, a recursão só para quando $n - k = 0 \iff k = n$. A profundidade da árvore é de $n$ andares inteiros!
O nível das folhas ($k = n$):
- Quantidade de folhas no fundo: $2^n$ folhas.
- Custo das folhas: $2^n \times 1 = 2^n$.
Soma total: \(S = \sum_{k=0}^n 2^k = 1 + 2 + 4 + \dots + 2^n = 2^{n+1} - 1 = 2 \cdot 2^n - 1\)
- Veredito Final: O crescimento é puramente exponencial. A complexidade é $\Theta(2^n)$.
- Lição didática: Compare com o Caso 1. No Caso 1 a árvore também duplicava os nós por nível, mas parava muito rápido porque a profundidade era apenas $\log_2 n$. Aqui, como subtraímos $1$ em vez de dividir, a árvore desce $n$ andares completos. Uma árvore binária com $n$ andares tem $2^n$ folhas, tornando o algoritmo inviável para valores grandes de $n$.
Caso 3: Dominância das folhas na divisão
\(T(n) = 4T(n/2) + n\)
Identificando os parâmetros da equação:
- Cada chamada gera $a = 4$ chamadas recursivas (4 ramificações saindo de cada nó).
- Cada subproblema recebe metade do tamanho ($n/2$).
- O trabalho local é linear: $f(n) = n$.
Construindo a árvore nível por nível:
- Nível 0 (Raiz):
- Quantidade de nós: $1$ nó.
- Tamanho da entrada: $n$.
- Custo local do nó: $f(n) = n$.
- Custo total do Nível 0: $1 \times n = n$.
- Ramificação: A equação diz $4T(n/2)$. Isso significa que essa raiz gera 4 chamadas filhas, cada uma recebendo uma entrada de tamanho $n/2$.
- Nível 1:
- Quantidade de nós: Temos 4 nós (os 4 filhos criados pela raiz).
- Tamanho da entrada em cada nó: $n/2$.
- Custo local de cada nó: Como o tamanho da entrada agora é $n/2$, cada nó gasta $f(n/2) = n/2$ para fazer seu trabalho local.
- Custo total do Nível 1: \(4 \text{ nós} \times \left(\frac{n}{2}\right) = 2n\)
- De onde vem o próximo nível (a origem dos 16 problemas): Aqui está a chave da árvore: cada um desses 4 nós precisa rodar o algoritmo novamente. E o que a equação faz? Cada nó gera 4 novas chamadas! \(\text{Quantidade de nós no Nível 2} = 4 \text{ nós do Nível 1} \times 4 \text{ filhos por nó} = 16 \text{ nós (ou } 4^2\text{)}\) E que tamanho cada um desses 16 nós receberá? Eles pegam o tamanho atual ($n/2$) e dividem por 2 novamente: \(\text{Tamanho de cada subproblema} = \frac{n/2}{2} = \frac{n}{4}\)
- Nível 2:
- Quantidade de nós: 16 nós ($4^2$).
- Tamanho da entrada em cada nó: $n/4$.
- Custo local de cada nó: $f(n/4) = n/4$.
- Custo total do Nível 2: \(16 \text{ nós} \times \left(\frac{n}{4}\right) = 4n\)
- Ramificação para o Nível 3: Cada um desses 16 nós gera mais 4 chamadas. Teremos $16 \times 4 = 64$ nós ($4^3$), cada um recebendo tamanho $(n/4)/2 = n/8$.
- Nível 3:
- Quantidade de nós: 64 nós ($4^3$).
- Tamanho da entrada em cada nó: $n/8$.
- Custo local de cada nó: $n/8$.
- Custo total do Nível 3: \(64 \text{ nós} \times \left(\frac{n}{8}\right) = 8n\)
- Nível $k$ (Genérico):
- Quantidade de nós: $4^k$.
- Tamanho da entrada em cada nó: $n / 2^k$.
- Custo local de cada nó: $n / 2^k$.
- Custo total do Nível $k$: \(4^k \times \left(\frac{n}{2^k}\right) = \left(\frac{4}{2}\right)^k \times n = 2^k n\)
Observe a progressão dos custos somados de cada nível:
- Nível 0: $n$
- Nível 1: $2n$
- Nível 2: $4n$
- Nível 3: $8n$
- …
- Nível $k$: $2^k n$
A cada nível que descemos, o custo total do nível dobra! Temos uma PG crescente com razão $q = 2$.
Altura da árvore e Caso Base: A divisão por 2 atinge o caso base quando o tamanho chega a $1$: \(\frac{n}{2^k} = 1 \iff 2^k = n \iff k = \log_2 n\) A altura da árvore é $h = \log_2 n$.
O nível das folhas ($k = \log_2 n$):
- Quantas folhas temos no último nível da árvore? \(\text{Número de folhas} = 4^h = 4^{\log_2 n}\) Usando a propriedade das potências e logaritmos $4^{\log_2 n} = (2^2)^{\log_2 n} = (2^{\log_2 n})^2 = n^2$: \(\text{Temos exatamente } n^2 \text{ folhas!}\)
- Cada folha executa o caso base $T(1)$, que custa tempo constante $\Theta(1)$.
- Custo total do nível das folhas: \(\text{Custo das folhas} = n^2 \times \Theta(1) = \Theta(n^2)\)
Soma total de toda a árvore:
- Custo somado dos nós internos (níveis de $0$ até $\log_2 n - 1$): \(S_{\text{internos}} = \sum_{k=0}^{\log_2 n - 1} 2^k n = n \sum_{k=0}^{\log_2 n - 1} 2^k = n (2^{\log_2 n} - 1) = n (n - 1) = n^2 - n\)
-
Somando os nós internos com o nível das folhas: \(\text{Custo Total} = S_{\text{internos}} + \text{Folhas} = (n^2 - n) + n^2 = 2n^2 - n = \Theta(n^2)\)
- Veredito Final: As folhas dominam o custo total. A complexidade é $\Theta(n^2)$.
- Lição didática: Por que deu quadrático se o trabalho na raiz era apenas linear ($n$)? Porque a árvore se ramifica muito agressivamente ($4$ novos galhos por nó) enquanto divide o problema lentamente (apenas pela metade). Essa proliferação de ramos gera tantas folhas no final ($n^2$) que o trabalho na base esmaga todo o restante!
Caso 4: O Equilíbrio Perfeito
\(T(n) = 2T(n/2) + n\) (Exemplo prático: O algoritmo Merge Sort)
Identificando os parâmetros da equação:
- Cada chamada gera $a = 2$ chamadas filhas (divide o vetor em duas metades).
- Cada subproblema recebe metade do tamanho ($n/2$).
- O trabalho local é linear: $f(n) = n$ (o custo de intercalar as duas metades).
Construindo a árvore nível por nível:
- Nível 0 (Raiz):
- Quantidade de nós: $1$ nó de tamanho $n$.
- Custo local do nó: $f(n) = n$.
- Custo total do Nível 0: $1 \times n = n$.
- Ramificação: Gera $2$ chamadas com tamanho $n/2$.
- Nível 1:
- Quantidade de nós: $2$ nós de tamanho $n/2$.
- Custo local de cada nó: $f(n/2) = n/2$.
- Custo total do Nível 1: \(2 \text{ nós} \times \left(\frac{n}{2}\right) = n\)
- Ramificação: Cada um dos $2$ nós faz $2$ chamadas $\implies 2 \times 2 = 4$ nós no Nível 2, cada um com tamanho $(n/2)/2 = n/4$.
- Nível 2:
- Quantidade de nós: $4$ nós ($2^2$) de tamanho $n/4$.
- Custo local de cada nó: $f(n/4) = n/4$.
- Custo total do Nível 2: \(4 \text{ nós} \times \left(\frac{n}{4}\right) = n\)
- Ramificação: Cada um dos $4$ nós faz $2$ chamadas $\implies 4 \times 2 = 8$ nós ($2^3$) no Nível 3.
- Nível $k$ (Genérico):
- Quantidade de nós: $2^k$.
- Tamanho da entrada em cada nó: $n / 2^k$.
- Custo local de cada nó: $n / 2^k$.
- Custo total do Nível $k$: \(2^k \times \left(\frac{n}{2^k}\right) = n\)
Observe a mágica deste caso: Em qualquer nível $k$, a soma de trabalho é rigorosamente igual a $n$! A razão é $q = 1$. Não há crescimento nem decrescimento.
Altura da árvore e Folhas:
- A recursão para quando $n / 2^k = 1 \iff k = \log_2 n$.
- A altura da árvore é $h = \log_2 n$.
- A árvore tem exatamente $(\log_2 n + 1)$ andares (do nível $0$ ao nível $\log_2 n$).
- No nível das folhas ($k = \log_2 n$), temos $2^{\log_2 n} = n$ folhas. Cada uma custa $\Theta(1)$, totalizando $n \times 1 = n$.
Soma total: Como cada andar custa exatamente $n$, basta multiplicar o custo de um nível pelo número de andares: \(\text{Custo Total} = n \times (\log_2 n + 1) = n \log_2 n + n = \Theta(n \log n)\)
- Veredito Final: Equilíbrio perfeito entre todos os níveis. A complexidade é $\Theta(n \log n)$.
- Lição didática: O aumento do número de nós ($2^k$) cancela perfeitamente a redução no tamanho do problema ($1/2^k$). Como nenhum andar domina o outro, o custo total é a multiplicação do trabalho por nível pela altura da árvore.
Caso 5: Dominância da raiz
\(T(n) = 2T(n/2) + n^2\)
Identificando os parâmetros da equação:
- Cada chamada gera $a = 2$ subproblemas.
- O problema é dividido pela metade ($n/2$).
- O trabalho local é quadrático: $f(n) = n^2$.
Construindo a árvore nível por nível:
- Nível 0 (Raiz):
- Quantidade de nós: $1$ nó de tamanho $n$.
- Custo local do nó: $f(n) = n^2$.
- Custo total do Nível 0: $1 \times n^2 = n^2$.
- Ramificação: Gera $2$ chamadas com tamanho $n/2$.
- Nível 1:
- Quantidade de nós: $2$ nós de tamanho $n/2$.
- Custo local de cada nó: Como $f(x) = x^2$, ao passar $x = n/2$: \(f(n/2) = \left(\frac{n}{2}\right)^2 = \frac{n^2}{4}\)
- Custo total do Nível 1: \(2 \text{ nós} \times \left(\frac{n^2}{4}\right) = \frac{n^2}{2}\)
- Ramificação: Cada um dos $2$ nós gera $2$ filhos $\implies 2 \times 2 = 4$ nós no Nível 2 com tamanho $(n/2)/2 = n/4$.
- Nível 2:
- Quantidade de nós: $4$ nós ($2^2$) de tamanho $n/4$.
- Custo local de cada nó: \(f(n/4) = \left(\frac{n}{4}\right)^2 = \frac{n^2}{16}\)
- Custo total do Nível 2: \(4 \text{ nós} \times \left(\frac{n^2}{16}\right) = \frac{n^2}{4}\)
- Ramificação: Cada um dos $4$ nós gera $2$ filhos $\implies 4 \times 2 = 8$ nós ($2^3$) no Nível 3.
- Nível $k$ (Genérico):
- Quantidade de nós: $2^k$.
- Tamanho da entrada em cada nó: $n / 2^k$.
- Custo local de cada nó: $\left(\frac{n}{2^k}\right)^2 = \frac{n^2}{4^k}$.
- Custo total do Nível $k$: \(2^k \times \frac{n^2}{4^k} = 2^k \times \frac{n^2}{2^{2k}} = \frac{n^2}{2^k}\)
Observe a sequência de custos dos níveis:
- Nível 0: $n^2$
- Nível 1: $\frac{n^2}{2}$
- Nível 2: $\frac{n^2}{4}$
- Nível 3: $\frac{n^2}{8}$
- …
- Nível $k$: $\frac{n^2}{2^k}$
O custo total de cada nível cai pela metade a cada andar que descemos! A razão é $q = 1/2 < 1$. Temos uma PG decrescente.
Altura da árvore e Folhas:
- A árvore para quando $n / 2^k = 1 \iff k = \log_2 n$.
- No nível das folhas ($k = \log_2 n$), temos $2^{\log_2 n} = n$ folhas.
- Custo das folhas: $n \times \Theta(1) = \Theta(n)$.
Soma total (PG Decrescente): Somando todos os níveis: \(S = n^2 + \frac{n^2}{2} + \frac{n^2}{4} + \dots = n^2 \left(1 + \frac{1}{2} + \frac{1}{4} + \dots + \frac{1}{2^{\log_2 n}}\right)\)
Como a razão $q = 1/2$ é menor que 1, a soma de toda a árvore é limitada pela soma da PG infinita: \(S < n^2 \sum_{k=0}^{\infty} \left(\frac{1}{2}\right)^k = n^2 \times \frac{1}{1 - 1/2} = n^2 \times 2 = 2n^2\)
Repare que o custo das folhas ($n$) é insignificante perto de $n^2$. A soma de todos os andares juntos não chega a passar do dobro da raiz!
- Veredito Final: A raiz domina completamente a complexidade. O resultado é $\Theta(n^2)$.
- Lição didática: O trabalho local na raiz é tão pesado ($n^2$) que, mesmo que o número de nós dobre a cada nível, os problemas menores ficam quadraticamente mais leves. A raiz faz praticamente todo o trabalho do algoritmo por conta própria.
5. A Conexão com o Teorema Mestre
Lembra do “temido” Teorema Mestre? Agora que entendemos como a árvore de recursão funciona, os três casos mágicos do teorema se revelam não como fórmulas abstratas para decorar, mas como os três comportamentos naturais da nossa árvore de recursão:
Para uma equação $T(n) = a T(n/b) + f(n)$, o número de folhas é sempre $n^{\log_b a}$. O Teorema Mestre apenas compara o peso das folhas com o peso da raiz $f(n)$:
- As folhas vencem (Caso 1 do Teorema Mestre):
Quando o trabalho local $f(n)$ cresce mais devagar que a quantidade de folhas $n^{\log_b a}$.
- O custo nas pontas da árvore domina o total: $\Theta(n^{\log_b a})$.
- Foi exatamente o que vimos no nosso Caso 3: $f(n) = n$, mas as folhas foram $n^{\log_2 4} = n^2$. Resultado: $\Theta(n^2)$.
- O equilíbrio perfeito (Caso 2 do Teorema Mestre):
Quando o trabalho local $f(n)$ cresce no mesmo ritmo que a quantidade de folhas $n^{\log_b a}$.
- Todos os níveis têm o mesmo custo. Multiplicamos o trabalho de um nível pela altura da árvore ($\log n$): $\Theta(f(n) \log n)$.
- Foi exatamente o que vimos no nosso Caso 4 (Merge Sort): $f(n) = n$ e as folhas foram $n^{\log_2 2} = n$. Resultado: $\Theta(n \log n)$.
- A raiz vence (Caso 3 do Teorema Mestre):
Quando o trabalho local $f(n)$ cresce mais rápido que a quantidade de folhas $n^{\log_b a}$.
- A raiz é tão pesada que concentra quase todo o custo do algoritmo: $\Theta(f(n))$.
- Foi exatamente o que vimos no nosso Caso 5: $f(n) = n^2$, enquanto as folhas foram apenas $n^{\log_2 2} = n$. Resultado: $\Theta(n^2)$.
A grande vantagem da Árvore de Recursão: O Teorema Mestre só funciona quando a divisão é do tipo $n/b$. Se a recorrência for por subtração (como o nosso Caso 2: $T(n) = 2T(n-1) + 1$), o Teorema Mestre não pode ser aplicado. Já com a Árvore de Recursão, o mesmo modelo mental resolve o problema sem esforço!
6. Resumão de Complexidade de Recorrências
Para fechar com chave de ouro, aqui está o resumo comparativo para você usar como guia rápido de consulta mental:
| Recorrência Exemplo | Tipo de Redução | Altura da Árvore | Razão ($q$) entre Níveis | Quem Domina o Custo? | Complexidade Final $\Theta$ |
|---|---|---|---|---|---|
| $T(n) = 2T(n/2) + 1$ | Divisão | $\log_2 n$ | $q = 2 > 1$ | As folhas (PG Crescente) | $\Theta(n)$ |
| $T(n) = 2T(n-1) + 1$ | Subtração | $n$ | $q = 2 > 1$ | As folhas explodem ($2^n$) | $\Theta(2^n)$ |
| $T(n) = 4T(n/2) + n$ | Divisão | $\log_2 n$ | $q = 2 > 1$ | As folhas (PG Crescente) | $\Theta(n^2)$ |
| $T(n) = 2T(n/2) + n$ | Divisão | $\log_2 n$ | $q = 1$ | Empate (Equilíbrio) | $\Theta(n \log n)$ |
| $T(n) = 2T(n/2) + n^2$ | Divisão | $\log_2 n$ | $q = 1/2 < 1$ | A raiz (PG Decrescente) | $\Theta(n^2)$ |
Agora você sabe: sempre que esbarrar em um algoritmo recursivo, basta fechar os olhos, desenhar mentalmente a árvore, responder às 4 perguntas de cada nível e verificar se o peso está no topo, na base ou espalhado igualmente. Sem fórmulas mágicas, apenas raciocínio lógico claro!