Preciso encontrar o menor elemento k na árvore de pesquisa binária sem usar qualquer variável estática / global. Como conseguir isso de forma eficiente? A solução que tenho em mente é fazer a operação em O (n), o pior caso, já que estou planejando fazer um percurso inordenado de toda a árvore. Mas, no fundo, sinto que não estou usando a propriedade BST aqui. A minha solução presumida está correta ou existe uma melhor disponível?
algorithm
data-structures
binary-tree
binary-search
fanfarrão
fonte
fonte
Respostas:
Aqui está apenas um esboço da ideia:
Em um BST, a subárvore esquerda do nó
T
contém apenas elementos menores do que o valor armazenado emT
. Sek
for menor que o número de elementos na subárvore esquerda, ok
menor elemento deve pertencer à subárvore esquerda. Caso contrário, sek
for maior, ok
menor elemento está na subárvore direita.Podemos aumentar o BST para que cada nó armazene o número de elementos em sua subárvore esquerda (suponha que a subárvore esquerda de um determinado nó inclua esse nó). Com essa informação, é simples atravessar a árvore perguntando repetidamente pelo número de elementos na subárvore esquerda, para decidir se deve recursar para a subárvore esquerda ou direita.
Agora, suponha que estejamos no nó T:
T
.k
menor. Portanto, reduzimos o problema para encontrar ok - num_elements(left subtree of T)
menor elemento da subárvore certa.k
ésimo menor está em algum lugar na subárvore esquerda, então reduzimos o problema para encontrar ok
ésimo elemento na subárvore esquerda.Análise de complexidade:
Isso leva
O(depth of node)
tempo, que éO(log n)
no pior caso em um BST balanceado ou,O(log n)
em média, em um BST aleatório.Um BST requer
O(n)
armazenamento e outroO(n)
para armazenar as informações sobre o número de elementos. Todas as operações BST levamO(depth of node)
tempo, e levaO(depth of node)
tempo extra para manter as informações do "número de elementos" para inserção, exclusão ou rotação de nós. Portanto, o armazenamento de informações sobre o número de elementos na subárvore esquerda mantém a complexidade de espaço e tempo de um BST.fonte
Uma solução mais simples seria fazer um percurso em ordem e acompanhar o elemento a ser impresso no momento (sem imprimi-lo). Quando chegarmos a k, imprima o elemento e pule o resto da travessia da árvore.
fonte
esta é a minha implementação em C # baseada no algoritmo acima apenas pensei em postá-la para que as pessoas entendam melhor que funciona para mim
obrigado IVlad
fonte
Uma solução mais simples seria fazer um percurso em ordem e manter o controle do elemento a ser impresso com um contador k. Quando chegarmos a k, imprima o elemento. O tempo de execução é O (n). Lembre-se de que o tipo de retorno da função não pode ser nulo, ele deve retornar seu valor atualizado de k após cada chamada recursiva. Uma solução melhor para isso seria um BST aumentado com um valor de posição classificado em cada nó.
fonte
// adiciona uma versão java sem recursão
fonte
Você pode usar a travessia interativa iterativa: http://en.wikipedia.org/wiki/Tree_traversal#Iterative_Traversal com uma verificação simples do k-ésimo elemento depois de retirar um nó da pilha.
fonte
Dada apenas uma árvore de pesquisa binária simples, tudo o que você pode fazer é começar do menor e percorrer para cima para encontrar o nó correto.
Se você vai fazer isso com muita frequência, pode adicionar um atributo a cada nó, indicando quantos nós estão em sua subárvore esquerda. Usando isso, você pode descer da árvore diretamente para o nó correto.
fonte
Caminhada recursiva em ordem com um contador
A ideia é semelhante à solução @prasadvk, mas tem algumas deficiências (veja as notas abaixo), então estou postando isso como uma resposta separada.
Observações (e diferenças da solução de @prasadvk):
if( counter == k )
o teste é exigido em três locais: (a) após a subárvore esquerda, (b) após a raiz e (c) após a subárvore direita. Isso é para garantir que o k-ésimo elemento seja detectado em todos os locais , ou seja, independentemente da subárvore em que está localizado.if( result == -1 )
teste necessário para garantir que apenas o elemento de resultado seja impresso , caso contrário, todos os elementos começando do k-ésimo menor até a raiz são impressos.fonte
O(k + d)
, onded
é a profundidade máxima da árvore. Portanto, ele usa uma variável global,counter
mas é ilegal para esta questão.Para árvore de busca não balanceada, leva O (n) .
Para uma árvore de busca balanceada , leva O (k + log n) no pior caso, mas apenas O (k) no sentido amortizado .
Ter e gerenciar o inteiro extra para cada nó: o tamanho da subárvore dá complexidade de tempo O (log n) . Essa árvore de pesquisa equilibrada é normalmente chamada de RankTree.
Em geral, existem soluções (baseadas não em árvore).
Saudações.
fonte
Isso funciona bem: status: é a matriz que contém se o elemento foi encontrado. k: é o kº elemento a ser encontrado. contagem: mantém o controle do número de nós percorridos durante a travessia da árvore.
fonte
Embora esta definitivamente não seja a solução ideal para o problema, é outra solução potencial que pensei que algumas pessoas poderiam achar interessante:
fonte
assinatura:
ligue como:
definição:
fonte
Bem, aqui estão meus 2 centavos ...
fonte
Isso é o que eu pensei e funciona. Ele será executado em o (log n)
fonte
Bem, podemos simplesmente usar o percurso em ordem e colocar o elemento visitado em uma pilha. pop k número de vezes, para obter a resposta.
também podemos parar após k elementos
fonte
Solução para o caso BST completo: -
fonte
O kernel do Linux tem uma excelente estrutura de dados em árvore vermelha e preta aumentada que suporta operações baseadas em classificação em O (log n) em linux / lib / rbtree.c.
Uma porta Java muito simples também pode ser encontrada em http://code.google.com/p/refolding/source/browse/trunk/core/src/main/java/it/unibo/refolding/alg/RbTree.java , junto com RbRoot.java e RbNode.java. O n'ésimo elemento pode ser obtido chamando RbNode.nth (nó RbNode, int n), passando a raiz da árvore.
fonte
Aqui está uma versão concisa em C # que retorna o menor elemento k-th, mas requer a passagem de k como um argumento ref (é a mesma abordagem que @prasadvk):
É O (log n) para encontrar o menor nó e, em seguida, O (k) para atravessar para o k-ésimo nó, então é O (k + log n).
fonte
http://www.geeksforgeeks.org/archives/10379
esta é a resposta exata para esta pergunta: -
1. usando a travessia em ordem no tempo O (n) 2. usando a árvore aumentada no tempo k + log n
fonte
Não consegui encontrar um algoritmo melhor ... então decidi escrever um :) Corrija-me se estiver errado.
}
fonte
Aqui está o código java,
max (raiz do nó, int k) - para encontrar o k-ésimo maior
min (raiz do nó, int k) - para encontrar o k-ésimo menor
fonte
isso funcionaria também. basta chamar a função com maxNode na árvore
def k_largest (self, node, k): if k <0: return None
if k == 0: return node else: k - = 1 return self.k_largest (self.predecessor (node), k)
fonte
Acho que isso é melhor do que a resposta aceita porque não precisa modificar o nó da árvore original para armazenar o número de seus nós filhos.
Precisamos apenas usar o percurso em ordem para contar o menor nó da esquerda para a direita, pare de pesquisar quando a contagem for igual a K.
fonte
A melhor abordagem já existe. Mas eu gostaria de adicionar um código simples para isso
}
fonte
Usando a classe Result auxiliar para rastrear se o nó é encontrado e k atual.
fonte
Complexidade do tempo da solução Python: O (n) Complexidade do espaço: O (1)
A ideia é usar Morris Inorder Traversal
fonte
escrevi uma função bacana para calcular o menor elemento k. I usa travessia em ordem e para quando atinge o k-ésimo menor elemento.
fonte
fonte
fonte
}
fonte