Quais são as aplicações das árvores binárias?

323

Eu estou querendo saber quais são as aplicações específicas das árvores binárias. Você poderia dar alguns exemplos reais?

Jichao
fonte

Respostas:

425

Discutir sobre o desempenho de árvores binárias não faz sentido - elas não são uma estrutura de dados, mas uma família de estruturas de dados, todas com características de desempenho diferentes. Embora seja verdade que as árvores binárias desequilibradas tenham um desempenho muito pior do que as árvores binárias com auto balanceamento para pesquisa, existem muitas árvores binárias (como tentativas binárias) para as quais "balancear" não tem significado.

Aplicações de árvores binárias

  • Árvore de pesquisa binária - usada em muitos aplicativos de pesquisa em que os dados estão constantemente entrando / saindo, como os objetos mape setnas bibliotecas de vários idiomas.
  • Partição de espaço binário - usada em quase todos os videogames 3D para determinar quais objetos precisam ser renderizados.
  • Tentativas binárias - Usado em quase todos os roteadores de alta largura de banda para armazenar tabelas de roteadores.
  • Árvores de hash - usadas em programas p2p e assinaturas de imagem especializadas nas quais um hash precisa ser verificado, mas o arquivo inteiro não está disponível.
  • Heaps - Usado na implementação de filas prioritárias eficientes, que por sua vez são usadas para agendar processos em muitos sistemas operacionais, qualidade de serviço em roteadores e A * (algoritmo de localização de caminho usado em aplicativos de IA, incluindo robótica e videogames) . Também usado em heap-sort.
  • Árvore de codificação Huffman ( Chip Uni ) - usada em algoritmos de compactação, como os usados ​​nos formatos de arquivo .jpeg e .mp3.
  • Árvores GGM - Usado em aplicativos criptográficos para gerar uma árvore de números pseudo-aleatórios.
  • Árvore de sintaxe - Construída por compiladores e (implicitamente) calculadoras para analisar expressões.
  • Treap - Estrutura de dados randomizada usada em redes sem fio e alocação de memória.
  • Árvore T - Embora a maioria dos bancos de dados use alguma forma de árvore B para armazenar dados na unidade, os bancos de dados que mantêm todos (a maioria) seus dados na memória geralmente usam árvores T para fazer isso.

A razão pela qual as árvores binárias são usadas com mais frequência do que as árvores n-árias para pesquisa é que as árvores n-árias são mais complexas, mas geralmente não oferecem nenhuma vantagem real de velocidade.

Em uma árvore binária (equilibrada) com mnós, mover de um nível para o próximo requer uma comparação, e há log_2(m)níveis, para um total de log_2(m)comparações.

Por outro lado, uma árvore n-ária exigirá log_2(n)comparações (usando uma pesquisa binária) para passar para o próximo nível. Como existem log_n(m)níveis totais, a pesquisa exigirá log_2(n)*log_n(m)= log_2(m)total de comparações. Portanto, embora as árvores n-árias sejam mais complexas, elas não fornecem vantagem em termos de comparações totais necessárias.

(No entanto, as árvores n-árias ainda são úteis em situações de nicho. Os exemplos que vêm imediatamente à mente são árvores quádruplas e outras árvores de particionamento de espaço, onde a divisão de espaço usando apenas dois nós por nível tornaria a lógica desnecessariamente complexa; e Árvores B usadas em muitos bancos de dados, onde o fator limitante não é quantas comparações são feitas em cada nível, mas quantos nós podem ser carregados a partir do disco rígido de uma só vez)

BlueRaja - Danny Pflughoeft
fonte
3
> Treap - Estrutura aleatória de dados usada em redes sem fio e alocação de memória. Como exatamente eles são usados ​​na alocação de memória e na rede sem fio?
FRP
1
Existem muitas estruturas de dados e algoritmos úteis que fazem uso da palavra "binário", e "árvore binária de pesquisa" é de fato uma delas, mas essa não é a pergunta que foi feita. Para que serve uma "árvore binária" antiga e simples, não uma classificada, nem equilibrada, nem completa. Apenas uma velha árvore aleatória simples?
Michael Erickson
4
@MichaelErickson Você leu a resposta? Porque essa é exatamente a pergunta que eu respondi.
BlueRaja - Danny Pflughoeft
1
Acredito hash árvores são comumente chamados de Merkle Trees, pelo menos dentro das comunidades bitcoin e ethereum, IPFS, etc.
Duke
1
Pena que esta resposta contém muitos erros. árvores n-árias em hardware moderno são quase sempre preferíveis a árvores binárias. Os aplicativos nomeados principalmente não usam árvores binárias.
Stephan Eggermont
290

Quando muitas pessoas falam sobre árvores binárias, geralmente pensam em pesquisa binária árvores de , por isso vou abordar isso primeiro.

Uma árvore de pesquisa binária não balanceada é realmente útil para pouco mais do que educar os alunos sobre estruturas de dados. Isso ocorre porque, a menos que os dados cheguem em uma ordem relativamente aleatória, a árvore pode degenerar facilmente em sua pior forma, que é uma lista vinculada, pois as árvores binárias simples não são equilibradas.

Um bom exemplo disso: certa vez, tive que consertar algum software que carregava seus dados em uma árvore binária para manipulação e pesquisa. Ele escreveu os dados em forma ordenada:

Alice
Bob
Chloe
David
Edwina
Frank

de modo que, ao lê-lo novamente, acabou com a seguinte árvore:

  Alice
 /     \
=       Bob
       /   \
      =     Chloe
           /     \
          =       David
                 /     \
                =       Edwina
                       /      \
                      =        Frank
                              /     \
                             =       =

qual é a forma degenerada. Se você procurar Frank nessa árvore, precisará procurar todos os seis nós antes de encontrá-lo.

As árvores binárias tornam-se realmente úteis para pesquisar quando você as equilibra. Isso envolve a rotação de subárvores através do nó raiz, para que a diferença de altura entre duas subárvores seja menor ou igual a 1. Adicionar esses nomes acima de um de cada vez em uma árvore equilibrada forneceria a seguinte sequência:

1.   Alice
    /     \
   =       =

 

2.   Alice
    /     \
   =       Bob
          /   \
         =     =

 

3.        Bob
        _/   \_
   Alice       Chloe
  /     \     /     \
 =       =   =       =

 

4.        Bob
        _/   \_
   Alice       Chloe
  /     \     /     \
 =       =   =       David
                    /     \
                   =       =

 

5.           Bob
        ____/   \____
   Alice             David
  /     \           /     \
 =       =     Chloe       Edwina
              /     \     /      \
             =       =   =        =

 

6.              Chloe
            ___/     \___
         Bob             Edwina
        /   \           /      \
   Alice     =      David        Frank
  /     \          /     \      /     \
 =       =        =       =    =       =

Você pode realmente ver subárvores inteiras girando para a esquerda (nas etapas 3 e 6) à medida que as entradas são adicionadas e isso fornece uma árvore binária equilibrada na qual a pesquisa de pior caso é O(log N)e não a O(N) que a forma degenerada fornece. Em nenhum momento o NULL mais alto ( =) difere do mais baixo em mais de um nível. E, na árvore final acima, você pode encontrar Frank apenas olhando para três nós ( Chloe, Edwinae, finalmente,Frank ).

Obviamente, eles podem se tornar ainda mais úteis quando você os torna árvores de várias vias equilibradas, em vez de árvores binárias. Isso significa que cada nó contém mais de um item (tecnicamente, eles contêm N itens e N + 1 ponteiros, uma árvore binária é um caso especial de uma árvore de várias vias unidirecional com 1 item e 2 ponteiros).

Com uma árvore de três vias, você acaba com:

  Alice Bob Chloe
 /     |   |     \
=      =   =      David Edwina Frank
                 /     |      |     \
                =      =      =      =

Isso geralmente é usado na manutenção de chaves para um índice de itens. Escrevi um software de banco de dados otimizado para o hardware em que um nó é exatamente do tamanho de um bloco de disco (digamos, 512 bytes) e você coloca o máximo de chaves possível em um único nó. Os ponteiros nesse caso eram, na verdade, números de registro em um arquivo de acesso direto de registro de comprimento fixo separado do arquivo de índice (para que o número do registro Xpudesse ser encontrado, basta procurar X * record_length).

Por exemplo, se os ponteiros tiverem 4 bytes e o tamanho da chave for 10, o número de chaves em um nó de 512 bytes será 36. São 36 chaves (360 bytes) e 37 ponteiros (148 bytes) para um total de 508 bytes com 4 bytes desperdiçados por nó.

O uso de chaves multidirecionais introduz a complexidade de uma pesquisa bifásica (pesquisa multidirecional para encontrar o nó correto combinado com uma pequena pesquisa seqüencial (ou binária linear) para encontrar a chave correta no nó), mas a vantagem de fazendo menos E / S de disco mais do que compensa isso.

Não vejo razão para fazer isso em uma estrutura na memória; é melhor você ficar com uma árvore binária equilibrada e manter seu código simples.

Lembre-se também de que as vantagens de O(log N)mais deO(N) não aparecem quando os conjuntos de dados são pequenos. Se você estiver usando uma árvore de múltiplas vias para armazenar as quinze pessoas em seu catálogo de endereços, provavelmente será um exagero. As vantagens surgem quando você armazena algo como todos os pedidos de seus cem mil clientes nos últimos dez anos.

O ponto principal da notação big-O é indicar o que acontece à medida que o Ninfinito se aproxima. Algumas pessoas podem discordar, mas não há problema em usar a classificação por bolha, se você tiver certeza de que os conjuntos de dados permanecerão abaixo de um determinado tamanho, desde que nada mais esteja disponível :-)


Quanto a outros usos para árvores binárias, existem muitos, como:

  • Montes binários em que as teclas mais altas estão acima ou iguais às mais baixas, e não à esquerda de (ou abaixo ou igual a e à direita);
  • Árvores de hash, semelhantes a tabelas de hash;
  • Árvores sintáticas abstratas para compilação de linguagens de computador;
  • Árvores de Huffman para compactação de dados;
  • Roteando árvores para o tráfego de rede.

Dada a quantidade de explicações geradas para as árvores de pesquisa, sou reticente em entrar em muitos detalhes sobre as outras, mas isso deve ser suficiente para pesquisá-las, se você desejar.

paxdiablo
fonte
28
+1 Para uma resposta escrita por escrito; além de me apresentar árvores equilibradas de múltiplas vias, algo que nunca encontrei antes.
Tony
3
Não concordo com a sua afirmação de que eles são úteis apenas para educar os alunos. Eles são bastante úteis, mesmo como uma estrutura de dados estática simples. No entanto, esta é uma resposta muito bem escrita e ilustrada; portanto, marque com +1 o resto. :-)
Benson
1
No hardware moderno, quase todas as árvores devem ser de várias vias.
Stephan Eggermont
89

A organização do código Morse é uma árvore binária.

árvore binária

Código Morse

IliasT
fonte
4
Esta é a minha resposta favorita. Ilustra diretamente a redução na complexidade computacional necessária para alcançar os caracteres mais adiante na lista.
Bigode
7
Eu realmente gostei desta resposta!
Duncan Edwards
2
Eu costumava trabalhar para uma empresa que fez filtros para rádio amador, isso leva-me 'caminho de volta' ...
JosephDoggie
62

Uma árvore binária é uma estrutura de dados de árvore na qual cada nó tem no máximo dois nós filhos, geralmente distinguidos como "esquerdo" e "direito". Nós com filhos são nós pais e nós filhos podem conter referências a seus pais. Fora da árvore, geralmente há uma referência ao nó "raiz" (o ancestral de todos os nós), se existir. Qualquer nó na estrutura de dados pode ser alcançado iniciando no nó raiz e seguindo repetidamente as referências ao filho esquerdo ou direito. Em uma árvore binária, um grau de cada nó é no máximo dois.

Árvore binária

As árvores binárias são úteis, porque, como você pode ver na figura, se quiser encontrar algum nó na árvore, basta procurar no máximo 6 vezes. Se você quisesse procurar o nó 24, por exemplo, começaria pela raiz.

  • A raiz tem um valor 31, que é maior que 24, então você vai para o nó esquerdo.
  • O nó esquerdo tem um valor 15, que é menor que 24, então você vai para o nó direito.
  • O nó direito tem um valor 23, que é menor que 24, então você vai para o nó direito.
  • O nó direito tem um valor 27, que é maior que 24, então você vai para o nó esquerdo.
  • O nó esquerdo tem um valor de 25, que é maior que 24, então você vai para o nó esquerdo.
  • O nó tem um valor de 24, que é a chave que estamos procurando.

Esta pesquisa é ilustrada abaixo: Pesquisa em árvore

Você pode ver que é possível excluir metade dos nós da árvore inteira na primeira passagem. e metade da subárvore esquerda no segundo. Isso contribui para pesquisas muito eficazes. Se isso fosse feito em 4 bilhões de elementos, você teria que pesquisar no máximo 32 vezes. Portanto, quanto mais elementos contidos na árvore, mais eficiente poderá ser sua pesquisa.

As exclusões podem se tornar complexas. Se o nó tiver 0 ou 1 filho, basta mover alguns ponteiros para excluir o que será excluído. No entanto, você não pode excluir facilmente um nó com 2 filhos. Então tomamos um atalho. Digamos que desejássemos excluir o nó 19.

Excluir 1

Como não é fácil tentar determinar para onde mover os ponteiros esquerdo e direito, encontramos um para substituí-lo. Nós vamos para a subárvore esquerda e vamos o mais à direita possível. Isso nos dá o próximo maior valor do nó que queremos excluir.

Excluir 3

Agora, copiamos todo o conteúdo dos anos 18, exceto os ponteiros esquerdo e direito, e excluímos o nó 18 original.

Excluir 4


Para criar essas imagens, implementei uma árvore AVL, uma árvore auto balanceada, para que, a qualquer momento, a árvore tenha no máximo um nível de diferença entre os nós das folhas (nós sem filhos). Isso evita que a árvore se incline e mantém o O(log n)tempo máximo de pesquisa, com o custo de um pouco mais de tempo necessário para inserções e exclusões.

Aqui está um exemplo mostrando como minha árvore AVL se manteve o mais compacta e equilibrada possível.

insira a descrição da imagem aqui

Em uma matriz classificada, as pesquisas ainda seriam necessárias O(log(n)), como uma árvore, mas a inserção e remoção aleatórias levariam O (n) em vez da árvore O(log(n)). Alguns contêineres da STL usam essas características de desempenho para sua vantagem, de modo que os tempos de inserção e remoção levam no máximo O(log n), o que é muito rápido. Alguns destes recipientes s map, multimap, set, e multiset.

O código de exemplo para uma árvore AVL pode ser encontrado em http://ideone.com/MheW8

Drise
fonte
5
Você só precisa pesquisar em O (log n) se estiver lidando com uma árvore de pesquisa binária (que seja bem equilibrada). As árvores binárias arbitrárias não têm restrições de ordem e um BST aleatório possui complexidade de pesquisa em O (log h ).
26412 dlev
Não são do tipo armazenado nos contêineres padrão relevantes.
Filhote de cachorro
12

A principal aplicação são as árvores de pesquisa binária . Essa é uma estrutura de dados na qual a pesquisa, inserção e remoção são muito rápidas (sobre log(n)operações)

BlueRaja - Danny Pflughoeft
fonte
1
As árvores de pesquisa binária não são um aplicativo, mas são um tipo específico de árvore binária.
N28 28/02
@ Nbro: Você está discutindo semântica inútil, essas são as duas formas válidas de dizer a mesma coisa. Observe que "aplicativo" aqui não significa a mesma coisa que "aplicativo de computador"
BlueRaja - Danny Pflughoeft
Eu acho que a pergunta estava mais relacionada a aplicativos do mundo real e não a implementações específicas ou tipos específicos de árvores binárias. E, o questionador não está perguntando quais estruturas de dados são árvores binárias específicas. Não é inútil, IMO. Mas eu concordo que é ambíguo de qualquer maneira. Por exemplo, em sua outra resposta, você menciona árvores de sintaxe, que é uma aplicação da estrutura de dados da árvore (mas não necessariamente binária ) em um aplicativo real. Com base no seu raciocínio, eu poderia enumerar todas as árvores binárias que conheço, todos ficaríamos felizes por causa da quantidade de itens.
nbro
11
  • Árvores binárias são usadas na codificação Huffman , que são usadas como um código de compactação.
  • As árvores binárias são usadas nas árvores de pesquisa binária , que são úteis para manter registros de dados sem muito espaço extra.
Chip Uni
fonte
11

Um exemplo interessante de uma árvore binária que não foi mencionada é o de uma expressão matemática avaliada recursivamente. É basicamente inútil do ponto de vista prático, mas é uma maneira interessante de pensar em tais expressões.

Basicamente, cada nó da árvore tem um valor que é inerente a si mesmo ou é avaliado recursivamente, operando os valores de seus filhos.

Por exemplo, a expressão (1+3)*2pode ser expressa como:

    *
   / \
  +   2
 / \
1   3

Para avaliar a expressão, solicitamos o valor do pai. Este nó, por sua vez, obtém seus valores de seus filhos, um operador positivo e um nó que simplesmente contém '2'. O operador plus, por sua vez, obtém seus valores de filhos com os valores '1' e '3' e os adiciona, retornando 4 ao nó de multiplicação que retorna 8.

Esse uso de uma árvore binária é semelhante a inverter a notação de polimento em certo sentido, pois a ordem na qual as operações são executadas é idêntica. Também é importante notar que ela não precisa necessariamente ser uma árvore binária, é apenas que os operadores mais usados ​​são binários. No nível mais básico, a árvore binária aqui é de fato apenas uma linguagem de programação puramente funcional muito simples.

Sem noção
fonte
9

Eu não acho que exista alguma utilidade para árvores binárias "puras". (exceto para fins educacionais) As árvores binárias balanceadas, como as árvores Vermelho-Preto ou AVL, são muito mais úteis, pois garantem operações O (logn). Árvores binárias normais podem acabar sendo uma lista (ou quase lista) e não são realmente úteis em aplicativos que usam muitos dados.

Árvores balanceadas são frequentemente usadas para implementar mapas ou conjuntos. Eles também podem ser usados ​​para classificação em O (nlogn), mesmo que existam maneiras melhores de fazê-lo.

Também para pesquisar / inserir / excluir tabelas Hash, podem ser usadas, que geralmente têm melhor desempenho do que as árvores de pesquisa binária (equilibradas ou não).

Uma aplicação em que árvores de pesquisa binárias (balanceadas) seriam úteis seria se fosse necessário pesquisar / inserir / excluir e classificar. A classificação pode estar no local (quase, ignorando o espaço de pilha necessário para a recursão), dada uma árvore balanceada de construção pronta. Ainda seria O (nlogn), mas com um fator constante menor e sem espaço extra necessário (exceto para a nova matriz, assumindo que os dados devam ser colocados em uma matriz). As tabelas de hash, por outro lado, não podem ser classificadas (pelo menos não diretamente).

Talvez eles também sejam úteis em alguns algoritmos sofisticados para fazer alguma coisa, mas nada me vem à mente. Se eu encontrar mais, editarei minha postagem.

Outras árvores, como as árvores fe B +, são amplamente usadas em bancos de dados

George
fonte
9

Uma das aplicações mais comuns é armazenar dados de forma eficiente, de forma classificada, para acessar e pesquisar rapidamente os elementos armazenados. Por exemplo, std::mapoustd::set na biblioteca padrão do C ++.

A árvore binária como estrutura de dados é útil para várias implementações de analisadores e solucionadores de expressões.

Também pode ser usado para resolver alguns problemas do banco de dados, por exemplo, indexação.

Geralmente, árvore binária é um conceito geral de estrutura de dados baseada em árvore específica e vários tipos específicos de árvores binárias podem ser construídos com propriedades diferentes.

mloskot
fonte
7

No C ++ STL e em muitas outras bibliotecas padrão em outras linguagens, como Java e C #. As árvores de pesquisa binária são usadas para implementar o conjunto e o mapa.

Yin Zhu
fonte
2
na verdade, em conjuntos / mapas em C ++ geralmente são baseados em árvores vermelho-pretas, que é uma árvore de pesquisa binária com mais algumas restrições.
Idan K
6

Uma das aplicações mais importantes das árvores binárias são as árvores de pesquisa binária equilibrada , como:

Esses tipos de árvores têm a propriedade de que a diferença nas alturas da subárvore esquerda e da subárvore direita é mantida pequena executando operações como rotações cada vez que um nó é inserido ou excluído.

Devido a isso, a altura geral da árvore permanece na ordem do log n e as operações como busca, inserção e exclusão dos nós são executadas no tempo O (log n). O STL do C ++ também implementa essas árvores na forma de conjuntos e mapas.

rohit
fonte
5

Eles podem ser usados ​​como uma maneira rápida de classificar dados. Inserir dados em uma árvore de pesquisa binária em O (log (n)). Em seguida, atravesse a árvore para classificá-las.

Aaron
fonte
2

a sintaxe de seus programas ou muitas outras coisas, como linguagens naturais, podem ser analisadas usando a árvore binária (embora não necessariamente).

Anycorn
fonte
2

Implementações de java.util.Set

romano
fonte
2

No hardware moderno, uma árvore binária é quase sempre abaixo do ideal devido ao mau comportamento do cache e do espaço. Isso também vale para as variantes (semi) balanceadas. Se você os encontrar, é onde o desempenho não conta (ou é dominado pela função de comparação) ou é mais provável por razões históricas ou de ignorância.

Stephan Eggermont
fonte
2
Subótimo em comparação com o que?
árvores de várias vias. Busca linear através de todos os dados que você começa a partir de um acesso à memória é muito mais rápido do que fazer um novo acesso de memória principal
Stephan Eggermont
Quero acreditar em você, mas não há nada na sua resposta que respalde suas reivindicações. Fontes, notação grande ou algo assim. Por favor elabore.
PhilT
@PhilT pt.wikipedia.org/wiki/CPU_cache
Stephan Eggermont
0

Um compilador que usa uma árvore binária para a representação de um AST, pode usar algoritmos conhecidos para analisar a árvore como postorder, inorder. O programador não precisa criar seu próprio algoritmo. Como uma árvore binária para um arquivo de origem é maior que a árvore n-ária, sua construção leva mais tempo. Veja esta produção: selstmnt: = "if" "(" expr ")" stmnt "ELSE" stmnt Em uma árvore binária, ela terá 3 níveis de nós, mas a árvore n-ária terá 1 nível (de chids)

É por isso que os sistemas operacionais baseados em Unix são lentos.

evenhorizon
fonte