Bir yığın oluşturmak O (n) zaman karmaşıklığı nasıl olabilir?


494

Birisi bir yığın oluşturmanın O (n) karmaşıklığı nasıl olabileceğini açıklamaya yardımcı olabilir mi?

Bir öğeyi yığının içine O(log n)yerleştirmek ve ekleme n / 2 kez yinelenir (kalan yapraklar ayrılır ve yığın özelliğini ihlal edemez). Yani, bu karmaşıklığın olması gerektiği anlamına gelir O(n log n)diye düşünüyorum.

Başka bir deyişle, "öbeklediğimiz" her bir öğe için, şimdiye kadar öbek için her seviye için bir kez filtre uygulama potansiyeline sahiptir (log n seviyeleri).

Neyi kaçırıyorum?


Bir yığını "inşa ederek" tam olarak ne demek istiyorsun?
mfrankli

Bir yığınta yaptığınız gibi, sıralanmamış bir dizi alın ve bir yığının kurallarına
uyuncaya

2
Bulabildiğim tek şey bu bağlantıydı: Buildheap'in karmaşıklığı Θ (n lg n) - n Heapify'a çağrı başına Θ (lg n) maliyetiyle geliyor, ancak bu sonuç Θ (n) olarak geliştirilebilir. cs.txstate.edu/~ch04/webtest/teaching/courses/5329/lectures/…
GBa 18:30 '

2
MIT bu videoyu izlemek @Gba: O matematik bir lil biraz biz O (n) olsun nasıl iyi açıklıyor youtube.com/watch?v=B7hVxCmfPtM
CodeShadow

2
@CodeShadow açıklamasına doğrudan bağlantı: youtu.be/B7hVxCmfPtM?t=41m21s
sha1

Yanıtlar:


435

Bu konuda gömülü birkaç soru olduğunu düşünüyorum:

  • O (n) zamanında buildHeapçalışması için nasıl uygularsınız ?
  • Doğru uygulandığında O (n) zamanında buildHeapçalıştığını nasıl gösterirsiniz ?
  • Neden aynı mantık öbek sıralamasının O (n log n) yerine O (n) zamanında çalışmasını sağlamak için çalışmıyor ?

O (n) zamanında buildHeapçalışması için nasıl uygularsınız ?

Genellikle bu soruların cevapları arasındaki fark odaklanmak siftUpve siftDown. O (n) performansını elde etmek için siftUpve arasında doğru seçim yapmak siftDownönemlidir , ancak genel ve arasındaki farkı anlamasına yardımcı olacak hiçbir şey yapmaz . Gerçekten de, hem uygun uygulamaları ve olacak sadece kullanmak . Örneğin, bir ikili yığın kullanarak bir öncelik sırası uygulamak için kullanılacak böylece operasyon sadece varolan yığın haline ekler gerçekleştirmek için gereklidir.buildHeapbuildHeapheapSortbuildHeapheapSortsiftDownsiftUp

Maks yığının nasıl çalıştığını açıklamak için bunu yazdım. Bu, genellikle yığın sıralaması için veya daha yüksek değerlerin daha yüksek önceliği işaret ettiği bir öncelik kuyruğu için kullanılan yığın türüdür. Min yığını da yararlıdır; örneğin, tamsayı tuşlarına sahip öğeleri artan sırada veya dizeleri alfabetik sırada alırken. İlkeler tamamen aynıdır; sadece sıralama düzenini değiştirin.

Yığın özellik belirtir bir ikili yığın her düğüm en azından alt öğelerinden ikisi kadar büyük olması gerektiğini. Özellikle, bu öbekteki en büyük öğenin kökte olduğu anlamına gelir. Aşağı eleme ve eleme, ters yönde aynı işlemdir: rahatsız edici bir düğümü, heap özelliğini karşılayana kadar taşıyın:

  • siftDown en azından altındaki her iki düğüm kadar büyük olana kadar en büyük çocuğuyla (böylece aşağı doğru hareket ettirerek) çok küçük bir düğümü değiştirir.
  • siftUp üstündeki düğümden daha büyük olmayana kadar üst öğesiyle çok büyük bir düğümü (böylece yukarı doğru hareket ettirerek) değiştirir.

İşlemlerinin sayısı için gerekli olan siftDownve siftUpdüğüm taşımak gerekebilir mesafesi ile orantılıdır. Çünkü siftDown, ağacın tabanına olan mesafedir, bu nedenle ağacın siftDownüstündeki düğümler için pahalıdır. İle siftUp, iş ağacın tepesine olan mesafe ile orantılıdır, bu nedenle ağacın siftUpaltındaki düğümler için pahalıdır. Her iki işlem de en kötü durumda O (log n) olmasına rağmen , bir yığın içinde üstte sadece bir düğüm bulunurken, düğümlerin yarısı alt katmanda yer alır. Yani biz her düğüme bir operasyon uygulamak zorunda, biz tercih edeceğini çok şaşırtıcı olmamalıdır siftDownüzerinde siftUp.

buildHeapHepsi bu suretle geçerli bir yığın üreten yığın özelliğini tatmin dek fonksiyon sıralanmamış öğeler ve hamle onları bir dizi alır. Birinin tanımladığımız ve işlemlerini buildHeapkullanmak için alabileceği iki yaklaşım vardır .siftUpsiftDown

  1. Yığının üstünden (dizinin başlangıcından) başlayın ve siftUpher öğeyi arayın . Her adımda, önceden elenmiş öğeler (dizideki geçerli öğeden önceki öğeler) geçerli bir yığın oluşturur ve sonraki öğenin yukarı kaydırılması, öğeyi yığında geçerli bir konuma yerleştirir. Her düğümü eledikten sonra, tüm öğeler heap özelliğini karşılar.

  2. Ya da, ters yönde gidin: dizinin sonundan başlayın ve öne doğru geri gidin. Her yinelemede, bir öğeyi doğru konuma gelene kadar eleyin.

Hangi uygulama buildHeapdaha verimli?

Bu çözümlerin her ikisi de geçerli bir yığın oluşturacaktır. Şaşırtıcı olmayan bir şekilde, daha verimli olan ikinci işlemdir siftDown.

H = log n , yığının yüksekliğini temsil etsin . siftDownYaklaşım için gerekli olan çalışma toplamla verilir.

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

Toplamdaki her terim, belirli bir yükseklikteki bir düğümün hareket etmesi gereken maksimum mesafeye sahiptir (alt katman için sıfır, kök için h), bu yükseklikteki düğüm sayısıyla çarpılır. Buna karşılık, siftUpher bir düğümü çağırmak için toplam

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

İkinci toplamın daha büyük olduğu açık olmalıdır. Sadece birinci terim hn / 2 = 1/2 n log n'dir , bu nedenle bu yaklaşım en iyi O (n log n) ' de karmaşıklığa sahiptir .

siftDownGerçekten O (n) yaklaşımının toplamını nasıl kanıtlarız ?

Bir yöntem (aynı zamanda işe yarayan başka analizler de vardır) sonlu toplamı sonsuz bir seriye dönüştürmek ve daha sonra Taylor serisini kullanmaktır. Sıfır olan ilk terimi göz ardı edebiliriz:

BuildHeap karmaşıklığı için Taylor serisi

Bu adımların her birinin neden işe yaradığından emin değilseniz, buradaki kelimeler için bir gerekçe:

  • Terimlerin hepsi pozitiftir, bu nedenle sonlu toplam sonsuzdan küçük olmalıdır.
  • Seri, x = 1/2 olarak değerlendirilen bir güç serisine eşittir .
  • Bu kuvvet serisi, f (x) = 1 / (1-x) için Taylor serisinin türevine (sabit bir kez) eşittir .
  • x = 1/2 bu Taylor serisinin yakınsama aralığındadır.
  • Bu nedenle, Taylor serisini 1 / (1-x) ile değiştirebilir , farklılaştırabilir ve sonsuz serilerin değerini bulmak için değerlendirebiliriz.

Sonsuz toplam tam olarak n olduğundan, sonlu toplamın daha büyük olmadığı ve bu nedenle O (n) olduğu sonucuna vardık .

Öbek sıralaması neden O (n log n) süresi gerektirir?

buildHeapDoğrusal zamanda çalışmak mümkünse , yığın sıralaması neden O (n log n) süresi gerektirir? Öbek türü iki aşamadan oluşur. İlk olarak, en iyi şekilde uygulandığında O (n) zamanı buildHeapgerektiren diziyi çağırırız . Bir sonraki aşama, yığındaki en büyük öğeyi tekrar tekrar silmek ve dizinin sonuna koymaktır. Bir öğeyi yığından sildiğimiz için, yığının bitiminden hemen sonra öğeyi saklayabileceğimiz her zaman açık bir nokta vardır. Öbek sıralaması, bir sonraki en büyük öğeyi art arda kaldırarak ve son konumda başlayıp öne doğru hareket ederek diziye koyarak sıralı bir sıraya ulaşır. Öbek türünde egemen olan bu son parçanın karmaşıklığıdır. Döngü şöyle:

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

Açıkçası, döngü O (n) kez çalışır ( kesin olarak n - 1 , son öğe zaten yerinde). deleteMaxBir yığın için karmaşıklığı O (log n) 'dir . Genellikle kök (yığın içinde kalan en büyük öğe) kaldırılarak ve bir yaprak olan öbekteki son öğe ve dolayısıyla en küçük öğelerden biri ile değiştirilerek uygulanır. Bu yeni kök neredeyse heap özelliğini ihlal edecektir, bu yüzden siftDownkabul edilebilir bir konuma geri taşıyana kadar aramalısınız . Bu, bir sonraki en büyük öğeyi köküne taşıma etkisine de sahiptir. buildHeapDüğümlerin çoğunun siftDownağacın altından nereye çağırdığımızın aksine , şimdi siftDownher yinelemede ağacın tepesinden aradığımızı fark edin!Ağaç küçülmesine rağmen, yeterince hızlı küçülmez : Ağacın yüksekliği, düğümlerin ilk yarısını kaldırana kadar (alt katmanı tamamen temizlediğinizde) sabit kalır. Sonra bir sonraki çeyrek için yükseklik h - 1'dir . Yani bu ikinci aşama için toplam çalışma

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

Düğmeye dikkat edin: şimdi sıfır çalışma durumu tek bir düğüme karşılık gelir ve h çalışma durumu düğümlerin yarısına karşılık gelir. Bu toplam, siftUp kullanılarak uygulanan verimsiz sürümü gibi O (n log n) 'buildHeap dir. Ancak bu durumda, sıralamaya çalıştığımız için bir seçeneğimiz yok ve bir sonraki en büyük öğenin kaldırılmasını istiyoruz.

Özetle, yığın sıralaması için yapılan çalışma iki aşamanın toplamıdır: O (n) buildHeap ve O (n log n) için her düğümü sırayla kaldırma zamanıdır , dolayısıyla karmaşıklık O (n log n) olur . Karşılaştırma tabanlı bir sıralama için O (n log n) ' in yine de umabileceğiniz en iyisi olduğunu kanıtlayabilirsiniz ( bu yüzden hayal kırıklığına uğramanız veya yığın sıralamasının O (n) zaman sınırlaması buildHeapyapar.


2
Cevabımı maks. Yığın kullanmak için düzenledim, çünkü çoğu insan buna atıfta bulunuyor gibi görünüyor ve yığın sıralaması için en iyi seçim.
Jeremy West

28
Bunu sezgisel olarak açıklığa kavuşturan şey buydu: "üstte sadece bir düğüm bulunurken, düğümlerin yarısı alt katmanda yer alır. Bu nedenle, her düğüme bir işlem uygulamak zorunda kalırsak, siftUp yerine siftDown'ı tercih edin. "
Vicky Chijwani

3
@JeremyWest "Bir tanesi yığının üstünden (dizinin başlangıcı) başlamak ve her öğe için siftUp'ı çağırmaktır." - Yığın dibinden mi başlamak istediniz?
aste123

4
@ aste123 Hayır, yazıldığı gibi doğru. Fikir dizinin heap özelliğini karşılayan kısmı ile dizinin ayrılmamış kısmı arasında bir engel oluşturmaktır. Başlangıçta ilerlemeye ve siftUpher öğeyi çağırmaya başlarsınız veya sonunda geri gitmeye ve çağırmaya başlarsınız siftDown. Hangi yaklaşımı seçerseniz seçin, dizinin sıralanmamış bölümünde sonraki öğeyi seçiyor ve dizinin sıralı bölümünde geçerli bir konuma taşımak için uygun işlemi gerçekleştiriyorsunuz. Tek fark performanstır.
Jeremy West

2
Bu, dünyadaki herhangi bir soruya şimdiye kadar gördüğüm en iyi cevap. Çok iyi açıklanmıştı, gerçekten mümkün olduğu gibi ... çok teşekkürler.
HARSHIL JAIN

314

Analiziniz doğru. Ancak, sıkı değil.

Bir yığın oluşturmanın neden doğrusal bir işlem olduğunu açıklamak gerçekten kolay değil, daha iyi okumalısınız.

Bir Büyük analiz algoritması görülebilir burada .


Ana fikir, build_heapalgoritmada gerçek heapifymaliyetin O(log n)tüm elemanlar için olmamasıdır .

Arandığında heapify, çalışma süresi, bir öğenin işlem sona ermeden önce ağaçta ne kadar aşağı hareket edebileceğine bağlıdır. Başka bir deyişle, öbekteki öğenin yüksekliğine bağlıdır. En kötü durumda, eleman yaprak seviyesine kadar inebilir.

Yaptığımız işi seviye adım sayalım.

En alt düzeyde, 2^(h)düğümler vardır, ancak heapifybunlardan herhangi birini çağırmayız , bu yüzden iş 0'dır. Seviyenin yanında 2^(h − 1)düğümler vardır ve her biri 1 seviye aşağı doğru hareket edebilir. Alttan 3. seviyede, 2^(h − 2)düğümler vardır ve her biri 2 seviye aşağı doğru hareket edebilir.

Tüm heapify işlemlerinin görmediği gibi O(log n), bu yüzden alıyorsunuz O(n).


17
Bu harika bir açıklama ... ama neden öbek sıralaması O (n log n) içinde çalışıyor. Aynı mantık yığın sıralaması için neden geçerli değil?
hba

49
@hba ben anlamakta Sorunuzu yalanlarına cevap düşünmek bu resim dan bu makalede . Heapifyile O(n)bittiğinde siftDownama O(n log n)bittiğinde siftUp. Bu siftUpnedenle, gerçek sıralama (öğeleri öbekten birer birer çekmek) yapılmalıdır O(n log n).
The111

3
Dış belgenizin alt kısımdaki sezgisel açıklamasını gerçekten seviyorum.
Lukas Greblikas

1
@hba Jeremy West'in aşağıdaki cevabı, sorunuzu daha ince, anlaşılması kolay ayrıntıda ele alarak The111'in yorum yanıtını burada daha ayrıntılı olarak açıklıyor.
cellepo

Bir soru. Bana öyle geliyor ki, ih yüksekliğindeki bir ağacın altından yükseklikte bir düğüm için yapılan # karşılaştırmaları da 2* log(h-i)karşılaştırmalar yapmalı ve aynı zamanda da hesaba katılmalıdır @ The111. Ne düşünüyorsun?
Sid

94

sezgisel:

"Karmaşıklık O (nLog n) olmalıdır ..." yığınladığımız "her öğe için, yığın için şimdiye kadar her seviye için bir kez filtreleme potansiyeline sahiptir (log n seviyeleri)."

Pek değil. Mantığınız sıkı bir sınır oluşturmaz - her bir yığının karmaşıklığını fazla tahmin eder. Aşağıdan yukarıya doğru inşa edilmişse, ekleme (heapify) çok daha az olabilir O(log(n)). İşlem aşağıdaki gibidir:

(Adım 1) İlk n/2elemanlar yığının alt satırına gider. h=0, öyleyse heapify gerekli değildir.

(Adım 2) Sonraki elemanlar alttan yukarı doğru 1. satıra gider. , filtreleri 1 seviye aşağı yığınlayın.n/22h=1

(Adım i ) Bir sonraki elemanlar aşağıdan yukarıya doğru sıralanır. , filtreler düzeylerini aşağı yığınla .n/2iih=ii

(Adım günlüğü (n) ) Son öğe aşağıdan yukarıya doğru sıralanır. , filtreler düzeylerini aşağı yığınla .n/2log2(n) = 1log(n)h=log(n)log(n)

DİKKAT: adım birinden sonra, o 1/2elementlerin (n/2)yığın zaten vardır ve biz bir kez bile heapify aramaya gerek yoktu. Ayrıca, sadece tek bir öğenin, kökün aslında tam log(n)karmaşıklığa maruz kaldığını unutmayın .


Teorik:

NBir yığın yığın oluşturmak için toplam adımlar n, matematiksel olarak yazılabilir.

Yüksek zamanında i, biz olacağı (yukarıda) gösterdik heapify çağırmanız gerekir elemanları ve yükseklik biz heapify biliyoruz olduğunu . Bu şunları verir:n/2i+1iO(i)

resim açıklamasını buraya girin

Son toplamın çözümü, iyi bilinen geometrik seri denklemin her iki tarafının türevi alınarak bulunabilir:

resim açıklamasını buraya girin

Son olarak, x = 1/2yukarıdaki denklemin içine takılması sonucu verir 2. Bunu ilk denkleme takmak:

resim açıklamasını buraya girin

Böylece, toplam adım sayısı boyuttadır O(n)


35

Yığını art arda öğeleri ekleyerek oluşturursanız O (n log n) olur. Bununla birlikte, öğeleri rasgele sırada ekleyerek ve sonra bunları uygun sıraya "yığmak" için bir algoritma uygulayarak (elbette yığın türüne bağlı olarak) daha verimli bir şekilde yeni bir yığın oluşturabilirsiniz.

Örnek için bkz. Http://en.wikipedia.org/wiki/Binary_heap , "Öbek oluşturma". Bu durumda, esas olarak ağacın alt seviyesinden çalışır, yığın koşulları sağlanana kadar ebeveyn ve alt düğümleri değiştirirsiniz.


12

Zaten bazı harika cevaplar var ama biraz görsel açıklama eklemek istiyorum

resim açıklamasını buraya girin

Şimdi, resmin bir göz atın, orada
n/2^1 yeşil düğümleri ile yüksekliği 0 (burada = 12 23/2)
n/2^2 kırmızı düğümleri ile yüksekliği 1 (burada = 6 23/4)
n/2^3 mavi düğüm ile yüksekliği 2 (burada 23/8 = 3) yükseklik 3 ile
n/2^4 mor düğümler (burada 23/16 = 2), böylece yükseklik h için düğümler vardır Zaman karmaşıklığını bulmak için, yapılan iş miktarını veya her düğüm tarafından gerçekleştirilen yinelemelerin maksimum sayısını hesaplayalım, şimdi her bir düğümün yineleme (en az) yineleme yapın == düğüm yüksekliği
n/2^(h+1)

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)   

h yüksekliği olan tüm düğümler için maksimum iş n / 2 ^ (h + 1) * h

Şimdi yapılan toplam iş

->(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) ) 

şimdi herhangi bir h değeri için , dizi

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

Asla 1'i geçmeyecek
Böylece zamanyığını, bina yığını için O (n) değerini asla aşmayacaktır.


7

Bir yığının yüksekliğinin log (n) olduğunu biliyoruz , burada n toplam eleman sayısıdır. H olarak temsil
   edelim Heapify işlemini gerçekleştirdiğimizde, son seviyedeki ( h ) elemanlar tek bir bile hareket etmeyecektir adım.
   İkinci son seviyedeki ( h-1 ) eleman sayısı 2 h-1'dir ve maksimum 1 seviyede hareket edebilirler (yığınlama sırasında).
   Buna benzer şekilde, I th , seviye Elimizdeki 2 i taşıyabilir elemanları yüksek pozisyonlar.

Bu nedenle toplam hareket sayısı = S = 2 sa * 0 + 2 sa-1 * 1 + 2 sa-2 * 2 + ... 2 0 * sa

                                               S = 2 sa {1/2 + 2/2 2 + 3/2 3 + ... sa / 2 sa } ----------------------- -------------------------- 1
Bu AGP serisidir, bu iki tarafı 2
                                               S / 2 = 2 sa bölünerek çözmek için {1/2 2 + 2/2 3 + ... sa / 2 sa + 1 } --------------------------------- ---------------- 2
denklem çıkarılarak 2 den 1 veren
                                               S / 2 = 2 saat {1/2 + 1/2 2 + 1/2 3 + ... + 1 / 2 sa. + H / 2 sa. + 1 }
                                               S = 2 h + 1 {1/2 + 1/2 2 + 1/2 3 + ... + 1/2 h + h / 2 , h + 1 }
hemen 1/2 + 1/2 2 + 1/2 3 + ... + 1/2 h azalmaktadır GP olan toplamıdır az olduğu 1 (h sonsuza eğiliminde olduğunda, toplam 1 eğilimi). Daha fazla analizde, 1 olan toplamda bir üst sınır alalım.
Bu, S = 2 saat + 1 {1 + saat / 2 saat + 1 }
                    = 2 saat + 1 + saat
                    ~ 2 saat + saat
, h = log olarak verir (n) , 2 sa = n

Bu nedenle S = n + log (n)
T (C) = O (n)


6

Bir yığın oluştururken, aşağıdan yukarıya bir yaklaşım aldığınızı varsayalım.

  1. Her öğeyi alıp çiftin yığın kurallarına uygun olup olmadığını kontrol etmek için çocuklarıyla karşılaştırırsınız. Bu nedenle, yapraklar yığına ücretsiz olarak dahil edilir. Çünkü çocukları yok.
  2. Yukarı doğru hareket ederken, yaprakların hemen üstündeki düğüm için en kötü durum senaryosu 1 karşılaştırma olacaktır (maksimumda sadece bir nesil çocukla karşılaştırılacaktır)
  3. Daha da ileri giderek, ebeveynleri en fazla iki nesil çocukla karşılaştırılabilir.
  4. Aynı yönde devam ederek, en kötü senaryoda kök için günlük (n) karşılaştırmaları olacaktır. ve hemen çocukları için log (n) -1, hemen çocukları için log (n) -2 vb.
  5. Yani hepsini özetlemek gerekirse log (n) + {log (n) -1} * 2 + {log (n) -2} * 4 + ..... + 1 * 2 ^ {( logn) -1} ki O (n) 'den başka bir şey değildir.

2

Yığının inşa edilmesi durumunda, yükseklikten başlıyoruz, logn -1 (burada logn n elementinin ağacının yüksekliği). 'H' yüksekliğinde bulunan her eleman için, maksimum (logn -h) yüksekliğine ineriz.

    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)

1

Ardışık eklemeler şu şekilde tanımlanabilir:

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

Starling yaklaşımıyla n! =~ O(n^(n + O(1))), bu nedenleT =~ O(nlog(n))

Umarım bu yardımcı olur, en uygun yol O(n)belirli bir küme için yapı yığını algoritmasını kullanmaktır (sıralama önemli değildir).


1

Temel olarak, bir yığın inşa edilirken sadece yaprak olmayan düğümlerde iş yapılır ... ve yapılan iş, yığın koşulunu karşılamak için aşağı kaydırma miktarıdır ... diğer bir deyişle (en kötü durumda) miktar, yükseklikle orantılıdır düğümün ... sorunun tüm karmaşıklığı, yapraksız düğümlerin yüksekliklerinin toplamı ile orantılıdır. (2 ^ h + 1 - 1) -h-1 = nh-1 = O (n)


1

@bcorso, karmaşıklık analizinin kanıtını zaten göstermiştir. Ama hala karmaşıklık analizini öğrenenler uğruna şunu eklemek istiyorum:

Orijinal hatanızın temeli, ifadenin anlamının yanlış yorumlanmasından kaynaklanmaktadır, "bir öbeğe ekleme O (log n) zaman alır". Yığına ekleme aslında O (log n) 'dir, ancak n'nin ekleme sırasında yığının boyutu olduğunu bilmeniz gerekir .

Bir yığına n nesnesi ekleme bağlamında, i'inci sokmanın karmaşıklığı O (log n_i) 'dir; burada n_i, i sokmadaki yığının boyutudur. Sadece son eklemenin karmaşıklığı O (log n) olur.


1

Diyelim ki bir yığında N öğeniz var. Sonra yüksekliği Log (N) olur.

Şimdi başka bir eleman eklemek istiyorsunuz, o zaman karmaşıklık şöyle olacaktır: Log (N) , UP ile kökünü karşılaştırmamız gerekiyor.

Şimdi N + 1 öğelerine sahipsiniz & height = Günlük (N + 1)

Kullanma indüksiyon tekniği sokulma karmaşıklığı olacağını ispat edilebilir Σlogi .

Şimdi

log a + log b = log ab

Bu basitleşir: ∑logi = log (n!)

aslında O (NlogN)

Fakat

burada yanlış bir şey yapıyoruz, çünkü her durumda tepeye ulaşmıyoruz. Bu nedenle, çoğu zaman bunu bulabilirken, ağacın yarısına bile gitmeyiz. Bu nedenle, bu sınır yukarıdaki cevaplarda verilen matematiği kullanarak başka bir sıkı bağlanmak üzere optimize edilebilir.

Bu farkındalık bir yığın ve yığınlar üzerinde deney sonra bana geldi.


0

Jeremy West'in açıklamasını gerçekten seviyorum .... Anlamak için gerçekten kolay olan başka bir yaklaşım burada verilmiştir http://courses.washington.edu/css343/zander/NotesProbs/heapcomplexity

çünkü, buildheap kullanımı heapify'a bağlıdır ve tüm düğümlerin yüksekliklerinin toplamına bağlı olan shiftdown yaklaşımı kullanılır. Bu nedenle, S = toplamı (= 2 i * (hi)) 'nin i = 0 ila i = h ile verilen düğümlerin yüksekliğinin toplamını bulmak için, burada h = logn ağaç çözme s'nin yüksekliği s = 2 ^ (h + 1) - 1 - (h + 1) çünkü n = 2 ^ (h + 1) - 1 s = n - h - 1 = n-kütük - 1 s = O (n), ve böylece yapının karmaşıklığı O (n) olur.


0

"Heap derlemesinin doğrusal zaman sınırı, yığındaki tüm düğümlerin yüksekliklerinin toplamı hesaplanarak gösterilebilir, bu da maksimum kesikli çizgi sayısıdır. H = 2 ^ içeren mükemmel yükseklik h ağacı için (= h + 1) - 1 düğüm, düğümlerin yüksekliklerinin toplamı N - H - 1'dir. Dolayısıyla O (N). "


0

O Kanıtı (n)

Kanıt fantezi değil ve oldukça basit, sadece tam bir ikili ağaç için davayı kanıtladım, sonuç tam bir ikili ağaç için genelleştirilebilir.


0

Her düğümün alabileceği maksimum hareketi bularak yığın oluşturma çalışma süresini elde ederiz. Bu nedenle, her bir satırda kaç düğüm olduğunu ve her düğümün ne kadar uzak olabileceğini bilmemiz gerekir.

Kök düğümünden başlayarak, sonraki her satırda önceki satırdan daha fazla düğüm bulunur, bu nedenle düğüm sayısı kalmayana kadar düğüm sayısını ne sıklıkta iki katına çıkarabileceğimizi söyleyerek ağacın yüksekliğini elde ederiz. Veya matematiksel olarak ağacın yüksekliği log2 (n), n dizinin uzunluğu.

Bir satırdaki düğümleri hesaplamak için arkadan başlıyoruz, n / 2 düğümünün en altta olduğunu biliyoruz, bu yüzden 2'ye bölerek bir önceki satırı elde ediyoruz vb.

Buna dayanarak, Eleme yaklaşımı için bu formülü elde ederiz: (0 * n / 2) + (1 * n / 4) + (2 * n / 8) + ... + (log2 (n) * 1)

Son parantezdeki terim, ağacın kökteki bir düğümle çarpıldığı yüksekliktir, ilk parantezdeki terim, alt sıradaki tüm düğümlerin seyahat edebildikleri uzunluk ile çarpımıdır, 0. Akıllıca aynı formül: resim açıklamasını buraya girin

Matematik

N'yi geri getirdik 2 * n, 2 atılabilir çünkü sabit ve tada, Siftdown yaklaşımının en kötü durum çalışma süresine sahibiz: n.


-6

bir hata yaptığınızı düşünüyorum. Şuna bir göz atın: http://golang.org/pkg/container/heap/ Öbek oluşturmak O (n) değildir. Ancak, ekleme O (lg (n) olduğunu düşünüyorum. Ben bir b / c yığın boyutu ayarlarsanız yığın O (n) olduğunu varsayalım yığın boşluk ayırmak ve veri yapısı kurmak gerekir. yığın içine sonra evet, her ekleme lg (n) ve n öğe vardır, bu yüzden u belirtildiği gibi n * lg (n) alırsınız


2
hayır sıkı değil. yapı yığını
verilerinin

bu bir tahmin gibi görünüyor. Alıntıladığı thearticle alıntı "sezgi heapify çağrıları çoğu çok kısa yığınlar üzerinde olmasıdır" Ancak bu bazı varsayımlar yapıyor. Muhtemelen, büyük bir yığın için, genellikle O (n) 'ye yaklaşsanız bile en kötü senaryo O (n * lg (n)) olacaktır. Ama yanılmış olabilirim
Mike Schachter

Evet, bu da benim sezgisel cevabım, ancak wikipedia durumu gibi referanslar "n elemanlı yığınlar O (n) 'de aşağıdan yukarıya doğru inşa edilebilir."
GBa

1
Tamamen sıralanmış bir veri yapısı düşünüyordum. Bir yığının belirli özelliklerini unuttum.
Mike Schachter
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.