Menu
Coddy logo textTech

Trie (árvore de prefixos)

Última atualização

Um trie (pronuncia-se "trai"), ou árvore de prefixos, armazena um conjunto de strings por seus caracteres: cada aresta é rotulada com um caractere e um caminho a partir da raiz soletra um prefixo. Palavras que compartilham um prefixo compartilham os mesmos nós, então "car", "card" e "care" reutilizam todas o caminho c-a-r. Pressione reproduzir acima para ver as palavras sendo inseridas um caractere por vez, ramificando apenas onde diferem.

Como as buscas percorrem um nó por caractere, procurar uma palavra de comprimento m leva O(m) independentemente de quantas palavras o trie contém. Isso torna os tries ideais para autocompletar, verificação ortográfica e busca por prefixos.

Complexidade de tempo e espaço

OperaçãoComplexidadeNotas
InserirO(m)m = comprimento da palavra
BuscarO(m)Um passo por caractere
Consulta de prefixoO(m)Percorrer até o nó do prefixo
EspaçoO(total chars)Prefixos compartilhados armazenados uma só vez

Passo a passo (inserção)

PassoO que acontece
1Comece no nó raiz.
2Para cada caractere da palavra, procure uma aresta filha correspondente.
3Se existir, siga-a (reutilizando o prefixo compartilhado).
4Se não, crie um novo nó filho para esse caractere.
5Após o último caractere, marque esse nó como fim de palavra.

Exemplo resolvido

Inserindo ["car", "card", "care"] em um trie vazio:

PassoEstruturaAção
Inserir carroot → c → a → r✓Não existem filhos correspondentes, então crie c, a, r e marque r como fim de palavra.
Inserir cardroot → c → a → r✓ → d✓Reutilize o caminho existente c-a-r, depois crie um novo filho d e marque-o como fim de palavra.
Inserir careroot → c → a → r✓ → {d✓, e✓}Reutilize c-a-r, ramifique a partir de r com um novo filho e e marque e como fim de palavra.
Buscar careroot → c → a → r → e✓Percorra c, a, r, e; o nó final está marcado como fim de palavra, então care está presente.
Buscar caroot → c → aO caminho existe, mas a não está marcado como fim de palavra, então ca é um prefixo, mas não uma palavra armazenada.

Quando usar um trie

Use quandoEvite quando
Você precisa de consultas por prefixo ou autocompletar sobre um conjunto de strings.Você só precisa de buscas de chaves completas - uma tabela hash é mais rápida e leve.
Muitas palavras armazenadas compartilham prefixos comuns, então os nós são reutilizados.As chaves são longas e raramente se sobrepõem, desperdiçando um nó por caractere.
Você quer que as chaves sejam retornadas em ordem por meio de um percurso.A memória é escassa - os ponteiros para filhos por nó adicionam uma sobrecarga significativa.
O custo de busca deve depender do comprimento da chave, não do tamanho do conjunto de dados.O alfabeto é enorme (por exemplo, todo o Unicode) e os filhos são armazenados de forma densa.

Código de Trie (Prefix Tree)

Uma implementação limpa e executável de Trie (Prefix Tree) em Python, JavaScript, Java, C++, C. Escolha uma linguagem, copie o código ou abra-o já carregado no Playground da Coddy.

Código de Trie (Prefix Tree) em Python

Python
1class TrieNode:2    def __init__(self):3        self.children = {}4        self.is_word = False5
6
7class Trie:8    def __init__(self):9        self.root = TrieNode()10
11    def insert(self, word):12        node = self.root13        for ch in word:14            node = node.children.setdefault(ch, TrieNode())15        node.is_word = True16
17    def search(self, word):18        node = self._walk(word)19        return node is not None and node.is_word20
21    def starts_with(self, prefix):22        return self._walk(prefix) is not None23
24    def _walk(self, s):25        node = self.root26        for ch in s:27            if ch not in node.children:28                return None29            node = node.children[ch]30        return node31
32
33trie = Trie()34for word in ["car", "card", "care", "dog"]:35    trie.insert(word)36
37print("search(card):     ", trie.search("card"))38print("search(ca):       ", trie.search("ca"))39print("starts_with(ca):  ", trie.starts_with("ca"))40print("starts_with(do):  ", trie.starts_with("do"))41print("starts_with(cat): ", trie.starts_with("cat"))
Execute este código no Playground de Python

Perguntas frequentes sobre tries

Para que serve um trie?
Tries impulsionam recursos baseados em prefixos: autocompletar e sugestões de busca, verificadores ortográficos, tabelas de roteamento IP e consultas de dicionário. Onde quer que você precise responder rápido a "alguma palavra armazenada começa com este prefixo?", um trie se destaca.
Qual é a complexidade de tempo de um trie?
Inserção, busca e consultas de prefixo rodam todas em O(m), onde m é o comprimento da palavra ou prefixo - independentemente de quantas palavras estão armazenadas. O trade-off é a memória: um trie pode usar muito espaço, embora os prefixos compartilhados sejam armazenados apenas uma vez.
Qual é a diferença entre um trie e uma tabela hash?
Uma tabela hash oferece busca média em O(1) de chaves completas, mas não consegue responder consultas de prefixo. Um trie é um pouco mais lento por busca, mas suporta naturalmente busca por prefixo, percurso ordenado e autocompletar - a razão pela qual é preferido nesses casos.
Quando devo usar um trie em vez de uma árvore binária de busca?
Use um trie quando suas chaves forem strings e você precisar de buscas por prefixo: ele roda em O(m) por operação sobre o comprimento da chave, enquanto uma BST balanceada custa O(m log n) porque cada comparação percorre a string e há log n delas. Uma BST é a melhor escolha quando as chaves não são do tipo string ou quando a sobrecarga de memória importa mais que a velocidade de prefixos.
Como se trata o fim de uma palavra em um trie?
Cada nó carrega uma marca de fim de palavra que é ativada apenas quando uma palavra completa inserida termina ali. Sem ela você não conseguiria distinguir uma palavra armazenada de um mero prefixo - por exemplo, após inserir card, o nó de car existe, mas só deveria ser reportado como palavra se car também tiver sido inserido.
Tries sempre economizam memória ao compartilhar prefixos?
Nem sempre. O compartilhamento ajuda apenas quando muitas chaves se sobrepõem; com chaves longas e distintas, um trie pode usar muito mais memória que um conjunto hash, já que cada caractere vira seu próprio nó com ponteiros para filhos. Se o espaço é a preocupação, uma árvore radix comprimida funde as cadeias de filho único para reduzir essa sobrecarga.
Coddy programming languages illustration

Domine algoritmos com a Coddy

COMEÇAR