Padrão 5: Trie (árvore de prefixos)
A Trie é uma árvore especializada em texto: cada aresta é um caractere, e cada caminho da raiz para baixo soletra um prefixo. Ela torna "esta palavra existe?" e "alguma palavra começa com este prefixo?" muito eficientes — e é a base de autocompletar.
Tente primeiro: Implement Trie (Prefix Tree)
Antes de ler a teoria, tente resolver. Pode começar pela força bruta, pode errar — a ideia é sentir o problema. A teoria vem logo abaixo.
Implemente uma Trie (árvore de prefixos) com insert, search (palavra exata) e startsWith (prefixo). Para testar, você recebe duas listas: operacoes (ex.: "Trie", "insert", "search", "startsWith") e args (o argumento de cada operação). Retorne a lista de resultados (None para construir/inserir, true/false para as buscas).
Exemplo: ops ["Trie","insert","search","search","startsWith"], args [[],["apple"],["apple"],["app"],["app"]] → [null,null,true,false,true].
Tentou? Guardar as palavras numa lista e procurar uma a uma é O(n·m) por busca. A teoria abaixo mostra como a estrutura de árvore por caractere torna cada busca proporcional só ao tamanho da palavra.
A teoria: caminhos que soletram palavras
Numa Trie, cada nó representa um prefixo, e seus filhos são os próximos caracteres possíveis. Palavras que compartilham um prefixo compartilham o mesmo caminho — daí a eficiência e a economia de espaço.
inserindo "casa", "caso", "cair":
(raiz)
│ c
c
│ a
a
/ \
s/ \i
s i
/ \ \
a o r
(casa)(caso)(cair)
Cada nó precisa de duas coisas:
- um mapa de filhos (
{caractere: próximo nó}); - uma flag fim de palavra, marcando que algum caminho termina ali (para distinguir uma palavra completa de um simples prefixo).
As três operações seguem o mesmo caminhar letra a letra:
insert(palavra): caminhe, criando nós que faltam; marque o último como fim.
search(palavra): caminhe; existe o caminho inteiro E o último é fim?
startsWith(pref): caminhe; existe o caminho inteiro? (não precisa ser fim)
A diferença entre search e startsWith é só a flag de fim — essa flag é o
que separa "a palavra cas existe?" de "existe palavra que começa com
cas?".
Por que a Trie é eficiente?
Cada operação (insert/search/startsWith) caminha um nó por caractere da palavra/prefixo → O(L), onde L é o comprimento — independente de quantas palavras já estão guardadas! Comparar com uma lista de palavras seria O(n·L) por busca. O custo da Trie é o espaço: nós para cada caractere distinto dos prefixos (mitigado pelo compartilhamento de prefixos comuns).
Onde a Trie brilha
Autocompletar, corretor ortográfico, busca por prefixo, e problemas que casam muitas palavras de uma vez contra um texto/grid (como o Word Search II, que combina Trie + DFS no grid da seção Matriz — Trie para checar muitos prefixos ao mesmo tempo). Sempre que o problema for sobre prefixos ou um dicionário de palavras, pense em Trie.
🧠 Quiz de fixação
0/41. O que cada nó de uma Trie representa, e o que ele guarda?
2. Qual a ÚNICA diferença entre search(palavra) e startsWith(prefixo)?
3. Por que cada operação da Trie é O(L) (L = tamanho da palavra)?
4. Por que palavras que compartilham um prefixo economizam espaço numa Trie?
Resumo
- Trie: árvore onde cada aresta é um caractere; um caminho soletra um prefixo.
- Cada nó: mapa de filhos + flag fim de palavra (separa palavra de prefixo).
- insert/search/startsWith caminham letra a letra: O(L), independente do número de palavras.
- Brilha em autocompletar, busca por prefixo e casar muitas palavras (Word Search II).
Última parada: a síntese da seção Árvores e o quiz antes da prova.