Std :: map neden kırmızı-siyah ağaç olarak uygulanmaktadır?


195

Neden olduğu std::mapbir olarak uygulanır kırmızı-siyah ağacın ?

Orada birkaç dengeli ikili arama ağacı (BST) vardır. Kırmızı-siyah ağaç seçiminde tasarım değişimleri nelerdi?


26
Gördüğüm tüm uygulamalar bir RB ağacı kullanıyor olsa da, bunun yine de uygulamaya bağlı olduğunu unutmayın.
Thomas

3
@Thomas. Uygulamaya bağımlıdır, neden tüm uygulama RB ağaçlarını kullansın?
Denis Gorodetskiy

1
Herhangi bir STL uygulayıcısının bir Atlama Listesi kullanmayı düşünüp düşünmediğini gerçekten bilmek istiyorum.
Matthieu M.Mar

2
C ++ 'ın haritası ve seti aslında sıralı harita ve sıralı kümedir. Karma işlevleri kullanılarak uygulanmazlar. Her sorgu alır O(logn)ve almaz O(1), ancak değerler her zaman sıralanır. C ++ 11'den başlayarak (sanırım), karma işlevleri kullanılarak uygulanan unordered_mapve var olan ve unordered_setsıralanmamış olsa da, çoğu sorgu ve işlem O(1)(ortalama olarak) mümkündür
SomethingSomething

@ Bu doğru, ama pratikte o kadar da ilginç değil. Standart, belirli bir algoritma veya bir dizi algoritma ile karmaşıklık garantileri verir.
Justin Meiners

Yanıtlar:


126

Muhtemelen en yaygın iki kendi kendini dengeleyen ağaç algoritması Kırmızı-Siyah ağaçlar ve AVL ağaçlar . Bir ekleme / güncellemeden sonra ağacı dengelemek için her iki algoritma da yeniden dengelemeyi gerçekleştirmek için ağacın düğümlerinin döndürüldüğü rotasyon kavramını kullanır.

Her iki algoritmada ekleme / silme işlemleri O (log n) iken, Kırmızı-Siyah ağaç yeniden dengeleme rotasyonu O (1) işlemi iken AVL ile bu bir O (log n) işlemidir. Kırmızı-Siyah ağaç, yeniden dengeleme aşamasının bu yönünde daha verimli ve daha yaygın olarak kullanılmasının olası nedenlerinden biridir.

Kırmızı-Siyah ağaçlar, Java ve Microsoft .NET Framework'ün sundukları da dahil olmak üzere çoğu koleksiyon kitaplığında kullanılır.


54
kırmızı-siyah ağaçların O (1) zamanında ağaç modifikasyonları yapabileceği gibi geliyorsunuz, bu doğru değil. ağaç modifikasyonları hem kırmızı-siyah hem de AVL ağaçları için O (log n) şeklindedir. ana işlem zaten O (log n) olduğu için ağaç modifikasyonunun dengeleme kısmının O (1) veya O (log n) olup olmadığını tartışır. AVL ağaçlarının yaptığı biraz ekstra işten sonra bile, biraz daha hızlı aramalara yol açan daha sıkı bir ağaçla sonuçlanır. bu yüzden tamamen geçerli bir ödünleşmedir ve AVL ağaçlarını kırmızı-siyah ağaçlardan daha aşağı yapmaz.
necromancer

35
Farkı görmek için gerçek çalışma zamanının karmaşıklığının ötesine bakmanız gerekir - AVL ağaçları, ekleme / silme işlemlerinden çok daha fazla arama olduğunda genellikle daha düşük toplam çalışma süresine sahiptir. Daha fazla kesici uç / silme olduğunda RB ağaçlarının toplam çalışma süresi daha düşüktür. Ara vermenin tam olarak gerçekleştiği oran, elbette uygulama, donanım ve kesin kullanımın birçok detayına bağlıdır, ancak kütüphane yazarları çok çeşitli kullanım modellerini desteklemesi gerektiğinden, eğitimli bir tahminde bulunmaları gerekir. AVL'nin uygulanması da biraz daha zordur, bu nedenle kullanmak için kanıtlanmış bir fayda isteyebilirsiniz.
Steve Jessop

6
RB ağacı "varsayılan uygulama" değildir. Her uygulayıcı bir uygulama seçer. Bildiğimiz kadarıyla, hepsi RB ağaçlarını seçti, bu yüzden muhtemelen bu performans veya uygulama / bakım kolaylığı için. Dediğim gibi, performans için kırılma noktası, aramalardan daha fazla ek / silme olduğunu düşündükleri anlamına gelmeyebilir, sadece ikisi arasındaki oran, RB'nin AVL'yi geçtiği düşünülen seviyenin üzerindedir.
Steve Jessop

9
@Denis: Maalesef sayı almanın tek yolu, std::mapuygulamaların bir listesini yapmak , geliştiricileri izlemek ve karar vermek için hangi kriterleri kullandıklarını sormaktır, bu yüzden spekülasyon kalır.
Steve Jessop

4
Bütün bunlardan eksik, düğüm başına denge kararları vermek için gerekli yardımcı bilgiyi saklama maliyetidir. Kırmızı-Siyah ağaçlar rengi temsil etmek için 1 bit gerektirir. AVL ağaçları en az 2 bit gerektirir (-1, 0 veya 1'i temsil etmek için).
SJHowe

47

Gerçekten kullanıma bağlıdır. AVL ağacı genellikle daha fazla yeniden dengeleme rotasyonuna sahiptir. Uygulamanızın çok fazla ekleme ve silme işlemi yoksa, ancak aramalarda ağırlıklar varsa, AVL ağacı muhtemelen iyi bir seçimdir.

std::map düğüm ekleme / silme ve arama hızı arasında makul bir denge elde ettiği için Kırmızı-Siyah ağacı kullanır.


1
Bundan emin misin??? Ben şahsen Kırmızı-Siyah ağacın ya daha karmaşık olduğunu ya da hiç olmadığı kadar basit olduğunu düşünüyorum. Tek şey, Rd-Black ağacında, yeniden dengeleme AVL'den daha az gerçekleşir.
Eric Ouellet

1
@Erik Teorik olarak, hem R / B ağacı hem de AVL ağacı, ekleme ve silme için O (log n) karmaşıklığına sahiptir. Ancak işletme maliyetinin büyük bir kısmı, bu iki ağaç arasında farklı olan rotasyon. Lütfen tartışın . Fogcreek.com/joelonsoftware/… Alıntı: "bir AVL ağacının dengelenmesi O (log n) rotasyonlarını gerektirebilirken, kırmızı siyah bir ağaç dengeye getirmek için en fazla iki rotasyon alacaktır ( rotasyonların nerede gerekli olduğuna karar vermek için O (log n) düğümlerini inceleyin). " Yorumlarımı buna göre düzenledi.
webbertiger

27

AVL ağaçları maksimum 1.44logn yüksekliğe sahipken, RB ağaçları maksimum 2logn'a sahiptir. Bir AVL'ye eleman eklemek, ağacın bir noktasında yeniden dengeleme anlamına gelebilir. Yeniden dengeleme ekleme işlemini tamamlar. Yeni bir yaprağın yerleştirilmesinden sonra, bu yaprağın atalarının köküne kadar veya iki alt ağacın eşit derinlikte olduğu bir noktaya kadar güncellenmesi gerekir. Düğüm düğümlerini güncelleme olasılığı 1/3 ^ k'dır. Yeniden dengeleme O (1) 'dir. Bir elemanın çıkarılması birden fazla yeniden dengeleme anlamına gelebilir (ağacın derinliğinin yarısına kadar).

RB ağaçları, ikili arama ağaçları olarak temsil edilen 4. dereceden B ağaçlarıdır. B ağacındaki 4 düğüm, eşdeğer BST'de iki seviye ile sonuçlanır. En kötü durumda, ağacın tüm düğümleri 2 düğümdür, sadece 3 düğümlü bir zincir bir yaprağa düşer. Bu yaprak kökten 2logn mesafede olacak.

Kökten ekleme noktasına inerken, herhangi bir eklemenin bir yaprağı doyuramayacağından emin olmak için 4 düğümü 2 düğüme dönüştürmek gerekir. Eklemeden geri dönersek, tüm bu düğümlerin 4 düğümü doğru bir şekilde temsil ettiklerinden emin olmak için analiz edilmelidir. Bu ağaçta aşağı inerek de yapılabilir. Küresel maliyet aynı olacaktır. Ücretsiz öğle yemeği yok! Bir öğeyi ağaçtan kaldırmak aynı sıradadır.

Tüm bu ağaçlar, düğümlerin boy, ağırlık, renk vb. Hakkında bilgi taşımasını gerektirir. Sadece Splay ağaçları bu tür ek bilgiler içermez. Ancak çoğu insan yapısının ramdomitesinden dolayı Splay ağaçlarından korkuyor!

Son olarak, ağaçlar ayrıca ağırlık dengelemesine izin vererek düğümlerde ağırlık bilgileri taşıyabilir. Çeşitli şemalar uygulanabilir. Bir alt ağaç, diğer alt ağacın eleman sayısının 3 katından fazla olduğunda yeniden dengelenmelidir. Yeniden dengeleme işlemi tek veya çift dönüşle tekrar yapılır. Bu en kötü 2.4logn anlamına gelir. Biri 3 yerine 2 kez, çok daha iyi bir oranla kurtulabilir, ancak burada ve orada dengesiz olan alt ağaçların% 1'inden biraz daha azını bırakmak anlamına gelebilir. Zor!

Hangi ağaç türü en iyisidir? Kesinlikle AVL. Kodlanması en basit olanlarıdır ve en kötü yükseklikleri logn'a en yakındır. 1000000 elemanlık bir ağaç için, bir AVL en fazla 29, RB 40 ve ağırlıkça 36 veya 50 dengeye bağlı olarak orana bağlı olacaktır.

Başka birçok değişken vardır: rasgelelik, ekleme oranı, silme, arama, vb.


2
İyi cevap. Ama eğer AVL'ler en iyiyse, standart kütüphane neden std :: map'i RB ağacı olarak uygular?
Denis Gorodetskiy

14
AVL ağaçlarının tartışmasız en iyisi olduğunu kabul etmiyorum. Düşük bir yüksekliğe sahip olmalarına rağmen, yeniden etiketleme yapmak için kırmızı / siyah ağaçlardan (O (log n) yeniden dengeleme işi yerine O (1) itfa edilmiş yeniden dengeleme işi) daha fazla çalışma gerektirirler. Yayvan ağaçlar çok, çok daha iyi olabilir ve insanların onlardan korktuğuna dair iddianız temelsizdir. Orada tek bir evrensel "en iyi" ağaç dengeleme şeması yoktur.
templatetypedef

Neredeyse mükemmel cevap. AVL'nin neden en iyisi olduğunu söyledin? Bu sadece yanlıştır ve bu yüzden çoğu genel uygulama Kırmızı-Siyah ağacı kullanır. AVL'yi seçmek için oldukça yüksek oranda okuma manipülasyonuna sahip olmanız gerekir. Ayrıca, AVL'nin RB'den biraz daha az bellek alanı vardır.
Eric Ouellet

AVL'nin çoğu durumda daha iyi olma eğiliminde olduğunu kabul ediyorum, çünkü genellikle ağaçlar yerleştirildiklerinden daha sık aranır. RB ağacı, çoğunlukla yazma durumunda hafif bir avantajı olan ve daha da önemlisi, çoğunlukla okunan durumda hafif bir dezavantajlı olduğu zaman neden daha iyi olarak kabul edilir? Bulacağınızdan daha fazlasını ekleyeceğinize gerçekten inanılıyor mu?
doug65536

25

Önceki cevaplar sadece ağaç alternatiflerini ele alır ve kırmızı siyah muhtemelen sadece tarihsel nedenlerle kalır.

Neden karma tablo yok?

Bir tür, yalnızca <işlecin (karşılaştırma) ağaçta anahtar olarak kullanılmasını gerektirir . Ancak, karma tablolar her anahtar türünün hashtanımlanmış bir işleve sahip olmasını gerektirir . Çok çeşitli tür ve algoritmalarla kullanabilmeniz için genel programlama için tür gereksinimlerini minimumda tutmak çok önemlidir.

İyi bir karma tablo tasarlamak, kullanılacağı bağlam hakkında derinlemesine bilgi gerektirir. Açık adresleme mi yoksa bağlantılı zincirleme mi kullanmalı? Yeniden boyutlandırmadan önce hangi yük seviyelerini kabul etmelidir? Çarpışmalardan kaçınan pahalı bir karma mı yoksa kaba ve hızlı bir karma mı kullanmalı?

STL, uygulamanız için en iyi seçim olanı tahmin edemediğinden, varsayılanın daha esnek olması gerekir. Ağaçlar "sadece çalışır" ve güzel ölçeklenir.

(C ++ 11 ile karma tablolar ekledi unordered_map. Bu seçeneklerin birçoğunu yapılandırmak için ilkelerin ayarlanmasını gerektiren belgelerden görebilirsiniz .)

Peki ya diğer ağaçlar?

Kırmızı Siyah ağaçlar, BST'lerin aksine hızlı arama sunar ve kendi kendini dengeliyor. Başka bir kullanıcı, kendi kendini dengeleyen AVL ağacı üzerindeki avantajlarına dikkat çekti.

Alexander Stepanov (STL'nin yaratıcısı), std::maptekrar yazdığı takdirde Kırmızı-Siyah ağaç yerine B * Ağacı kullanacağını söyledi , çünkü modern bellek önbellekleri için daha dostudur.

O zamandan bu yana yapılan en büyük değişikliklerden biri önbelleklerin büyümesi oldu. Önbellek özledikleri çok maliyetlidir, bu nedenle referans yeri artık çok daha önemlidir. Referans konumu düşük olan düğüm tabanlı veri yapıları çok daha az mantıklıdır. Bugün STL tasarlayacak olsaydım, farklı bir konteyner setim olurdu. Örneğin, bellek içi bir B * ağacı, bir ilişkisel kap uygulamak için kırmızı-siyah bir ağaçtan çok daha iyi bir seçimdir. - Alexander Stepanov

Haritalar her zaman ağaç kullanmalı mı?

Başka bir olası harita uygulaması, sıralı bir vektör (ekleme sıralaması) ve ikili arama olacaktır. Bu, sık sık değiştirilmeyen ancak sık sık sorgulanan kaplar için iyi çalışır. Bunu genellikle C olarak qsortve bsearchyerleşik olarak yaparım .

Haritayı kullanmam bile gerekiyor mu?

Önbellek değerlendirmeleri, nadiren std::listveya daha std::dequefazla kullanımın mantıklı olduğu anlamına gelirstd:vector , okulda bize öğretilen bu durumlar için bile (listenin ortasından bir öğenin kaldırılması gibi) . Aynı akıl yürütmeyi uygulamak, bir listeyi doğrusal aramaya kullanmak için for döngüsü kullanmak, birkaç arama için bir harita oluşturmaktan genellikle daha verimli ve daha temizdir.

Tabii ki okunabilir bir kap seçmek genellikle performanstan daha önemlidir.


3

Güncelleme 2017-06-14: Ben yorum yaptıktan sonra webbertiger cevabını düzenleyin. Cevabının şimdi gözlerim için çok daha iyi olduğunu belirtmeliyim. Ama cevabımı ek bilgi olarak sakladım ...

İlk cevabın yanlış olduğunu düşündüğümden (düzeltme: artık her ikisi de değil) ve üçüncüsünün yanlış bir doğrulaması var. Bazı şeyleri açıklığa kavuşturmak zorunda olduğumu hissediyorum ...

En popüler 2 ağaç AVL ve Kırmızı Siyah (RB). Temel fark kullanımda yatmaktadır:

  • AVL: İstişare oranı (okuma) manipülasyondan (modifikasyon) daha büyükse daha iyidir. Bellek ayağı baskısı RB'den biraz daha azdır (renklendirme için gereken bit nedeniyle).
  • RB: Danışma (okuma) ve manipülasyon (değiştirme) veya danışma üzerinde daha fazla değişiklik arasında bir denge olduğu genel durumlarda daha iyi. Kırmızı-siyah bayrağın depolanması nedeniyle biraz daha büyük bir bellek alanı.

Ana fark renklendirmeden gelir. RB ağacında AVL'den daha az yeniden dengeleme eyleminiz var, çünkü renklendirme göreceli bir yüksek maliyete sahip yeniden dengeleme eylemlerini bazen atlamanızı veya kısaltmanızı sağlar. Renklendirme nedeniyle, RB ağacı da daha yüksek düğüm seviyesine sahiptir, çünkü siyah olanlar arasındaki kırmızı düğümleri kabul edebilir (~ 2x daha fazla seviyeye sahip olabilir) arama (okuma) biraz daha az verimli hale getirir ... sabit (2x), O'da kalır (log n).

Bir ağacın modifikasyonu için isabetli performansı (anlamlı) VS bir ağacın konsültasyonunun performans isabetini (neredeyse önemsiz) düşünürseniz, genel bir durum için AVL yerine RB'yi tercih etmek doğal hale gelir.


2

Bu sadece uygulamanızın seçimidir - herhangi bir dengeli ağaç olarak uygulanabilirler. Çeşitli seçeneklerin tümü küçük farklarla karşılaştırılabilir. Bu nedenle, herhangi biri herhangi bir şey kadar iyidir.

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.