O método: custo × vezes
- Cada linha executa em tempo constante \(c_i\) — é o modelo RAM da Aula 01.
- Logo, basta saber quantas vezes cada linha executa.
O custo total é a soma, linha a linha, de \(\text{custo}\times\text{vezes}\).
| Linha | Custo | Vezes |
|---|---|---|
soma = 0; | \(c_1\) | \(1\) |
for (i = 0; i < n; i++) | \(c_2\) | \(n+1\) |
soma += v[i]; | \(c_3\) | \(n\) |
Somando:
$$T(n) = c_1 + c_2(n+1) + c_3\,n = a\,n + b \;\Rightarrow\; \Theta(n)$$for executa uma vez a mais que o corpo: a última avaliação é a que falha e encerra o laço. Corpo \(n\) vezes, teste \(n+1\).Exemplo 1 · dois laços
void func(int *v, int n) {int i, j, sum = 0;for (i = 0; i < n; i++)sum += v[i];for (i = 1; i < n; i++)for (j = 1; j < n; j++)sum += v[j];}
i percorre \(0,1,\dots,n-1\): o corpo executa \(n\) vezes e o teste, \(n+1\).
Externo: i vai de \(1\) a \(n-1\), ou seja \(n-1\) iterações. Para cada valor de i, o interno roda \(n-1\) vezes.
Pela Aula 02, termos de menor ordem e constantes não importam: \(T(n)=\Theta(n^2)\). E dá para afirmar \(\Theta\), não apenas \(O\), porque a contagem foi exata e vale para toda entrada.
Exemplo 2 · Max, e quando melhor caso = pior caso
int Max(int *A, int n) {int i, temp;temp = A[0];for (i = 1; i < n; i++)if (temp < A[i])temp = A[i];return temp;}
O laço roda \(n-1\) vezes e cada iteração faz 1 comparação de elementos (temp < A[i]). Total: \(n-1\) comparações, sempre.
A atribuição temp = A[i] pode ou não executar, mas ela só altera a constante, nunca a ordem.
Se o custo é \(\Omega(f(n))\) no melhor caso e \(O(f(n))\) no pior caso, então o algoritmo é \(\Theta(f(n))\) — sem precisar analisar o caso médio.
O caso médio está sempre imprensado entre o melhor e o pior.
Exemplos 3 e 4 · MaxMin1 e MaxMin2
Os dois algoritmos acham o maior e o menor de uma vez. A diferença é uma única palavra: o else.
void MaxMin1(int *A, int n,int *Max, int *Min) {int i;*Max = A[0]; *Min = A[0];for (i = 1; i < n; i++) {if (A[i] > *Max) *Max = A[i];if (A[i] < *Min) *Min = A[i];}}
Laço com \(n-1\) iterações, e cada iteração faz 2 comparações de elementos — os dois if sempre executam.
void MaxMin2(int *A, int n,int *Max, int *Min) {int i;*Max = A[0]; *Min = A[0];for (i = 1; i < n; i++) {if (A[i] > *Max) *Max = A[i];else if (A[i] < *Min) *Min = A[i];}}
Agora a 2ª comparação só executa se a 1ª falhar — o custo passa a depender da entrada.
| Caso | Entrada | Comparações |
|---|---|---|
| Melhor | vetor crescente: A[i] > *Max é sempre verdadeiro, o else nunca executa | \(n-1\) |
| Pior | vetor decrescente: a 1ª comparação sempre falha, a 2ª sempre executa | \(2(n-1)\) |
| Médio | algo entre os dois | não precisa calcular |
Melhor caso \(\Omega(n)\) e pior caso \(O(n)\) têm a mesma ordem, logo \(T(n)=\Theta(n)\) para toda entrada. O caso médio vem de brinde.
MaxMin1 e MaxMin2 estão ambos em \(\Theta(n)\) — diferem só na constante. Na Aula 02 vimos que, dentro da mesma classe, é a constante que decide.
Exemplo 5 · pesquisa sequencial
int Pesquisa(TipoRegistro *A,int n, int chave) {int i = 0;while (i < n) {if (A[i].chave == chave)break;i++;}return i;}
O break faz o custo depender de onde a chave está.
| Caso | Situação | Comparações · ordem |
|---|---|---|
| Melhor | chave na posição 0 | \(1\) comparação \(\Rightarrow \Theta(1)\) |
| Pior | chave ausente, ou na última posição | \(n\) comparações \(\Rightarrow \Theta(n)\) |
| Médio | chave presente, posições equiprováveis | \(\frac{n+1}{2}\) comparações \(\Rightarrow \Theta(n)\) |
O caso médio entra como resultado citado: a derivação usa probabilidade (CLRS, seção 5.2).
Laços em sequência
Qual a ordem de complexidade do trecho?
for (i = 0; i < n; i++) s += A[i];for (j = 0; j < n; j++) s += B[j];
Laços em sequência somam, não multiplicam. Nenhum dos dois está dentro do outro.
Laço interno que depende de i
E agora, com o limite do laço interno preso ao índice externo?
for (i = 0; i < n; i++)for (j = 0; j <= i; j++)s += M[i][j];
Para cada \(i\), o interno roda \(i+1\) vezes (de \(j=0\) até \(j=i\)). A contagem vira um somatório:
$$\sum_{i=0}^{n-1}(i+1) = \frac{n(n+1)}{2} \;\Rightarrow\; \Theta(n^2)$$Metade do laço duplo completo — mas a ordem final é a mesma, porque a constante \(\tfrac12\) some no \(\Theta\).
O algoritmo misterioso
Abrindo a parte 2, um exercício de leitura: o que esta função calcula?
int XXXXX(int n) {int r = n;n--;while (n > 0) {r *= n;n--;}return r;}
\(r\) acumula \(n\cdot(n-1)\cdot(n-2)\cdots 2\cdot 1\): é o fatorial, \(n!\).
Pelo método custo × vezes: o laço roda \(n-1\) vezes com corpo constante.
$$T(n)=\Theta(n)$$A versão recursiva e o círculo
int Fatorial(int n) {if (n <= 0)return 1;elsereturn n * Fatorial(n-1);}
É a definição matemática escrita literalmente: \(n! = n\cdot(n-1)!\). Mas quantas “operações” custa a linha 5?
O custo dessa linha depende do custo da própria função que estamos analisando. Contar linha a linha entra em círculo: para saber \(T(n)\) eu precisaria já saber \(T(n-1)\), que precisa de \(T(n-2)\)…
O método da parte 1, sozinho, não fecha aqui.
A ferramenta que quebra o círculo — transformar essa circularidade em uma relação de recorrência e resolvê-la — é o assunto da parte 3.
Anatomia de uma função recursiva
Antes de resolver o círculo, a revisão de como a recursão funciona. Toda função recursiva tem duas partes, e as linhas do Fatorial acima se dividem exatamente entre elas.
A função não chama a si mesma; devolve uma resposta direta. É a condição de parada.
A função chama a si mesma com uma entrada menor, aproximando-se do caso base.
Guarde essa estrutura: a ferramenta de análise que vem depois espelha exatamente essas duas partes — caso base do algoritmo vira caso base de \(T\), chamada recursiva vira um termo \(T(\text{entrada menor})\).
Uma função \(A\) pode chamar \(B\), e \(B\) chamar \(A\) de volta — é a recursão indireta (ou mútua). A análise é a mesma.
Toda chamada de função cria um registro de ativação — parâmetros, variáveis locais, endereço de retorno — na pilha de execução. Ele só é liberado quando a chamada retorna.
A pilha de execução: \(fat(4)\) passo a passo
int fat(int n) {if (n <= 0)return 1;return n * fat(n-1);}int main() {int f = fat(4);printf("%d", f);}
A pilha cresce para cima. Leia a coluna do meio de baixo para cima (a descida) e a da direita de cima para baixo (a volta).
| Registro | Fase 1 · empilha | Fase 2 · desempilha |
|---|---|---|
fat(0) | testa \(n\le 0\) → caso base | devolve \(1\) |
fat(1) | chama fat(0) | \(1\cdot 1 = 1\) |
fat(2) | chama fat(1) | \(2\cdot 1 = 2\) |
fat(3) | chama fat(2) | \(3\cdot 2 = 6\) |
fat(4) | chama fat(3) | \(4\cdot 6 = 24\) |
main() | chama fat(4) | \(f = 24\) |
Fase 1: empilha até o caso base. Fase 2: desempilha multiplicando na volta — nenhuma multiplicação acontece na descida.
No pico, \(n+1\) registros de fat coexistem na pilha. Esse pico volta na parte 6, quando o custo de espaço entra na conta.
O que é uma relação de recorrência
Uma relação de recorrência define uma função \(T(n)\) em termos de seu próprio valor em entradas menores — mais os casos base, que encerram a definição.
Aqui \(T(n)\) é o tempo de execução do algoritmo para uma entrada de tamanho \(n\).
O que torna a ferramenta natural é que ela espelha o algoritmo: as duas partes da anatomia do tópico 08 viram as duas partes da recorrência, uma a uma.
- Extrair a recorrência do código.
- Resolvê-la, achando a ordem \(\Theta\) de \(T(n)\).
Extraindo a recorrência do Fatorial
Passo 1, feito sobre o mesmo Fatorial do tópico 07.
O caso mais simples é \(n=0\): testa e devolve 1. Trabalho constante — chame de \(c>0\).
$$T(0) = c$$Fora da chamada há teste, produto e retorno: custo constante \(d\). E a chamada Fatorial(n-1) custa, por definição, \(T(n-1)\).
Compacta e válida para todo \(n\) — mas ainda não responde: \(T(n)\) é \(\Theta\) de quê?
Repare no que aconteceu: o círculo do tópico 07 virou ferramenta. Em vez de fugir da circularidade, usamos o próprio \(T\) para nomear o custo da chamada — e agora é um problema de álgebra, não de contagem.
Resolvendo por expansão
- Expanda \(T\) algumas vezes — 2 ou 3 costumam bastar.
- Escreva o termo geral após \(k\) expansões.
- Descubra o \(k\) que atinge o caso base e substitua.
Também chamado de método da iteração, ou expansão telescópica.
No caso base: \(n-k=0 \Rightarrow k=n\). São \(n\) expansões — uma por chamada recursiva. Substituindo:
$$T(n) = \underbrace{T(0)}_{=\,c} + n\,d = d\,n + c \;\Rightarrow\; \Theta(n)$$Mesma ordem da versão iterativa do tópico 06 — e faz sentido: as duas fazem \(n\) multiplicações. As constantes \(c\) e \(d\) sumiram no \(\Theta\): nunca foi preciso conhecê-las, só saber que são constantes.
Imprime da parte 4 — daria \(k=n-1\). Errar aí troca o termo geral inteiro.Soma recursiva de vetor
Escreva e resolva a recorrência deste algoritmo.
int Soma(int *v, int n) {if (n == 0) return 0;return v[n-1] + Soma(v, n-1);}
Caso base: \(T(0)=c\). Caso recursivo: trabalho constante \(d\) mais uma chamada com \(n-1\).
$$T(n) = T(n-1) + d \;\Rightarrow\; T(n)=\Theta(n)$$É a mesma recorrência do fatorial — não precisa expandir de novo.
Quando a entrada é dividida
Até aqui a entrada encolhia de 1 em 1. Agora um algoritmo que divide: imprime o primeiro elemento e, se \(n>1\), chama a si mesmo para a segunda metade do vetor.
void Imprime(int *v, int n) {printf("%d\n", v[0]);if (n > 1)Imprime(v + n/2, n - n/2);}
Trabalho local constante (um printf) e uma chamada com entrada de tamanho \(\approx n/2\):
Seguindo o CLRS (p. 88), vamos suprimir pisos e tetos e supor \(n\) potência de 2. Esse desleixo controlado não altera a ordem de crescimento e é padrão na literatura.
A árvore de recursão
Expandir \(T(n-1)\) era fácil: uma linha por passo. Com \(T(n/2)\) — e pior, com duas chamadas — a expansão vira um emaranhado. A árvore de recursão organiza a mesma conta em um desenho.
- Cada nó é uma chamada da função, anotada com o custo local dela — sem contar as chamadas filhas.
- Os filhos de um nó são as chamadas recursivas que ele faz.
E somar tem dois passos: 1. some os custos dentro de cada nível; 2. some os totais dos níveis.
O tamanho cai pela metade a cada nível:
$$\frac{n}{2^0},\; \frac{n}{2^1},\; \frac{n}{2^2},\; \dots,\; \frac{n}{2^k} = 1 \;\Longrightarrow\; 2^k = n \;\Longrightarrow\; k = \log_2 n$$Logo a árvore tem \(\log_2 n + 1\) níveis — do nível 0 ao nível \(\log_2 n\).
Cada nível custa \(c\):
$$T(n) = c\,(\log_2 n + 1) = c\log_2 n + c \;\Rightarrow\; \Theta(\log n)$$Duas chamadas: a árvore do merge sort
Mesmo esqueleto, dois ajustes: o trabalho local vira uma varredura linear, e agora são duas chamadas, uma para cada metade.
void ImprimeMax(int *v, int n) {int i, max = v[0];for (i = 1; i < n; i++) /* custo c n */if (v[i] > max) max = v[i];printf("%d\n", max);if (n > 1) {ImprimeMax(v, n/2);ImprimeMax(v + n/2, n - n/2);}}
É a recorrência do merge sort (CLRS, eq. 2.1). O que fizermos aqui vale lá.
O tamanho cai à metade por nível, igual ao caso anterior: \(n/2^k = 1 \Rightarrow k=\log_2 n\), ou seja \(\log_2 n + 1\) níveis.
Confere pelas folhas: com \(\log_2 n\) duplicações, o último nível tem \(2^{\log_2 n} = n\) folhas. ✓
O custo do merge sort, provado com um desenho.
Os padrões que já sabemos resolver
| Recorrência | Estrutura do algoritmo | Solução |
|---|---|---|
| \(T(n-1) + c\) | 1 chamada, reduz 1, local constante | \(\Theta(n)\) |
| \(T(n/2) + c\) | 1 chamada, metade, local constante | \(\Theta(\log n)\) |
| \(2\,T(n/2) + c\,n\) | 2 chamadas, metades, local linear | \(\Theta(n\log n)\) |
Três perguntas decidem a classe: quanto reduz a entrada, quantas chamadas saem de cada nó e quanto custa o trabalho local.
Falta uma linha nessa tabela: e se forem duas chamadas que reduzem a entrada de 1 em 1? Esse é o Fibonacci, e a resposta é exponencial — é a parte 5, a seguir.
Vale o aviso honesto do slide: os exemplos de hoje foram bem comportados, com divisão exata e custo local simples. Para \(3T(n/4)+c\,n^2\) ou \(7T(n/2)+n^2\), desenhar e somar níveis vira conta longa e sujeita a erro — e é por isso que existe o método mestre (CLRS, seção 4.5), assunto da próxima aula, que a árvore de hoje justamente prova.
Fibonacci recursivo
O caso que faltava na tabela: duas chamadas que reduzem a entrada de 1 em 1.
int Fibonacci(int n) {if (n < 3)return 1;return Fibonacci(n-1)+ Fibonacci(n-2);}
É a definição matemática escrita literalmente — \(F_1=F_2=1\) e \(F_n=F_{n-1}+F_{n-2}\). Elegante. E quanto custa?
Dois termos recursivos, de tamanhos diferentes. Expandir direto fica feio: cada expansão duplica a quantidade de termos, e eles nem sequer são iguais entre si.
A desigualdade-chave e o teto \(O(2^n)\)
\(T\) é crescente — entrada maior nunca custa menos. Então \(T(n-2)\le T(n-1)\), e dá para trocar o termo menor pelo maior:
$$T(n) \;\le\; 2\,T(n-1) + d$$Trocamos a recorrência por uma mais simples que a limita por cima: o que provarmos para ela vale como \(O\) para a original.
O caso base é \(n<3\), então paramos em \(n-k=2 \Rightarrow k=n-2\):
$$T(n) \;\le\; 2^{\,n-2}c + (2^{\,n-2}-1)\,d \;\Rightarrow\; O(2^n)$$O número de chamadas dobra a cada nível: crescimento exponencial.
Com mais matemática — as raízes do polinômio característico \(x^2=x+1\) — chega-se ao limite justo:
$$T(n)=\Theta(\varphi^{\,n}), \qquad \varphi=\frac{1+\sqrt5}{2}=1{,}61803\dots \quad \text{(razão áurea)}$$A árvore entrega o culpado
Por que tanto? A árvore de recursão da parte 4, desenhada para \(F_5\), mostra o problema em um golpe de vista.
O número de chamadas é \(\approx 2F_n-1\). Supondo \(10^8\) operações por segundo:
| \(n\) | Chamadas | Tempo |
|---|---|---|
| 30 | \(\approx 1{,}7\times10^{6}\) | \(\approx 0{,}02\) s — imperceptível |
| 50 | \(\approx 2{,}5\times10^{10}\) | \(\approx 4\) min — um cafezinho |
| 100 | \(\approx 7\times10^{20}\) | \(\approx 2\times10^{5}\) anos — civilizações inteiras |
Ir de \(n=50\) para \(n=100\) não dobra o tempo: multiplica por \(\sim 10^{10}\). É isso que “exponencial” significa.
A alternativa iterativa
int FibIter(int n) {int fn1 = 1, fn2 = 1, fn, i;if (n < 3) return 1;for (i = 3; i <= n; i++) {fn = fn2 + fn1;fn2 = fn1;fn1 = fn;}return fn;}
Guarda só os 2 últimos valores e avança; nenhum subproblema é recalculado. O laço roda \(n-2\) vezes com corpo constante.
$$T(n)=\Theta(n)$$Para \(n=100\): recursivo \(\sim 10^{20}\) operações, iterativo \(\sim 100\).
Dá para fazer ainda melhor — \(\Theta(\log n)\) com exponenciação rápida de matrizes —, mas isso fica fora do escopo da disciplina.
Mesma tarefa, memórias diferentes
Volte ao fatorial do tópico 09 e olhe as duas versões no pico de uso da pilha, para \(n=4\).
Complexidade de espaço
| Algoritmo | Tempo | Espaço (pilha) |
|---|---|---|
| Fatorial iterativo | \(\Theta(n)\) | \(\Theta(1)\) |
| Fatorial recursivo | \(\Theta(n)\) | \(\Theta(n)\) |
Mesmo tempo, espaços diferentes: a pilha de execução é custo, e não aparece se olharmos só o tempo. O Fibonacci recursivo, esse, perdia nos dois critérios.
Recursão nem sempre é a melhor escolha — mesmo quando o problema é definido recursivamente.
Mas ela não é vilã: no merge sort da parte 4 a pilha tem profundidade \(\Theta(\log n)\), um preço ínfimo pela clareza de um dividir-para-conquistar.
Padrões de reconhecimento rápido
A tabela do tópico 16, agora com a linha que faltava.
| Se o algoritmo… | Recorrência | Classe |
|---|---|---|
| reduz 1, faz 1 chamada, local \(O(1)\) | \(T(n-1)+c\) | \(\Theta(n)\) |
| divide por 2, faz 1 chamada, local \(O(1)\) | \(T(n/2)+c\) | \(\Theta(\log n)\) |
| divide por 2, faz 2 chamadas, local \(O(n)\) | \(2T(n/2)+c\,n\) | \(\Theta(n\log n)\) |
| reduz 1, faz 2 chamadas | \(T(n-1)+T(n-2)+c\) | \(\Theta(\varphi^{\,n})\) |
Classifique sem desenhar
Para cada recorrência, diga a ordem \(\Theta\) — usando expansão ou a árvore mental.
Caminho com \(\log_2 n\) níveis de custo \(c\).
$$\Rightarrow\; \Theta(\log n)$$Padrão novo: reduz 1, mas com trabalho local linear. Expandindo:
$$c\,n + c(n-1) + \dots + c = c\,\frac{n(n+1)}{2} \;\Rightarrow\; \Theta(n^2)$$Dobra a cada passo e reduz só 1 — o termo geral é \(2^k\,T(n-k)\).
$$\Rightarrow\; \Theta(2^n)$$Exercícios propostos e referências
- Escrever a busca binária recursiva, extrair a recorrência e resolvê-la pela árvore. Esperado: \(T(n)=T(n/2)+c \Rightarrow \Theta(\log n)\).
- Resolver \(T(n)=T(n-1)+c\,n\) por expansão e dizer que algoritmo conhecido tem esse custo no pior caso.
- Desenhar a árvore de \(T(n)=4T(n/2)+c\,n\) e mostrar que a soma por nível cresce ao descer. Qual a ordem?
- Modificar o Fibonacci recursivo para guardar resultados já computados em um vetor. Qual passa a ser o custo de tempo? E o de espaço?
- Do CLRS: exercícios 2.3-3 e 4.4-1.
Os itens 3 e 4 são os que mais rendem: o primeiro é o caso em que o custo se concentra nas folhas — o que prepara o método mestre; o segundo é a ponte para programação dinâmica.
CLRS, Introduction to Algorithms, 3ª ed., MIT Press, 2009:
- Seção 2.2 — Analyzing algorithms: custo × vezes, melhor e pior caso (pp. 23–27).
- Seção 2.3.2 — Analyzing divide-and-conquer algorithms: a recorrência do merge sort (pp. 34–37).
- Seção 4.4 — The recursion-tree method (pp. 88–92).
- Seção 4.5 — The master method: a próxima aula.
Complementar: Jeff Erickson, Algorithms, capítulo 1 (Recursion), disponível gratuitamente online.
Checklist da A3
Se você consegue responder tudo abaixo sem consultar, a aula está fechada.
| Pergunta | Onde está |
|---|---|
| Montar a tabela custo × vezes de um trecho e chegar em \(T(n)\) | O método: custo × vezes |
Dizer quantas vezes o teste de um for executa, e por que é uma a mais que o corpo | O método: custo × vezes |
| Decidir, num código com vários laços, o que soma e o que multiplica | Dois laços |
| Justificar por que a resposta é \(\Theta\) e não apenas \(O\) | Dois laços |
| Contar as comparações do Max e concluir \(\Theta(n)\) sem olhar o caso médio | Max |
| Enunciar a observação “melhor \(\Omega(f)\) + pior \(O(f)\) ⇒ \(\Theta(f)\)” | Max |
| Exibir a entrada de melhor e a de pior caso do MaxMin2, com a contagem de cada uma | MaxMin1 e MaxMin2 |
| Explicar por que MaxMin1 e MaxMin2 estão na mesma classe mesmo contando diferente | MaxMin1 e MaxMin2 |
| Dizer por que a pesquisa sequencial não tem \(\Theta\) único, e o que sobra afirmar | Pesquisa sequencial |
| Resolver o somatório \(\sum_{i=0}^{n-1}(i+1)\) de um laço duplo triangular | Exercício · laço interno |
| Identificar caso base e caso recursivo em uma função dada | Anatomia |
| Explicar por que contar linha a linha não fecha num algoritmo recursivo | A versão recursiva |
| Desenhar a pilha de \(fat(4)\) e dizer em que fase cada multiplicação acontece | A pilha de execução |
| Extrair a recorrência de um código recursivo, caso base e caso recursivo | Extraindo do código |
| Expandir \(T(n)=T(n-1)+d\), escrever o termo geral e achar o \(k\) do caso base | Resolvendo por expansão |
| Explicar por que dois algoritmos diferentes podem cair na mesma recorrência | Exercício · soma recursiva |
| Enunciar o contrato da árvore: o que é um nó, o que são os filhos, como somar | A árvore de recursão |
| Achar a profundidade resolvendo \(n/2^k = 1\) e contar \(\log_2 n + 1\) níveis | A árvore de recursão |
| Mostrar que em \(2T(n/2)+cn\) todo nível soma \(cn\), e concluir \(\Theta(n\log n)\) | Duas chamadas |
| Conferir a profundidade pelo número de folhas, \(2^{\log_2 n}=n\) | Duas chamadas |
| Dizer as três perguntas que decidem a classe de uma recorrência | Os padrões |
| Escrever a recorrência do Fibonacci e dizer por que a expansão direta não fecha | Fibonacci recursivo |
| Usar \(T(n-2)\le T(n-1)\) para obter o teto, e justificar por que sai \(O\) e não \(\Theta\) | A desigualdade-chave |
| Dar a ordem exata do Fibonacci recursivo e de onde vem o \(\varphi\) | A desigualdade-chave |
| Explicar, pela árvore, por que o custo explode — e o que a recursão deixa de guardar | A árvore entrega o culpado |
| Escrever o Fibonacci iterativo e justificar o \(\Theta(n)\) | A alternativa iterativa |
| Comparar tempo e espaço do fatorial nas duas versões | Complexidade de espaço |
| Dizer por que a recursão do merge sort não é cara em espaço | Complexidade de espaço |
| Classificar \(T(n)=T(n-1)+c\,n\) e dizer que algoritmo tem esse custo no pior caso | Exercício · classifique |
| Não confundir número de chamadas (tempo) com profundidade da pilha (espaço) | Padrões de reconhecimento |