as árvores de busca Binárias suportam três operações principais: inserção de elementos, remoção de elementos, e pesquisa (verificando se uma chave está presente).
SearchingEdit
Searchingedit in a binary search tree for a specific key can be programated recursively or iteratively.
começamos por examinar o nó raiz. Se a árvore é nula, a chave que procuramos não existe na árvore. Caso contrário, se a chave é igual à da raiz, a busca é bem sucedida e nós retornamos o nó. Se a chave for menor que a da raiz, procuramos na subárvore esquerda., Da mesma forma, se a chave é maior do que a da raiz, nós procuramos a subárvore direita. Este processo é repetido até que a chave seja encontrada ou a subárvore restante seja nula. Se a chave procurada não for encontrada após a obtenção de uma sub-árvore nula, então a chave não está presente na árvore. Isso é facilmente expresso como um algoritmo recursivo (implementado em Python):
O mesmo algoritmo pode ser implementado de forma iterativa:
Estes dois exemplos não suportam duplicados, ou seja, eles dependem de ordem em relação a ser uma ordem total.
pode-se notar que o algoritmo recursivo é recursivo de cauda., Em uma linguagem que suporta a otimização de chamadas de cauda, os exemplos recursivos e iterativos Irão compilar para programas equivalentes.
porque no pior caso este algoritmo deve procurar da raiz da árvore para a folha mais distante da raiz, a operação de busca leva tempo proporcional à altura da árvore (ver terminologia da árvore). Em média, as árvores de busca binárias com teclas de nós têm a altura de O (log |nodos|). No entanto, no pior caso, as árvores de busca binárias podem ter a altura de o(|nós|), quando a árvore desequilibrada se assemelha a uma lista ligada (árvore degenerada).,
pesquisar com duplicados permite edit
Se a relação de ordem é apenas uma pré-ordem total, uma extensão razoável da funcionalidade é a seguinte: também em caso de busca de igualdade para as folhas. Assim, permitindo especificar (ou fio duro) uma direção, onde inserir um duplicado, tanto para a direita como para a esquerda de todos os duplicados na árvore até agora. Se a direção estiver conectada, ambas as opções, direita e esquerda, suportam uma pilha com insert duplicate como push operation e delete como a operação pop.,: 155
uma espécie de árvore binária equipada com tal função de busca torna-se estável.
Inserçõesedit
inserção começa como uma busca iria começar; se a chave não é igual à da raiz, nós procuramos as sub-árvores esquerda ou direita como antes. Eventualmente, vamos chegar a um nó externo e adicionar o novo par de valores-chave (aqui codificado como um registro ‘newNode’) como seu filho direito ou esquerdo, dependendo da chave do nó., Em outras palavras, nós examinamos a raiz e recursivamente inserir o novo nó para a subárvore esquerda se sua chave é menor que a da raiz, ou a subárvore direita se sua chave é maior ou igual à raiz.
Aqui está como uma inserção típica de árvore de busca binária pode ser realizada em uma árvore binária em C++:
alternativamente, uma versão não-recursiva pode ser implementada assim., Usando um ponteiro-a-ponteiro para manter o controle de onde viemos permite que o código evite a verificação explícita para e manipulação do caso em que ele precisa inserir um nó na raiz da árvore:
a variante destrutiva acima modifica a árvore no lugar. Ele usa apenas espaço heap constante (e a versão iterativa também usa espaço de pilha constante), mas a versão anterior da árvore é perdida., Alternativamente, como no exemplo Python a seguir, podemos reconstruir todos os antepassados do nó inserido; qualquer referência à raiz da árvore original permanece válida, fazendo da árvore uma estrutura de dados persistente:
a parte que é reconstruída usa o espaço o(log n) No caso médio e O(n) no pior caso.
Em qualquer versão, esta operação requer tempo proporcional à altura da árvore, no pior caso, que é O(log n) no caso médio sobre todas as árvores, mas O(n) no pior caso.,
outra maneira de explicar a inserção é que, a fim de inserir um novo nó na árvore, sua chave é primeiramente comparada com a da raiz. Se a sua chave for menor que a da raiz, é então comparada com a chave do filho esquerdo da raiz. Se a sua chave é maior, é comparada com o filho direito da raiz. Este processo continua, até que o novo nó seja comparado com um nó folha, e então ele é adicionado como o filho direito ou esquerdo deste nó, dependendo de sua chave: se a chave é menor que a chave da folha, então ela é inserida como o filho esquerdo da folha, caso contrário como o filho direito da folha.,
Existem outras formas de inserir nós em uma árvore binária, mas esta é a única maneira de inserir nós nas folhas e ao mesmo tempo preservar a estrutura BST.
DeletionEdit
ao remover um nó de uma árvore de busca binária é obrigatório manter a sequência em ordem dos nós. Há muitas possibilidades de o fazer. No entanto, o método que se segue, proposto por T. Hibbard em 1962, garante que as alturas dos subtrees do sujeito são alteradas no máximo em um., Existem três casos possíveis para considerar:
- excluir um nó sem filhos: simplesmente remover o nó da árvore.
- apagar um nó com um filho: remover o nó e substituí-lo pelo seu filho.
- excluir um nó D com dois filhos: escolha o antecessor em ordem de D ou o sucessor em ordem E (veja figura). Em vez de excluir D, sobreponha a sua chave e valor com e’S. se E não tiver um filho, remova E do seu pai anterior G. se E tiver um filho F, é um filho certo, de modo que é para substituir E no pai de E. ,
apagar um nó com dois filhos de uma árvore de pesquisa binária. Primeiro, o nó mais à esquerda na sub-árvore direita, o sucessor em ordem E, é identificado. Seu valor é copiado para o nó D sendo excluído. O sucessor em ordem pode então ser facilmente excluído porque tem no máximo uma criança. O mesmo método funciona simetricamente usando o predecessor em ordem C.
em todos os casos, quando D acontece ser a raiz, fazer o nó de substituição raiz novamente.nós com dois filhos são mais difíceis de apagar., O sucessor de um nó em ordem é o filho mais esquerdo de sua subárvore direita, e o predecessor de um nó em ordem é o filho mais direito da subárvore esquerda. Em qualquer dos casos, este nó terá apenas uma ou nenhuma criança. Apague-o de acordo com um dos dois casos mais simples acima.
consistentemente usando o sucessor em ordem ou o antecessor em ordem para cada instância do caso de duas crianças pode levar a uma árvore desequilibrada, então algumas implementações selecionam uma ou outra em momentos diferentes.,
Análise de tempo de execução: embora esta operação nem sempre atravesse a árvore até uma folha, esta é sempre uma possibilidade; assim, no pior dos casos, requer tempo proporcional à altura da árvore. Ele não requer mais mesmo quando o nó tem dois filhos, uma vez que ele ainda segue um único caminho e não visita nenhum nó duas vezes.,
TraversalEdit
uma Vez que a árvore de busca binária foi criado, seus elementos podem ser recuperados em ordem, recursivamente percorrer a subárvore esquerda do nó raiz, acessando o próprio nó, em seguida, recursivamente percorrer a subárvore direita do nó, continuando este padrão com cada nó na árvore como é recursivamente acessado. Tal como acontece com todas as árvores binárias, pode-se conduzir uma travessia pré-ordem ou uma travessia pós-ordem, mas nenhuma delas pode ser útil para árvores binárias de busca., Uma passagem em ordem de uma árvore de busca binária sempre resultará em uma lista ordenada de itens de nós (números, strings ou outros itens comparáveis).
o código para a passagem em ordem em Python é dado abaixo. Ele vai chamar de volta (alguma função que o programador deseja chamar no valor do nó, como a impressão para a tela) para cada nó na árvore.
Traversal requer tempo O (n), Uma vez que deve visitar cada nó. Este algoritmo também é O(n), Por isso é assintoticamente ótimo.
Traversal também pode ser implementado iterativamente. = = Ligações externas = = , busca igual maior, busca aproximativa, uma operação para Passo único (iterativo) traversal pode ser muito útil. Isto é, naturalmente, implementado sem a construção de callback e toma O (1) em média e O(log n) no pior caso.
VerificationEdit
às vezes já temos uma árvore binária, e precisamos determinar se ela é uma BST. Este problema tem uma solução recursiva simples.,
a propriedade BST-cada nó na subárvore direita tem que ser maior que o nó atual e cada nó na subárvore esquerda tem que ser menor que o nó atual—é a chave para descobrir se uma árvore é um BST ou não. O algoritmo ganancioso-simplesmente atravessa a árvore, em cada nó verificar se o nó contém um valor maior do que o valor no Filho esquerdo e menor do que o valor no Filho direito—não funciona para todos os casos., Considere o seguinte árvore:
20 / \ 10 30 / \ 5 40
Na árvore acima, cada nó atende a condição de que o nó contém um valor maior do que o esquerdo, criança e menor do que o direito da criança segurar, e ainda não é um BST: o valor 5 é na subárvore direita do nó contendo 20, uma violação do BST propriedade.
em vez de tomar uma decisão baseada apenas nos valores de um nó e seus filhos, também precisamos de informação fluindo para baixo do pai também., No caso da árvore acima, se pudéssemos lembrar sobre o nó contendo o valor 20, veríamos que o nó com o valor 5 está violando o contrato de propriedade BST.,
Assim, a condição precisamos verificar em cada nó é:
- se o nó filho à esquerda de seu pai, então ele deve ser menor do que (ou igual a) o pai e ele deve passar para baixo o valor do pai para a sua subárvore direita para se certificar de que nenhum de nós na subárvore é maior do que o pai
- se o nó é o direito da criança de seu pai, em seguida, ele deve ser maior do que o pai e ele deve passar para baixo o valor do pai para a sua subárvore esquerda para se certificar de que nenhum de nós na subárvore é menor que o pai.,
Uma solução recursiva em C++ pode explicar isso melhor:
node->key+1
e node->key-1
são feitos para permitir apenas elementos distintos de BST.
Se queremos que os mesmos elementos também estejam presentes, então podemos usar apenas node->key
em ambos os lugares.
A chamada inicial para esta função pode ser algo como:
if (isBST(root, INT_MIN, INT_MAX)) { puts("This is a BST.");} else { puts("This is NOT a BST!");}
, Essencialmente, nós mantenha a criação de um intervalo válido (a partir de ) e manter o encolhimento para cada nó medida que desce recursivamente.,
Como indicado na secção #Traversal, um traversal em ordem de uma árvore de pesquisa binária devolve os nós ordenados. Assim, só precisamos manter o último nó visitado enquanto percorre a árvore e verificar se a sua chave é menor (ou menor/igual, se os duplicados devem ser permitidos na árvore) em comparação com a chave atual.
Paralelo algorithmsEdit
Existem também algoritmos paralelos para árvores de busca binária, incluindo a inserção/exclusão de vários elementos, a construção da matriz, filtro com certas predicator, achate-o para uma matriz, fusão/subtrair/a interseção de duas árvores, etc., Estes algoritmos podem ser implementados usando algoritmos de árvore baseados em juntas, que também podem manter a árvore balanceada usando vários esquemas de balanceamento (incluindo árvore AVL, árvore vermelha-negra, árvore balanceada com peso e torap) .