Yanıtlar:
İkili ağaçların performansı hakkında kavga etmek anlamsızdır - bunlar bir veri yapısı değil, hepsi farklı performans özelliklerine sahip bir veri yapıları ailesidir. O doğru olmakla birlikte dengesiz ikili ağaçlar çok daha kötü performans kendini dengeleyen ikili ağaçlar arama için birçok ikili ağaçlar vardır (örneğin ikili denemeden gibi) kendisi için "dengeleme" bir anlamı yoktur.
map
set
İkili ağaçların arama için n-ary ağaçlarından daha sık kullanılmasının nedeni, n-ary ağaçlarının daha karmaşık olmasıdır, ancak genellikle gerçek hız avantajı sağlamaz.
m
Düğümleri olan (dengeli) bir ikili ağaçta , bir seviyeden diğerine geçmek için bir karşılaştırma gerekir ve log_2(m)
toplam log_2(m)
karşılaştırmalar için seviyeler vardır .
Buna karşılık, n-ary ağacı bir sonraki seviyeye geçmek için log_2(n)
karşılaştırmalar (ikili arama kullanarak) gerektirir . Olduğundan log_n(m)
toplam seviyeleri, arama gerektirecektir log_2(n)*log_n(m)
= log_2(m)
karşılaştırmalar toplam. Bu nedenle, n-ary ağaçları daha karmaşık olsa da, gerekli toplam karşılaştırmalar açısından hiçbir avantaj sağlamazlar.
(Bununla birlikte, n-ary ağaçları niş durumlarda hala yararlıdır. Hemen akla gelen örnekler, dört düzey ağaçlar ve diğer boşluk bölümleme ağaçlarıdır, burada seviye başına yalnızca iki düğüm kullanarak alanı bölmek mantığı gereksiz yere karmaşık hale getirir; ve Sınırlama faktörünün her seviyede kaç karşılaştırma yapılması olmadığı, aynı anda sabit sürücüden kaç düğümün yüklenebildiği birçok veritabanında kullanılan B ağaçları )
Çoğu insan ikili ağaçlar hakkında konuştuğunda, ikili arama ağaçlarını düşünmekten daha sıktır , bu yüzden önce bunu ele alacağım.
Dengeli olmayan bir ikili arama ağacı aslında öğrencileri veri yapıları hakkında eğitmekten biraz daha faydalıdır. Yani en, çünkü veri nispeten rasgele sırada geliyor sürece, ağaç can basit ikili ağaçlar olduğundan, bağlantılı bir listedir onun kötü durum formu, kolayca dejenere değil dengeli.
İyi bir örnek: Bir keresinde verilerini manipülasyon ve arama için bir ikili ağaca yükleyen bazı yazılımları düzeltmek zorunda kaldım. Verileri sıralı biçimde yazdı:
Alice
Bob
Chloe
David
Edwina
Frank
böylece, geri okurken, aşağıdaki ağaç ile sona erdi:
Alice
/ \
= Bob
/ \
= Chloe
/ \
= David
/ \
= Edwina
/ \
= Frank
/ \
= =
dejenere olan formdur. O ağaçta Frank'i bulmaya gidersen, onu bulmadan önce altı düğümü de aramalısın.
İkili ağaçlar, onları dengelediğinizde arama yapmak için gerçekten yararlı olur. Bu, alt ağaçların kök düğümleri boyunca döndürülmesini içerir, böylece herhangi iki alt ağaç arasındaki yükseklik farkı 1'den küçük veya ona eşittir. Bir kerede bu adların dengeli bir ağaca eklenmesi size aşağıdaki sırayı verir:
1. Alice
/ \
= =
2. Alice
/ \
= Bob
/ \
= =
3. Bob
_/ \_
Alice Chloe
/ \ / \
= = = =
4. Bob
_/ \_
Alice Chloe
/ \ / \
= = = David
/ \
= =
5. Bob
____/ \____
Alice David
/ \ / \
= = Chloe Edwina
/ \ / \
= = = =
6. Chloe
___/ \___
Bob Edwina
/ \ / \
Alice = David Frank
/ \ / \ / \
= = = = = =
Girişler eklendikçe sola dönen tüm alt ağaçların (adım 3 ve 6'da) olduğunu görebilirsiniz ve bu size en kötü durum aramasının olduğu dengeli bir ikili ağaç verir. O(log N)
ziyade O(N
dejenere formu verir). Hiçbir noktada en yüksek NULL ( =
), en düşük değerden birden fazla düzeyle farklılık göstermez. Ve, yukarıdaki son ağacında (yalnızca üç düğümler bakarak Frank bulabilirsiniz Chloe
, Edwina
nihayet ve Frank
).
Tabii ki, ikili tress yerine dengeli çok yönlü ağaçlar yaptığınızda daha da yararlı olabilirler . Bu, her düğümün birden fazla öğe içerdiği anlamına gelir (teknik olarak, N öğeleri ve N + 1 işaretçileri tutarlar; ikili ağaç, 1 öğe ve 2 işaretçi içeren 1 yollu çok yönlü bir ağacın özel bir vakasıdır).
Üç yönlü bir ağaçla, aşağıdakilerle sonuçlanırsınız:
Alice Bob Chloe
/ | | \
= = = David Edwina Frank
/ | | \
= = = =
Bu genellikle bir öğe dizini için anahtarların korunmasında kullanılır. Bir düğümün tam olarak bir disk bloğunun boyutu (512 bayt) olduğu donanım için optimize edilmiş veritabanı yazılımı yazdım ve tek bir düğüme olabildiğince çok sayıda anahtar koydunuz. İşaretçileri bu durumda aslında indeks dosyasından ayrı bir sabit uzunlukta kayıt direkt erişim dosyasına kayıt numaralarına idi (o kadar rekor sayıda X
sadece isteyen bulunabileceğini X * record_length
).
Örneğin, işaretçiler 4 bayt ve anahtar boyutu 10 ise, 512 baytlık bir düğümdeki anahtar sayısı 36'dır. Bu, 36 tuş (360 bayt) ve 37 işaretçi (148 bayt) ile toplam 508 bayttır. Düğüm başına 4 bayt harcanır.
Çok yönlü tuşların kullanılması, iki fazlı aramanın karmaşıklığını ortaya çıkarır (düğümdeki doğru anahtarı bulmak için küçük bir sıralı (veya doğrusal ikili) aramayla birlikte doğru düğümü bulmak için çok yönlü arama), ancak avantajı bunun için oluşturduğundan daha az disk G / Ç işlemi yapar.
Bunu bellek içi bir yapı için yapmak için hiçbir neden göremiyorum, dengeli bir ikili ağaca yapışıp kodunuzu basit tutmanız daha iyi olur.
Ayrıca , veri kümeleriniz küçük olduğunda O(log N)
aşırı avantajların O(N)
gerçekten görünmediğini unutmayın. On beş kişiyi adres defterinizde saklamak için çok yönlü bir ağaç kullanıyorsanız, muhtemelen aşırıya kaçmış demektir. Avantajlar, son on yılda yüz bin müşterinizden gelen her sipariş gibi bir şey depoladığınızda gelir.
Büyük-O notasyonunun bütün amacı, N
sonsuzluğa yaklaşırken . Bazı insanlar buna katılmayabilir, ancak başka hiçbir şey mevcut olmadığı sürece veri kümelerinin belirli bir boyutun altında kalacağından eminseniz kabarcık sıralaması kullanmak bile uygundur :-)
İkili ağaçların diğer kullanımlarına gelince, aşağıdakiler gibi çok sayıda vardır:
Arama ağaçları için ne kadar açıklama ürettiğim göz önüne alındığında, diğerleri hakkında çok fazla ayrıntıya girmekten çekiniyorum, ancak arzu ederseniz, bunları araştırmak için yeterli olmalı.
Mors alfabesinin organizasyonu ikili bir ağaçtır.
İkili ağaç, her düğümün genellikle "sol" ve "sağ" olarak ayırt edilen en fazla iki alt düğüme sahip olduğu bir ağaç veri yapısıdır. Çocuklu düğümler üst düğümlerdir ve alt düğümler ebeveynlerine referanslar içerebilir. Ağacın dışında, varsa, "kök" düğüme (tüm düğümlerin atası) bir gönderme yapılır. Veri yapısındaki herhangi bir düğüme kök düğümünden başlanarak ve sol veya sağ alt öğeye yapılan referansları tekrar tekrar izleyerek erişilebilir. İkili ağaçta her düğümün derecesi en fazla iki olur.
İkili ağaçlar kullanışlıdır, çünkü resimde gördüğünüz gibi, ağaçta herhangi bir düğüm bulmak istiyorsanız, en fazla 6 kez bakmanız gerekir. Örneğin 24 nolu düğümü aramak isterseniz, kökten başlayacaksınız.
Bu arama aşağıda gösterilmiştir:
İlk geçişte tüm ağacın düğümlerinin yarısını hariç tutabileceğinizi görebilirsiniz. ikincisinde ise sol alt ağacın yarısı. Bu çok etkili aramalar yapar. Eğer bu 4 milyar element üzerinde yapıldıysa , sadece en fazla 32 kez arama yapmanız gerekir. Bu nedenle, ağaçta ne kadar çok öğe varsa, aramanız o kadar verimli olabilir.
Silme işlemleri karmaşık olabilir. Düğümün 0 veya 1 alt öğesi varsa, silinecek olanı hariç tutmak için bazı işaretçileri hareket ettirmektir. Ancak, 2 çocuklu bir düğümü kolayca silemezsiniz. Bu yüzden kısa bir yol alıyoruz. Diyelim ki 19 numaralı düğümü silmek istedik.
Sol ve sağ işaretçilerin nereye taşınacağını belirlemeye çalışmak kolay olmadığından, yerini alacak birini buluyoruz. Sol alt ağaca gidiyoruz ve gidebildiğimiz kadar sağa gidiyoruz. Bu bize silmek istediğimiz düğümün bir sonraki en büyük değerini verir.
Şimdi sol ve sağ işaretçiler hariç 18 içeriğin tümünü kopyalıyoruz ve orijinal 18 düğümü siyoruz.
Bu görüntüleri oluşturmak için, bir AVL ağacı, kendi kendini dengeleyen bir ağaç uyguladım, böylece herhangi bir zamanda ağacın yaprak düğümleri (çocuksuz düğümler) arasında en fazla bir seviye farkı vardır. Bu, ağacın eğri olmasını önler ve O(log n)
ekleme ve silme işlemleri için biraz daha fazla zaman harcayarak maksimum arama süresini korur .
İşte AVL ağacımın kendini mümkün olduğunca kompakt ve dengeli tuttuğunu gösteren bir örnek.
Sıralı bir dizide, O(log(n))
tıpkı bir ağaç gibi aramalar yine de sürer , ancak rastgele ekleme ve kaldırma, ağaç yerine O (n) alır O(log(n))
. Bazı STL kapları bu performans özelliklerini kendi avantajlarına göre kullanırlar, bu nedenle takma ve çıkarma süreleri maksimum sürer O(log n)
, bu da çok hızlıdır. Bu kutulardan bazıları şunlardır map
, multimap
, set
, ve multiset
.
Bir AVL ağacı için örnek kodu http://ideone.com/MheW8 adresinde bulabilirsiniz.
Ana uygulama ikili arama ağaçları . Bunlar, arama, ekleme ve kaldırma işlemlerinin çok hızlı olduğu bir veri yapısıdır ( log(n)
işlemler hakkında)
Bahsedilmemiş bir ikili ağacın ilginç bir örneği, özyinelemeli olarak değerlendirilen bir matematiksel ifadedir. Temelde pratik bir açıdan işe yaramaz, ancak bu tür ifadeleri düşünmenin ilginç bir yoludur.
Temel olarak ağacın her bir düğümü kendine özgü bir değere sahiptir veya çocuklarının değerleri üzerinde çalışarak özyinelemeli olarak değerlendirilir.
Örneğin, ifade (1+3)*2
şu şekilde ifade edilebilir:
*
/ \
+ 2
/ \
1 3
İfadeyi değerlendirmek için ebeveynin değerini isteriz. Bu düğüm sırayla değerlerini çocuklarından alır, bir artı operatörü ve sadece '2' içeren bir düğüm. Artı operatörü, değerlerini '1' ve '3' olan çocuklardan alır ve bunları ekleyerek 8'i döndüren çarpma düğümüne 4 döndürür.
İkili ağacın bu kullanımı, bir anlamda, lehçe gösterimini tersine çevirmeye benzer, çünkü işlemlerin yapıldığı sıra aynıdır. Ayrıca dikkat edilmesi gereken bir şey, bunun bir ikili ağaç olması gerekmediğidir, sadece en yaygın kullanılan operatörlerin ikili olmasıdır. En temel düzeyde, buradaki ikili ağaç aslında çok basit, tamamen işlevsel bir programlama dilidir.
Ben "saf" ikili ağaçlar için herhangi bir kullanım olduğunu sanmıyorum. (eğitim amaçları hariç) Kırmızı-Siyah ağaçlar veya AVL ağaçları gibi dengeli ikili ağaçlar çok daha kullanışlıdır, çünkü O (kütük) işlemlerini garanti ederler. Normal ikili ağaçlar bir liste (veya neredeyse liste) olabilir ve çok fazla veri kullanan uygulamalarda gerçekten yararlı değildir.
Dengeli ağaçlar genellikle haritaların veya kümelerin uygulanması için kullanılır. Bunu yapmanın daha iyi yolları olsa bile, O (nlogn) 'da sıralama için de kullanılabilirler.
Ayrıca arama / ekleme / silme için genellikle ikili arama ağaçlarından (dengeli veya değil) daha iyi performansa sahip karma tablolar kullanılabilir.
(Dengeli) ikili arama ağaçlarının yararlı olacağı bir uygulama, arama / ekleme / silme ve sıralama gerektiğinde olacaktır. Hazır yapı dengeli bir ağaç göz önüne alındığında, sıralama yerinde olabilir (neredeyse, özyineleme için gereken yığın alanını göz ardı ederek). Hala O (nlogn) olurdu, ancak daha küçük bir sabit faktör ve fazladan boşluk gerekmez (yeni dizi hariç, verilerin bir diziye konması gerektiğini varsayarsak). Öte yandan karma tablolar sıralanamaz (en azından doğrudan değil).
Belki de bir şey yapmak için bazı karmaşık algoritmalarda da yararlıdırlar, ama aklıma hiçbir şey gelmiyor. Daha fazla bulursam gönderimi düzenleyeceğim.
Fe B + ağaçları gibi diğer ağaçlar veritabanlarında yaygın olarak kullanılmaktadır.
En yaygın uygulamalardan biri, depolanan öğelere hızlı bir şekilde erişmek ve bunları aramak için verileri verimli bir şekilde sıralanmış biçimde saklamaktır. Örneğin std::map
veya std::set
C ++ Standart Kitaplığı'nda.
Veri yapısı olarak ikili ağaç, ifade ayrıştırıcılarının ve ifade çözücülerin çeşitli uygulamaları için yararlıdır.
Ayrıca, dizinleme gibi bazı veritabanı sorunlarını çözmek için de kullanılabilir.
Genel olarak, ikili ağaç, belirli ağaç tabanlı veri yapısının genel bir konseptidir ve farklı özelliklere sahip çeşitli spesifik ikili ağaç türleri oluşturulabilir.
İkili ağaçların en önemli uygulamalarından biri, dengeli ikili arama ağaçlarıdır:
Bu tür ağaçlar, her düğüm eklendiğinde veya silindiğinde dönüş gibi sol alt ağaç ve sağ alt ağaç yüksekliklerindeki farkın küçük tutulması özelliğine sahiptir.
Bu nedenle, ağacın toplam yüksekliği, oturum n sırasına göre kalır ve düğümlerin arama, ekleme ve silinmesi gibi işlemler O (oturum n) zamanında gerçekleştirilir. C ++ STL'si de bu ağaçları kümeler ve haritalar şeklinde uygular.
Modern donanımda, ikili bir ağaç kötü önbellek ve alan davranışı nedeniyle neredeyse her zaman yetersizdir. Bu aynı zamanda (yarı) dengeli varyantlar için de geçerlidir. Onları bulursanız, performansın sayılmadığı (veya karşılaştırma işlevinin baskın olduğu) veya tarihi veya cehalet nedenleriyle daha olasıdır.
Bir AST'nin temsili için ikili bir ağaç kullanan bir derleyici, ağacı postorder, inorder gibi ayrıştırmak için bilinen algoritmaları kullanabilir.Programcı kendi algoritmasını bulmak zorunda değildir. Kaynak dosya için bir ikili ağaç n-ary ağacından daha yüksek olduğu için, bina daha fazla zaman alır. Bu üretimi alın: selstmnt: = "eğer" "(" ifade ")" stmnt "ELSE" stmnt İkili bir ağaçta 3 seviye düğüm olacaktır, ancak n-ary ağacının 1 seviyesi (chids) olacaktır
Bu nedenle Unix tabanlı işletim sistemleri yavaştır.