Como a construção de um heap pode ser uma complexidade de tempo O (n)?

494

Alguém pode ajudar a explicar como a construção de um heap pode ser de O (n) complexidade?

A inserção de um item em um heap é O(log n)e a inserção é repetida n / 2 vezes (o restante são folhas e não podem violar a propriedade heap). Então, isso significa que a complexidade deveria ser O(n log n), eu acho.

Em outras palavras, para cada item que "heapizamos", é possível filtrar uma vez para cada nível do heap até agora (que é o nível de log n).

o que estou perdendo?

GBa
fonte
o que exatamente você quer dizer com "construir" um monte?
Mfrankli
Como seria em um heapsort, tomar uma matriz não triados e filterdown cada um dos elementos de topo meio até que ele esteja em conformidade com as regras de um montão
GBA
2
A única coisa que pude encontrar foi este link: A complexidade do Buildheap parece ser Θ (n lg n) - n chamadas para o Heapify a um custo de Θ (lg n) por chamada, mas esse resultado pode ser aprimorado para Θ (n) cs.txstate.edu/~ch04/webtest/teaching/courses/5329/lectures/…
GBa
2
@Gba assistir a este vídeo do MIT: Ele explica bem sobre a forma como obtemos O (n), com um pouco lil de matemática youtube.com/watch?v=B7hVxCmfPtM
CodeShadow
2
Link direto para a explicação que o @CodeShadow mencionou: youtu.be/B7hVxCmfPtM?t=41m21s
sha1:

Respostas:

435

Eu acho que há várias perguntas enterradas neste tópico:

  • Como você implementa buildHeappara que seja executado em O (n) tempo?
  • Como você mostra que buildHeapé executado no tempo O (n) quando implementado corretamente?
  • Por que essa mesma lógica não funciona para fazer a classificação de heap ser executada em O (n) tempo em vez de O (n log n) ?

Como você implementa buildHeappara que seja executado em O (n) tempo?

Freqüentemente, as respostas a essas perguntas concentram-se na diferença entre siftUpe siftDown. Fazer a escolha correta entre siftUpe siftDowné essencial para obter o desempenho de O (n)buildHeap , mas não ajuda a entender a diferença entre buildHeape heapSortem geral. De fato, as implementações adequadas de ambos buildHeape heapSortserá única utilizar siftDown. A siftUpoperação é necessária apenas para executar inserções em um heap existente, portanto, ela seria usada para implementar uma fila de prioridade usando um heap binário, por exemplo.

Eu escrevi isso para descrever como um heap máximo funciona. Esse é o tipo de heap normalmente usado para classificação de heap ou para uma fila de prioridade em que valores mais altos indicam prioridade mais alta. Uma pilha mínima também é útil; por exemplo, ao recuperar itens com chaves inteiras em ordem crescente ou cadeias em ordem alfabética. Os princípios são exatamente os mesmos; basta mudar a ordem de classificação.

A propriedade heap especifica que cada nó em um heap binário deve ser pelo menos tão grande quanto os dois filhos. Em particular, isso implica que o maior item do heap está na raiz. Peneirar e peneirar são essencialmente a mesma operação em direções opostas: mova um nó ofensivo até satisfazer a propriedade heap:

  • siftDown troca um nó que é muito pequeno com seu maior filho (movendo-o para baixo) até que ele seja pelo menos tão grande quanto os dois nós abaixo dele.
  • siftUp troca um nó que é muito grande com seu pai (movendo-o para cima) até que não seja maior que o nó acima dele.

O número de operações necessárias siftDowne siftUpé proporcional à distância que o nó pode ter que se mover. Pois siftDown, é a distância até o fundo da árvore, por isso siftDowné caro para os nós no topo da árvore. Com siftUp, o trabalho é proporcional à distância até o topo da árvore, por isso siftUpé caro para os nós na parte inferior da árvore. Embora ambas as operações sejam O (log n) no pior caso, em um heap, apenas um nó está no topo, enquanto metade dos nós está na camada inferior. assim ele não deve ser muito surpreendente que, se temos de aplicar uma operação para cada nó, nós preferimos siftDownmais siftUp.

A buildHeapfunção pega uma matriz de itens não classificados e os move até que todos satisfaçam a propriedade heap, produzindo um heap válido. Existem duas abordagens que você pode buildHeapusar para usar as operações siftUpe siftDownque descrevemos.

  1. Comece na parte superior da pilha (o início da matriz) e chame siftUpcada item. Em cada etapa, os itens peneirados anteriormente (os itens anteriores ao item atual na matriz) formam um heap válido, e a peneiração do próximo item o coloca em uma posição válida no heap. Após selecionar cada nó, todos os itens atendem à propriedade heap.

  2. Ou siga na direção oposta: comece no final da matriz e mova-se para trás em direção à frente. A cada iteração, você peneira um item até que ele esteja no local correto.

Qual implementação buildHeapé mais eficiente?

Ambas as soluções produzirão um heap válido. Sem surpresa, a mais eficiente é a segunda operação que utiliza siftDown.

Deixe h = log n representar a altura da pilha. O trabalho necessário para a siftDownabordagem é dado pela soma

(0 * n/2) + (1 * n/4) + (2 * n/8) + ... + (h * 1).

Cada termo na soma tem a distância máxima que um nó na altura especificada terá que se mover (zero para a camada inferior, h para a raiz) multiplicado pelo número de nós nessa altura. Por outro lado, a soma para chamar siftUpcada nó é

(h * n/2) + ((h-1) * n/4) + ((h-2)*n/8) + ... + (0 * 1).

Deve ficar claro que a segunda soma é maior. O primeiro termo por si só é hn / 2 = 1/2 n log n , portanto, essa abordagem tem complexidade na melhor das hipóteses O (n log n) .

Como provamos que a soma da siftDownabordagem é realmente O (n) ?

Um método (existem outras análises que também funcionam) é transformar a soma finita em uma série infinita e depois usar a série de Taylor. Podemos ignorar o primeiro termo, que é zero:

Série Taylor para a complexidade buildHeap

Se você não sabe ao certo por que cada uma dessas etapas funciona, eis uma justificativa para o processo em palavras:

  • Os termos são todos positivos; portanto, a soma finita deve ser menor que a soma infinita.
  • A série é igual a uma série de potências avaliada em x = 1/2 .
  • Essa série de potências é igual a (um tempo constante) a derivada da série de Taylor para f (x) = 1 / (1-x) .
  • x = 1/2 está dentro do intervalo de convergência dessa série de Taylor.
  • Portanto, podemos substituir a série de Taylor por 1 / (1-x) , diferenciar e avaliar para encontrar o valor da série infinita.

Como a soma infinita é exatamente n , concluímos que a soma finita não é maior e, portanto, é O (n) .

Por que a classificação de heap requer tempo O (n log n) ?

Se é possível executar buildHeapem tempo linear, por que a classificação de heap requer tempo O (n log n) ? Bem, a classificação de heap consiste em dois estágios. Primeiro, chamamos buildHeapa matriz, que requer tempo O (n) se implementada de maneira ideal. O próximo estágio é excluir repetidamente o maior item da pilha e colocá-lo no final da matriz. Como excluímos um item do heap, sempre há um ponto aberto logo após o final do heap, onde podemos armazenar o item. Portanto, a classificação da pilha atinge uma ordem classificada, removendo sucessivamente o próximo item maior e colocando-o na matriz, começando na última posição e movendo-se para a frente. É a complexidade desta última parte que domina na classificação da pilha. O loop fica assim:

for (i = n - 1; i > 0; i--) {
    arr[i] = deleteMax();
}

Claramente, o loop executa O (n) vezes ( n - 1 para ser mais preciso, o último item já está no lugar). A complexidade de deleteMaxum heap é O (log n) . Geralmente, é implementado removendo a raiz (o maior item deixado na pilha) e substituindo-a pelo último item da pilha, que é uma folha e, portanto, um dos itens menores. Essa nova raiz quase certamente violará a propriedade heap, portanto, você deve ligar siftDownaté movê-la de volta para uma posição aceitável. Isso também tem o efeito de mover o próximo item maior para a raiz. Observe que, ao contrário do buildHeaplocal para a maioria dos nós que estamos chamando Embora a árvore esteja encolhendo, ela não encolhe rápido o suficientesiftDown da parte inferior da árvore, agora estamos chamando siftDowndo topo da árvore em cada iteração!: A altura da árvore permanece constante até você remover a primeira metade dos nós (quando você limpa a camada inferior completamente). Então, para o próximo trimestre, a altura é h - 1 . Portanto, o trabalho total para esta segunda etapa é

h*n/2 + (h-1)*n/4 + ... + 0 * 1.

Observe a opção: agora o caso de trabalho zero corresponde a um único nó e o caso de trabalho h corresponde à metade dos nós. Essa soma é O (n log n), assim como a versão ineficiente dobuildHeap disso é implementada usando o siftUp. Mas, neste caso, não temos escolha, pois estamos tentando classificar e exigimos que o próximo maior item seja removido em seguida.

Em resumo, o trabalho para classificação de heap é a soma dos dois estágios: O (n) tempo para buildHeap e O (n log n) para remover cada nó em ordem , portanto, a complexidade é O (n log n) . Você pode provar (usando algumas idéias da teoria da informação) que, para uma classificação baseada em comparação, O (n log n) é o melhor que você poderia esperar de qualquer maneira; portanto, não há razão para se decepcionar com isso ou esperar que a classificação da pilha atinja o O (n) tempo limite que buildHeapsim.

Jeremy West
fonte
2
Editei minha resposta para usar um heap máximo, pois parece que a maioria das pessoas está se referindo a isso e é a melhor opção para a classificação de heap.
Jeremy Oeste
28
Foi isso que deixou intuitivamente claro para mim: "apenas um nó está no topo, enquanto metade dos nós está na camada inferior. Portanto, não seria de surpreender que, se tivéssemos que aplicar uma operação a cada nó, prefira siftDown sobre siftUp. "
Vicky Chijwani
3
@JeremyWest "Um é começar no topo da pilha (o início da matriz) e chamar siftUp em cada item." - Você queria começar no final da pilha?
aste123
4
@ aste123 Não, está correto como está escrito. A idéia é manter uma barreira entre a parte da matriz que satisfaça a propriedade heap e a parte não classificada da matriz. Você começa no início, avançando e chamando siftUpcada item, ou começa no final, avançando e chamando siftDown. Independentemente da abordagem escolhida, você está selecionando o próximo item na parte não classificada da matriz e executando a operação apropriada para movê-lo para uma posição válida na parte ordenada da matriz. A única diferença é o desempenho.
Jeremy West
2
Esta é a melhor resposta que eu já vi para qualquer pergunta no mundo. Foi tão bem explicado, eu era como é realmente possível ... muito obrigado.
HARSHIL JAIN 26/12/19
314

Sua análise está correta. No entanto, não é apertado.

Não é realmente fácil explicar por que construir uma pilha é uma operação linear; você deve lê-la melhor.

Uma ótima análise do algoritmo pode ser vista aqui .


A idéia principal é que no build_heapalgoritmo o heapifycusto real não é O(log n)para todos os elementos.

Quando heapifyé chamado, o tempo de execução depende da distância que um elemento pode mover para baixo na árvore antes que o processo termine. Em outras palavras, isso depende da altura do elemento na pilha. Na pior das hipóteses, o elemento pode descer até o nível da folha.

Vamos contar o trabalho realizado, nível por nível.

No nível mais baixo, existem 2^(h)nós, mas não chamamos heapifynenhum deles, portanto o trabalho é 0. No nível seguinte, existem 2^(h − 1)nós, e cada um pode descer 1 nível. No terceiro nível, a partir da parte inferior, existem 2^(h − 2)nós, e cada um pode descer 2 níveis.

Como você pode ver, nem todas as operações de heapify são O(log n), é por isso que você está obtendo O(n).

emre nevayeshirazi
fonte
17
Essa é uma ótima explicação ... mas porque é que o heap-sort é executado em O (n log n). Por que o mesmo raciocínio não se aplica à heap-sort?
HBA
49
@hba Acho que a resposta à sua pergunta reside na compreensão esta imagem a partir deste artigo . Heapifyé O(n)quando termina com siftDownmas O(n log n)quando termina com siftUp. A classificação real (extrair itens do heap um por um) deve ser feita com o siftUpmesmo, portanto O(n log n).
precisa saber é o seguinte
3
Eu realmente gosto da explicação intuitiva do seu documento externo na parte inferior.
Lukas Greblikas
1
@hba A resposta abaixo, de Jeremy West, aborda sua pergunta com mais detalhes, mais fáceis de entender, e explica melhor a resposta dos comentários do The111 aqui.
Cellepo # 21/15
Uma pergunta. Parece-me que as # comparações feitas para um nó em altura a ipartir da parte inferior de uma árvore de altura h devem também fazer 2* log(h-i)comparações e devem ser consideradas também @ The111. O que você acha?
Sid
94

Intuitivamente:

"A complexidade deve ser O (nLog n) ... para cada item que" heapizamos ", ele tem o potencial de filtrar uma vez para cada nível do heap até agora (que é o nível de log n)."

Nem tanto. Sua lógica não produz um limite estreito - ela superestima a complexidade de cada heapify. Se construída de baixo para cima, a inserção (heapify) pode ser muito menor que O(log(n)). O processo é como se segue:

(Etapa 1) Os primeiros n/2elementos vão para a linha inferior da pilha. h=0, portanto, o heapify não é necessário.

(Etapa 2) Os próximos elementos vão na linha 1 acima da parte inferior. , empilhe os filtros 1 nível abaixo.n/22h=1

(Etapa i ) Os próximos elementos são alinhados de baixo para cima. , empilhe os níveis dos filtros para baixo.n/2iih=ii

( Log da etapa (n) ) O último elemento entra na linha acima da parte inferior. , empilhe os níveis dos filtros para baixo.n/2log2(n) = 1log(n)h=log(n)log(n)

AVISO: após a etapa um, 1/2os elementos (n/2)já estão no heap e nem precisamos chamar heapify uma vez. Além disso, observe que apenas um único elemento, a raiz, incorre em toda a log(n)complexidade.


Teoricamente:

As etapas totais Npara criar um monte de tamanho npodem ser gravadas matematicamente.

Em altura i, mostramos (acima) que haverá elementos que precisam chamar heapify, e sabemos que heapify em altura é . Isto dá:n/2i+1iO(i)

insira a descrição da imagem aqui

A solução para o último somatório pode ser encontrada tomando a derivada de ambos os lados da bem conhecida equação de série geométrica:

insira a descrição da imagem aqui

Finalmente, a conexão com x = 1/2a equação acima rende 2. Conectar isso à primeira equação fornece:

insira a descrição da imagem aqui

Assim, o número total de etapas é de tamanho O(n)

bcorso
fonte
35

Seria O (n log n) se você construísse o heap inserindo elementos repetidamente. No entanto, você pode criar um novo heap com mais eficiência, inserindo os elementos em ordem arbitrária e aplicando um algoritmo para "heapificá-los" na ordem correta (dependendo do tipo de heap, é claro).

Veja http://en.wikipedia.org/wiki/Binary_heap , "Criando uma pilha" para um exemplo. Nesse caso, você trabalha essencialmente a partir do nível inferior da árvore, trocando os nós pai e filho até que as condições da pilha sejam atendidas.

mike__t
fonte
12

Já existem ótimas respostas, mas eu gostaria de adicionar uma pequena explicação visual

insira a descrição da imagem aqui

Agora, dê uma olhada na imagem, existem
n/2^1 nós verdes com altura 0 (aqui 23/2 = 12)
n/2^2 nós vermelhos com altura 1 (aqui 23/4 = 6)
n/2^3 nó azul com altura 2 (aqui 23/8 = 3)
n/2^4 nós roxos com altura 3 (aqui 23/16 = 2)
para que haja n/2^(h+1)nós para altura h
Para encontrar a complexidade do tempo, conte a quantidade de trabalho realizado ou o número máximo de iterações executadas por cada nó
agora pode-se notar que cada nó pode executar (no máximo) iterações == altura do nó

Green  = n/2^1 * 0 (no iterations since no children)  
red    = n/2^2 * 1 (heapify will perform atmost one swap for each red node)  
blue   = n/2^3 * 2 (heapify will perform atmost two swaps for each blue node)  
purple = n/2^4 * 3 (heapify will perform atmost three swaps for each purple node)   

portanto, para qualquer nó com altura h, o trabalho máximo realizado é n / 2 ^ (h + 1) * h

Agora, o trabalho total realizado é

->(n/2^1 * 0) + (n/2^2 * 1)+ (n/2^3 * 2) + (n/2^4 * 3) +...+ (n/2^(h+1) * h)  
-> n * ( 0 + 1/4 + 2/8 + 3/16 +...+ h/2^(h+1) ) 

agora para qualquer valor de h , a sequência

-> ( 0 + 1/4 + 2/8 + 3/16 +...+ h/2^(h+1) ) 

nunca excederá 1
Portanto, a complexidade do tempo nunca excederá O (n) para construir heap

Julkar9
fonte
7

Como sabemos que a altura de um heap é log (n) , onde n é o número total de elementos.Vamos representá-lo como h
   Quando executamos uma operação de heapify, os elementos no último nível ( h ) não se moverão nem um único degrau.
   O número de elementos no segundo nível anterior ( h-1 ) é 2 h-1 e eles podem se mover no nível máximo 1 (durante o heapify).
   Da mesma forma, para o i th , nível, temos 2 i elementos que podem se mover oi posições.

Portanto, número total de movimentos = S = 2 h * 0 + 2 h-1 * 1 + 2 h-2 * 2 + ... 2 0 * h

                                               S = 2 h {1/2 + 2/2 2 + 3/2 3 + ... h / 2 h } ----------------------- -------------------------- 1
esta é a série AGP , para resolver essa divisão dos dois lados por 2
                                               S / 2 = 2 h {1/2 2 + 2/2 3 + ... h / 2 h + 1 } --------------------------------- ---------------- 2
subtraindo a equação 2 de 1, obtém
                                               S / 2 = 2 h { 1/2 + 1/2 2 + 1/2 3 + ... + 1 / 2 h + h / 2 h + 1 }
                                               S = 2 h + 1 {1/2 + 1/2 2 + 1/2 3 + ... + 1/2 h + h / 2 h + 1 }
agora 1/2 + 1/2 2 + 1/2 3 + ... + 1/2 h está diminuindo GP cuja soma é menor que 1 (quando h tende ao infinito, a soma tende a 1). Em uma análise mais aprofundada, vamos usar um limite superior na soma que é 1.
Isso dá S = 2 h + 1 {1 + h / 2 h + 1 }
                    = 2 h + 1 + h
                    ~ 2 h + h
como h = log (n) , 2 h = n

Portanto S = n + log (n)
T (C) = O (n)

Tanuj Yadav
fonte
6

Ao criar um monte, digamos que você esteja adotando uma abordagem ascendente.

  1. Você pega cada elemento e o compara com seus filhos para verificar se o par está em conformidade com as regras de heap. Portanto, as folhas são incluídas na pilha gratuitamente. Isso é porque eles não têm filhos.
  2. Movendo para cima, o pior cenário para o nó logo acima das folhas seria 1 comparação (no máximo, eles seriam comparados com apenas uma geração de filhos)
  3. Indo além, seus pais imediatos podem no máximo ser comparados a duas gerações de filhos.
  4. Continuando na mesma direção, você terá comparações de log (n) para a raiz no pior cenário. e log (n) -1 para seus filhos imediatos, log (n) -2 para seus filhos imediatos e assim por diante.
  5. Então, resumindo tudo, você chega a algo como log (n) + {log (n) -1} * 2 + {log (n) -2} * 4 + ..... + 1 * 2 ^ {( logn) -1} que nada mais é que O (n).
Jones
fonte
2

No caso de construir a pilha, começamos a partir da altura, logn -1 (onde logn é a altura da árvore de n elementos). Para cada elemento presente na altura 'h', subimos no máximo até (logn -h).

    So total number of traversal would be:-
    T(n) = sigma((2^(logn-h))*h) where h varies from 1 to logn
    T(n) = n((1/2)+(2/4)+(3/8)+.....+(logn/(2^logn)))
    T(n) = n*(sigma(x/(2^x))) where x varies from 1 to logn
     and according to the [sources][1]
    function in the bracket approaches to 2 at infinity.
    Hence T(n) ~ O(n)
Kartik Goyal
fonte
1

Inserções sucessivas podem ser descritas por:

T = O(log(1) + log(2) + .. + log(n)) = O(log(n!))

Por aproximação de estorninho n! =~ O(n^(n + O(1))), portanto,T =~ O(nlog(n))

Espero que isso ajude, a maneira ideal O(n)é usar o algoritmo de pilha de compilação para um determinado conjunto (a ordenação não importa).

Tomer Shalev
fonte
1

Basicamente, o trabalho é feito apenas em nós que não são folhas durante a construção de um heap ... e o trabalho realizado é a quantidade de trocas para satisfazer a condição do heap ... em outras palavras (no pior caso), o valor é proporcional à altura do nó ... apesar de toda a complexidade do problema, é proporcional à soma das alturas de todos os nós não foliares .. que é (2 ^ h + 1 - 1) -h-1 = nh-1 = Em)

Shubham Jindal
fonte
1

O @bcorso já demonstrou a prova da análise de complexidade. Mas, para quem ainda está aprendendo a análise da complexidade, tenho que acrescentar:

A base do seu erro original se deve a uma interpretação incorreta do significado da instrução "a inserção em um heap leva tempo O (log n)". A inserção em um heap é realmente O (log n), mas é necessário reconhecer que n é o tamanho do heap durante a inserção .

No contexto de inserção de n objetos em um heap, a complexidade da i-ésima inserção é O (log n_i), em que n_i é o tamanho do heap como na inserção i. Somente a última inserção tem uma complexidade de O (log n).

N.Vegeta
fonte
1

Vamos supor que você tenha N elementos em um heap. Então sua altura seria Log (N)

Agora você deseja inserir outro elemento, então a complexidade seria: Log (N) , temos que comparar todo o caminho UP à raiz.

Agora você está tendo N + 1 elementos & height = Log (N + 1)

Usando a técnica de indução , pode-se provar que a complexidade da inserção seria ∑logi .

Agora usando

log a + log b = log ab

Isso simplifica para: logi = log (n!)

que na verdade é O (NlogN)

Mas

estamos fazendo algo errado aqui, como em todos os casos em que não alcançamos o topo. Portanto, durante a execução na maioria das vezes, podemos descobrir que não estamos indo nem pela metade da árvore. Portanto, esse limite pode ser otimizado para ter outro limite mais apertado usando a matemática fornecida nas respostas acima.

Essa percepção veio a mim depois de um detalhe e experimentação no Heaps.

Fooo
fonte
0

Eu realmente gosto da explicação de Jeremy West .... outra abordagem realmente fácil de entender é dada aqui http://courses.washington.edu/css343/zander/NotesProbs/heapcomplexity

Como o buildheap depende do uso, depende da heapify e é usada a abordagem shiftdown, que depende da soma das alturas de todos os nós. Então, para encontrar a soma da altura dos nós que é dada por S = somatório de i = 0 a i = h de (2 ^ i * (oi)), onde h = logn é a altura da árvore que resolve s, obtemos s = 2 ^ (h + 1) - 1 - (h + 1), uma vez que n = 2 ^ (h + 1) - 1 s = n - h - 1 = n- logn - 1 s = O (n), e, portanto, a complexidade do buildheap é O (n).

Nitish Jain
fonte
0

"O limite linear de tempo do build Heap pode ser mostrado calculando a soma das alturas de todos os nós no heap, que é o número máximo de linhas tracejadas. Para a árvore binária perfeita da altura h contendo N = 2 ^ ( h + 1) - 1 nós, a soma das alturas dos nós é N - H - 1. Portanto, é O (N). "

sec3
fonte
0

Prova de O (n)

A prova não é chique, e bem direta, eu só provei o caso de uma árvore binária completa; o resultado pode ser generalizado para uma árvore binária completa.

Yi Y
fonte
0

Obtemos o tempo de execução da construção do heap, descobrindo a movimentação máxima que cada nó pode executar. Portanto, precisamos saber quantos nós estão em cada linha e a que distância cada nó pode ir.

Começando no nó raiz, cada linha seguinte tem o dobro dos nós que a linha anterior, portanto, respondendo com que frequência podemos duplicar o número de nós até não termos mais nós, obtemos a altura da árvore. Ou, em termos matemáticos, a altura da árvore é log2 (n), sendo n o comprimento da matriz.

Para calcular os nós em uma linha que começamos pela parte de trás, sabemos que n / 2 estão na parte inferior; portanto, ao dividir por 2, obtemos a linha anterior e assim por diante.

Com base nisso, obtemos esta fórmula para a abordagem Siftdown: (0 * n / 2) + (1 * n / 4) + (2 * n / 8) + ... + (log2 (n) * 1)

O termo na última parêntese é a altura da árvore multiplicada pelo nó que está na raiz; o termo na primeira parêntese é todos os nós na linha inferior multiplicados pelo comprimento que podem percorrer, 0. A mesma fórmula no smart: insira a descrição da imagem aqui

Matemática

Trazendo o n de volta, temos 2 * n, 2 pode ser descartado porque é uma constante e tada, temos o pior tempo de execução da abordagem Siftdown: n.

Max Tromp
fonte
-6

pense que você está cometendo um erro. Dê uma olhada no seguinte: http://golang.org/pkg/container/heap/ Construir um heap não é O (n). No entanto, a inserção é O (lg (n). Suponho que a inicialização seja O (n) se você definir um tamanho de heap b / c, o heap precisará alocar espaço e configurar a estrutura de dados. Se você tiver n itens para colocar no heap, sim, cada inserção é lg (n) e há n itens, então você obtém n * lg (n) conforme indicado

Mike Schachter
fonte
2
não, não é apertado. análise mais rigorosa da pilha de compilação gera O (n)
emre nevayeshirazi 18/03/12
parece que é uma estimativa. A citação no artigo que ele referenciou é "A intuição é que a maioria das chamadas para heapify são feitas em montes muito curtos". No entanto, isso está fazendo algumas suposições. Presumivelmente, para uma pilha grande, o pior cenário ainda seria O (n * lg (n)), mesmo que geralmente você possa se aproximar de O (n). Mas eu posso estar errado
Mike Schachter
Sim, essa também é minha resposta intuitiva, mas referências como o estado da wikipedia "Montes com n elementos podem ser construídos de baixo para cima em O (n)".
GBa 18/03/12
1
Eu estava pensando em uma estrutura de dados totalmente classificada. Esqueci as propriedades específicas de uma pilha.
precisa