Eu estou querendo saber quais são as aplicações específicas das árvores binárias. Você poderia dar alguns exemplos reais?
fonte
Eu estou querendo saber quais são as aplicações específicas das árvores binárias. Você poderia dar alguns exemplos reais?
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.
map
e set
nas bibliotecas de vários idiomas.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 m
nó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)
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:
de modo que, ao lê-lo novamente, acabou com a seguinte árvore:
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:
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 aO(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
,Edwina
e, 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:
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
X
pudesse ser encontrado, basta procurarX * 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
N
infinito 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:
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.
fonte
A organização do código Morse é uma árvore binária.
fonte
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.
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.
Esta pesquisa é ilustrada abaixo:
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.
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.
Agora, copiamos todo o conteúdo dos anos 18, exceto os ponteiros esquerdo e direito, e excluímos o nó 18 original.
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.
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 árvoreO(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áximoO(log n)
, o que é muito rápido. Alguns destes recipientes smap
,multimap
,set
, emultiset
.O código de exemplo para uma árvore AVL pode ser encontrado em http://ideone.com/MheW8
fonte
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)fonte
fonte
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)*2
pode ser expressa como: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.
fonte
Aplicações da árvore binária:
fonte
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
fonte
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::map
oustd::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.
fonte
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.
fonte
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.
fonte
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.
fonte
a sintaxe de seus programas ou muitas outras coisas, como linguagens naturais, podem ser analisadas usando a árvore binária (embora não necessariamente).
fonte
Implementações de
java.util.Set
fonte
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.
fonte
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.
fonte