İkili arama ağacı ile ikili yığın arasındaki fark nedir?


91

Bu ikisi birbirine çok benziyor ve neredeyse aynı bir yapıya sahipler. Fark ne? Her birinin farklı işlemleri için zaman karmaşıklıkları nelerdir?

Yanıtlar:


63

Yığın, sadece yüksek seviyelerdeki elemanların düşük seviyelerdeki elemanlardan daha büyük (azami yığın için) veya daha küçük (en az yığın için) olduğunu garanti ederken, BST siparişi ("sol" dan "sağa") garanti eder. Sıralanmış elemanları istiyorsanız, BST ile gidin. Dante tarafından bir inek değil

Öbek findMin / findMax'ta (O (1)) daha iyidir, BST ise tüm buluntularda iyidir (O ​​(logN)). Her iki yapı için de Insert (O) logL'dir. Yalnızca findMin / findMax (örneğin öncelik ile ilgili) ile ilgileniyorsanız, öbek ile gidin. Her şeyin sıralanmasını istiyorsanız, BST ile gidin.

xysun tarafından


Bence BST findMin & findMax stackoverflow.com/tr/27074221/764592
Yeo

10
Bunun sadece yaygın bir yanılgı olduğunu düşünüyorum. İkili bir ağaç, Yeo tarafından işaret edilen minimum ve maksimum değerleri bulmak için kolayca değiştirilebilir. Bu aslında yığının bir kısıtlamasıdır : tek etkili sonuç min veya max.
Yığının

Bu videoya göre , daha küçük olanın soyundan gelmediği sürece, daha düşük bir seviyede daha büyük değerlere sahip olabilirsiniz.
Whoan

Öbek yaprak kök dizilir ve BST soldan sağa sıralanır.
Derin Joshi,

34

Hem ikili arama ağaçları hem de ikili yığınlar ağaç tabanlı veri yapılarıdır.

Yığınlar, düğümlerin çocukları üzerinde bir önceliğe sahip olmasını gerektirir. Maksimum bir yığında, her düğümün çocukları kendisinden daha az olmalıdır. Bir dk öbek için tam tersi:

İkili Max Yığın

İkili arama ağaçları (BST) kardeş düğümleri arasında belirli bir sıralamayı (ön sipariş, sipariş sırası, sipariş sonrası) izler. Ağaç gerekir yığınları aksine sıralanabilir:

İkili Arama Ağacı

BST, ekleme, silme ve arama için ortalama değerine sahiptir . İkili Yığınlar findMin / findMax için ortalama ve ekleme ve silme için değerine sahiptir .O(logn)O ( 1 ) O ( log n )
O(1)O(logn)


1
@ FrankW Extraction , hayır? O(logn)
flow2k

32

özet

          Type      BST (*)   Heap
Insert    average   log(n)    1
Insert    worst     log(n)    log(n) or n (***)
Find any  worst     log(n)    n
Find max  worst     1 (**)    1
Create    worst     n log(n)  n
Delete    worst     log(n)    log(n)

Bu tablodaki tüm ortalama zamanlar Insert dışındaki en kötü zamanlarla aynıdır.

  • *: bu cevabın her yerinde BST == Dengeli BST, çünkü dengesiz asimptotik olarak berbat
  • **: bu cevapta açıklanan önemsiz bir değişiklik kullanarak
  • ***: log(n)işaretçi ağacı yığını niçin, dinamik dizi yığını için

BST üzerinde ikili yığının avantajları

BST'nin ikili yığın üzerinden avantajı

  • keyfi öğeleri arayın O(log(n)). Bu BST'lerin katil özelliğidir.

    Yığın için, O(n)genel olarak, en büyük olan hariç O(1).

BST üzerindeki yığının "Yanlış" avantajı

  • Öbek O(1)en fazla BST O(log(n)).

    Bu yaygın bir yanılgıdır, çünkü en büyük öğenin izini sürmek için bir BST'yi değiştirmek ve bu öğenin ne zaman değiştirilebileceğini güncellemek çok önemlidir: daha büyük bir takasın yerleştirilmesinde, kaldırırken en büyük ikinciyi bulabilirsiniz. https://stackoverflow.com/questions/7878622/can-we-use-binary-search-tree-to-simulate-heap-operation ( Yeo tarafından belirtilmiştir ).

    Aslında, bu BST'lere kıyasla yığınların bir kısıtlamasıdır : tek etkili arama, en büyük eleman için olandır .

Ortalama ikili yığın ekleme O(1)

Kaynaklar:

Sezgisel argüman:

  • alt ağaç seviyelerinin üstel olarak üst seviyelere göre daha fazla elemanı vardır, bu nedenle yeni elemanların en altta olacağı kesin
  • Öbek ekleme alttan başlar , BST üstten başlamalıdır

İkili bir yığında, belirli bir endekste değeri arttırmak da O(1)aynı sebepten dolayıdır. Ancak bunu yapmak istiyorsanız, yığın işlemleri hakkında ek bir endeks güncel tutmak isteyebilirsiniz, https://stackoverflow.com/questions/17009056/how-to-implement-ologn-decrease- Min-yığın-temelli-öncelik-kuyruğu için anahtar-işlem örneğin Dijkstra için. Ekstra ücret ödemeden mümkün.

GCC C ++ standart kütüphane gerçek donanımda bir referans niteliği taşıyor

Ekleme zamanları hakkında haklı olup olmadığımı görmek için C ++ std::set( Kırmızı-siyah ağaç BST ) ve std::priority_queue( dinamik dizi yığını ) ekini karşılaştırdım ve elde ettiğim şey bu:

görüntü tanımını buraya girin

  • referans kodu
  • arsa betiği
  • arsa verileri
  • Ubuntu 19.04, GCC 8.3.0 üzerinde CPU'lu bir Lenovo ThinkPad P51 dizüstü bilgisayarda test edilmiştir: Intel Core i7-7820HQ CPU (4 çekirdekli / 8 diş, 2.90 GHz tabanlı, 8 MB önbellek), RAM: 2x Samsung M471A2K43BB1-CRC (2x 16GiB) , 2400 Mbps), SSD: Samsung MZVLB512HAJQ-000L7 (512GB, 3.000 MB / s)

Çok açıkça:

GCC C + + standart kütüphane

gem5 tam bir sistem simülatörüdür ve bu nedenle ile birlikte sonsuz hassas bir saat sağlar m5 dumpstats. Bu yüzden bireysel ekler için zamanlamaları tahmin etmek için kullanmaya çalıştım.

görüntü tanımını buraya girin

Yorumlama:

  • yığın hala sabittir, fakat şimdi daha ayrıntılı olarak birkaç satır olduğunu görüyoruz ve daha yüksek olan her satır daha seyrek.

    Bu, bellek erişim gecikmelerine karşılık gelmelidir yüksek ve yüksek kesici uçlar için yapılır.

  • TODO BST'yi tam olarak yorumlayamıyorum, çünkü logaritmik ve biraz daha sabit görünmüyor.

    Ancak bu daha büyük ayrıntıyla görebildiğimiz birkaç farklı çizgiyi görebiliyoruz, ancak bunların neyi temsil ettiğinden emin değilim: En alt çizgiyi yerleştirdiğimiz için en alt çizginin daha ince olmasını bekliyorum.

Aarch64 HPI CPU'da bu Buildroot kurulumu ile karşılaştırılmıştır .

BST bir dizi üzerinde verimli bir şekilde uygulanamaz

Yığın işlemlerinin yalnızca tek bir ağaç dalına kabarması veya aşağı kabarması gerekir, bu nedenle O(log(n))en kötü durum değişimleri O(1)ortalamadır.

Bir BST'yi dengede tutmak, üst öğeyi bir başkası için değiştirebilen ve tüm dizinin etrafında hareket etmesini gerektiren ağaç dönüşleri gerektirir ( O(n)).

Yığınlar bir dizi üzerinde verimli şekilde uygulanabilir

Ebeveyn ve çocuk dizinleri, burada gösterildiği gibi mevcut dizinden hesaplanabilir .

BST gibi dengeleme işlemleri yoktur.

Min Sil, yukarıdan aşağıya doğru olması gereken en endişe verici işlemdir. Ancak her zaman burada açıklandığı gibi yığının tek bir dalını "aşağı çevirerek" yapılabilir . Yığın her zaman iyi dengelendiğinden, bu O (log (n)) en kötü duruma yol açar.

Kaldırdığınız her biri için tek bir düğüm ekliyorsanız, yığınların silme baskınlığı sağladığı gibi sağladığı asimptotik O (1) ortalama eklemenin avantajını kaybedersiniz ve bir BST kullanabilirsiniz. Dijkstra her düğüm için birkaç kez düğüm günceller, bu yüzden biz iyiyiz.

Dinamik dizi kümeleri vs işaretçi ağacı yığınları

Yığınlar işaretçi yığınlarının üstüne etkili bir şekilde uygulanabilir: https://stackoverflow.com/questions/19720438/is-it-possible-to-make-efficient-pointer-based-binary-heap-implementations

Dinamik dizi uygulaması daha fazla alan verimlidir. Her bir yığın öğesinin sadece bir gösterici içerdiğini varsayalım struct:

  • ağaç uygulaması her eleman için üç işaretçi saklamalıdır: ebeveyn, sol çocuk ve sağ çocuk. Yani hafıza kullanımı her zaman 4n(3 ağaç işaretçisi + 1 structişaretçi).

    Ağaç BST'leri ayrıca daha fazla dengeleme bilgisine ihtiyaç duyacaktır, örneğin siyah-kırmızılık.

  • dinamik dizi uygulamasının 2niki katına çıktıktan hemen sonra büyüklüğü olabilir . Yani ortalama olarak olacak 1.5n.

Öte yandan, ağaç yığınının en kötü durumda olması daha iyidir, çünkü destek dinamik dizisinin boyutunu iki katına kopyalamak en O(n)kötü durumu alırken, ağaç yığını her düğüm için yeni küçük tahsisler yapar.

Yine de, destek dizisinin iki katına çıkarılması O(1)itfa edilir, bu nedenle azami gecikme süresine varır. Burada belirtilen .

Felsefe

  • BST'ler, bir ebeveyn ile tüm soyundan gelenler arasında küresel bir özellik sürdürür (daha küçük, sağ daha büyük).

    Bir BST'nin üst düğümü, sürdürülmesi için küresel bilgi gerektiren orta elementtir (kaç tane daha küçük ve daha büyük element olduğunu bilmek).

    Bu global mülkün bakımı daha pahalıdır (log n insert), ancak daha güçlü aramalar sağlar (log n search).

  • Yığınlar, ebeveyn ile doğrudan çocuk arasında yerel bir özellik sağlar (ebeveyn> çocuklar).

    Bir yığının en üst notu, yalnızca korumak için yerel bilgi gerektiren (ebeveyni bilmek) büyük unsurdur.

Çift bağlantılı liste

İkili bağlantılı bir liste, ilk öğenin en büyük önceliğe sahip olduğu yığının alt kümesi olarak görülebilir, bu yüzden bunları burada da karşılaştıralım:

  • ekleme:
    • durum:
      • iki kat bağlantılı liste: Eklenen öğe, yalnızca bu öğelere işaretçilerimiz olduğundan ilk veya son olmalıdır.
      • İkili öbek: eklenen öğe herhangi bir pozisyonda bitebilir. Bağlantılı listeden daha az kısıtlayıcı.
    • süresi:
      • iki kat bağlantılı liste: O(1)Maddelere dair işaretçilerimiz bulunduğundan en kötü durum ve güncelleme gerçekten basittir.
      • ikili yığın: O(1)ortalama, bağlantılı listeden daha kötü. Daha genel yerleştirme pozisyonuna sahip olduğu için tradeoff.
  • arama: O(n)her ikisi için de

Bunun için bir kullanım durumu, öbek anahtarının geçerli zaman damgası olduğu durumdur: bu durumda, yeni girişler her zaman listenin başına gider. Böylece tam zaman damgasını tamamen unutabiliriz ve listedeki sırasını öncelik olarak tutabiliriz.

Bu, bir LRU önbelleğini uygulamak için kullanılabilir . Dijkstra gibi yığın uygulamaları için olduğu gibi , hangi düğümün hızlı bir şekilde güncelleneceğini bulmak için, anahtardan listenin ilgili düğümüne ek bir hashmap tutmak isteyeceksiniz.

Farklı Dengeli BST'nin karşılaştırılması

Asimptotik, şu ana kadar gördüğüm "Dengeli BST" olarak sınıflandırılan tüm veri yapıları için zaman ekleyip bulsa da aynı olsa da, farklı BBST'lerin farklı değişimleri var. Bunu henüz tam olarak çalışmadım, ancak bu takasları burada özetlemek iyi olur:

  • Kırmızı-siyah ağaç . 2019'dan beri en yaygın kullanılan BBST gibi görünüyor, örneğin GCC 8.3.0 C ++ uygulaması tarafından kullanılan
  • AVL ağacı . BST'den biraz daha dengeli görünüyor, bu nedenle gecikme bulmak için biraz daha pahalı bulgular karşılığında daha iyi olabilir. Wiki şöyle özetliyor: "AVL ağaçları genellikle kırmızı-siyah ağaçları ile karşılaştırılır, çünkü her ikisi de aynı işlemleri destekler ve temel işlemler için aynı zamana sahiptir. Yoğun arama uygulamaları için AVL ağaçları kırmızı-siyah ağaçlarından daha hızlıdır çünkü kırmızı-siyah ağaçlara benzer şekilde, AVL ağaçlarının boyları dengelidir, her ikisi de, genel olarak, hiçbir mu <1/2 için ne ağırlık dengeli ne de mu dengelidir, yani kardeş düğümleri büyük ölçüde olabilir. farklı sayıda soyundan. "
  • WAVL . Orijinal kağıt yeniden dengelemesi ve rotasyon operasyonlar sınırları açısından bu sürümün avantajları bahseder.

Ayrıca bakınız

CS'de benzer soru: İkili arama ağacı ile ikili yığın arasındaki fark nedir?


1
Mükemmel cevap. Yaygın olarak kullanılan yığın ortanca, k dk, en üst k elementlerdir. Bu en yaygın işlem için, min sonra çıkarın (genellikle birkaç saf uç işlemi ile küçük bir yığınımız vardır). Öyle görünüyor ki pratikte bu algoritmalar için BST'den daha iyi performans göstermiyor.
yura

1
Olağanüstü cevap !!! Temel yığın yapısı olarak deque kullanarak yeniden boyutlandırma sürelerini önemli ölçüde azaltabilirsiniz, ancak yine de O (n) en kötü durumudur, çünkü topaklara işaretçilerin (küçük) işaretçilerinin yeniden tahsis edilmesi gerekir.
Bulat,

13

Veri yapısı ile ilgili endişe düzeylerini ayırt etmek zorundasınız.

  1. Soyut veri yapıları , bu, söz konusu (saklı nesneler, operasyon) farklıdır. Biri öncelik sırasını, diğeri bir kümeyi uygular. Bir öncelik sırası, yalnızca en büyük önceliğe sahip olan rasgele bir öğe bulmakla ilgilenmez.

  2. Somut uygulama yapılarının. Burada ilk bakışta her ikisi de (ikili) ağaçlardır, ancak farklı yapısal özelliklere sahiptir. Hem anahtarların göreceli sıralaması hem de olası küresel yapılar farklıdır. (Bir miktar BSTanahtar içinde soldan sağa doğru sıralanır, bir yığın halinde yukarıdan aşağıya sıralanırlar.) IPlant'ın doğru bir şekilde söylediği gibi bir yığın da "tamamlanmış" olmalıdır.

  3. Düşük seviye uygulamasında son bir fark var . Dengesiz bir ikili arama ağacında, işaretçiler kullanılarak yapılan standart bir uygulama vardır. Aksine bir ikili yığın, bir diziyi kullanarak (tam olarak kısıtlanmış yapı nedeniyle) etkin bir uygulamaya sahiptir.


1

Önceki yanıtların üstünde, yığın, yığın yapısı özelliğine sahip olmalıdır; ağaç dolu olmalı ve her zaman dolu olamayacak en alttaki katman boşluk bırakmadan en soldan en sağa doldurulmalıdır.

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.