İkili ağaçlara karşı karma tablolar


30

Bir sözlük uygularken ('Müşteri verilerini müşteri kimlikleriyle aramak istiyorum'), kullanılan tipik veri yapıları karma tabloları ve ikili arama ağaçlarıdır. Örneğin, C ++ STL kitaplığının (dengeli) ikili arama ağaçlarını kullanarak sözlükleri (onları haritalar olarak adlandırırlar) uyguladığını ve .NET çerçevesinin başlık altında karma tabloları kullandığını biliyorum.

Bu veri yapılarının avantajları ve dezavantajları nelerdir? Bazı durumlarda makul olan başka bir seçenek var mı?

Özellikle, anahtarların güçlü bir alt yapıya sahip olduğu durumlarla ilgilenmediğimi unutmayın, örneğin, hepsi 1 ile n arasında bir tamsayıdır.


1
Seni kızdıracağım ama sadece "1 ve n arasındaki tam sayılar" diyemezsin, çünkü bu durumda bir dizi diğer tüm veri yapılarını geçecektir :-). "Dizeler" adil görünüyor ve çoğu durumu kapsar.
jmad

@jmad o söyledi değildi bu durumda ilgi.
Joe

@Joe Bunu dikkate aldığımı açıkça düşündüm. Her neyse, mümkün olan en kötü anahtar örneğini vermek için bir sebep değil.
jmad

1
Aslına bakılırsa .NET, hem ağaçlar kullanılarak uygulanan sözlükler hem de karma tablolar kullanılarak uygulanan sözlükler içerir (ve 2011 standardından bu yana C ++ 'ı da içerir).
sepp2k 13.03.2012

Yanıtlar:


26

Bu konuyla ilgili kapsamlı bir tez yazılabilir; Sadece bazı belirgin noktaları ele alacağım ve diğer veri yapılarının tartışmasını da asgari düzeyde tutacağım (aslında birçok değişken var). Bu cevap boyunca , sözlükteki anahtarların sayısıdır.n

Kısa cevap, karma tabloların çoğu durumda daha hızlı olduğu , ancak en kötüsünde çok kötü olabileceğidir. Arama ağaçları , evcil hayvanların en kötü davranışları da dahil olmak üzere birçok avantaja sahiptir , ancak tipik durumlarda biraz daha yavaştır.

Dengeli ikili arama ağaçları oldukça düzgün bir karmaşıklığa sahiptir: her bir eleman ağaçta bir düğüm alır (tipik olarak 4 bellek hafızası) ve temel işlemler (arama, ekleme, silme) zaman alır (asimptotik garantilidir ) üst sınır). Daha doğrusu, ağaçtaki bir erişim sürer l o gO(lg(n)) karşılaştırılması.lOg2(n)

Karma tablolar biraz daha değişkendir. Yaklaşık işaretçilerden oluşan bir dizi gerektirir . Bir elemana erişim, hash fonksiyonunun kalitesine bağlıdır. Bir karma fonksiyonunun amacı elemanları dağıtmaktır. Bir depoda depolamak istediğiniz tüm elemanların farklı karmaları varsa “çalışır”. Bu durumda, temel işlemler (arama, ekleme, silme) , oldukça küçük bir sabitle (bir karma hesaplama artı bir işaretçi arama) O ( 1 ) zaman alır. Bu, birçok tipik durumda karma tabloları çok hızlı hale getirir.2nO(1)

Karma tablolarla ilgili genel bir sorun, karmaşıklığının garanti edilmemesidir.O(1)

  • Ek olarak, masanın dolduğu bir nokta var; bu olduğunda (veya daha iyisi, bundan biraz önce), maliyeti için tüm öğelerinin taşınmasını gerektiren tablonun büyütülmesi gerekir . Bu, birçok element eklendiğinde “sarsıntılı” davranışa neden olabilir.O(n)
  • Girişin birkaç karma değerin üzerine çarpışması mümkündür. Bu nadiren doğal olarak gerçekleşir, ancak girdiler bir saldırgan tarafından seçildiyse bu bir güvenlik sorunu olabilir: bu, bazı sunucuları oldukça yavaşlatmanın bir yoludur. Bu sorun bazı programlama dili uygulamalarının (Perl ve Python gibi) düz eski bir karma tablodan karma tablo oluştururken, bu karma veriyi iyi yayan bir karma işlevi ile birlikte, karma tablo oluşturulurken seçilen rasgele bir sayı içeren karma işlevine geçmesine neden olmuştur. (Bu, deki çarpım sabitini arttırır ) veya bir ikili arama ağacına. Kriptografik bir karma kullanarak çarpışmaları önleyebilirsiniz, ancak pratikte bu yapılmaz, çünkü şifreleme karma değerleri hesaplamak için nispeten çok yavaşlar.O(1)

Eğer attığımda veri yerellik karışımı içine, karma tabloları kötü yapmak. Tam olarak çalışırlar çünkü ilgili elemanları birbirinden uzakta saklarlar, yani uygulama bir öneki sırayla paylaşan öğeleri ararsa, önbellek etkilerinden faydalanmayacaktır. Uygulama esas olarak rasgele aramalar yaparsa, bu durum ilgili değildir.

Arama ağaçlarının lehine olan bir başka faktör de değişmez bir veri yapıları olmalarıdır : bir ağacın kopyasını almanız ve içindeki birkaç öğeyi değiştirmeniz gerekirse, veri yapısının çoğunu paylaşabilirsiniz. Bir karma tablosunun kopyasını alırsanız, tüm işaretçiler dizisini kopyalamanız gerekir. Ayrıca, tamamen işlevsel bir dilde çalışıyorsanız, karma tabloları genellikle bir seçenek değildir.

Eğer dizeleri ötesine gittiğinde karma tabloları ve ikili arama ağaçları anahtarının veri türüne farklı gereksinimleri olun: hash tabloları tamsayılar öyle ki anahtarları (a işlevi karma işlevi gerektiren k1k2h(k1)=h(k2)

Özellikle, tuşların sırasına ihtiyacınız olacaksa , örneğin tuşları alfabetik sırayla listelemek istiyorsanız, karma tabloların yardımı yoktur (sıralamanız gerekir), oysa Bir arama ağacını kolayca sırayla geçirebilir.

İkili arama ağaçları ve karma tabloları karma ağaçlar şeklinde birleştirebilirsiniz . Bir karma ağacı, anahtarları bir karma arama ağacında karma değerlerine göre saklar. Bu, örneğin, hesaplanması kolay bir düzen ilişkisi olmayan veriler üzerinde çalışmak istediğiniz tamamen işlevsel bir programlama dilinde faydalıdır.

Tuşları dizeleri (veya tamsayılar) olduğunda, bir tray başka seçenek olabilir. Bir ağaç ağacıdır, ancak bir arama ağacından farklı olarak dizine eklenir: anahtarı ikilik olarak yazarsınız ve 0 için sağa ve 1 için sağa gidersiniz. Bir erişimin maliyeti anahtarın uzunluğu ile orantılıdır. Denemeler, ara düğümleri çıkarmak için sıkıştırılabilir; Bu bir patricia trie veya radix ağacı olarak bilinir . Radix ağaçları, özellikle birçok anahtarın ortak bir ön eki paylaşması durumunda, dengeli ağaçlardan daha iyi performans gösterir.


2
BST’lerin de kötü veri konumları yok mu?
svick

@svick Düğümlerin nasıl tahsis edildiğine bağlı olarak onlar olabilir veya olmayabilir. Ağacın arititesini artırmak, çalışma süresinden ödün vermeden yardımcı olabilir (maliyet daha büyük ve daha karmaşık koddur).
Gilles 'SO- kötülük' dur

2
Bir BST'de elementleri "sırayla" elde etmek kolaydır, karma tablo için söz konusu olmaz.
von

Güvenlik nedenleri dışında, ortalama durumları ikili ağaçlarınkinden daha iyiyse neden karma tabloların kötü durumda olması önemli değil? Kullanıcının / kullanıcının rahatlığının ağacın ne kadar sürede biteceği ile kabaca doğrusal bir ilişkisinin olduğunu hayal ediyorum, bu nedenle beklenen (ortalama) değer önemli olan her şey olmalı.
Kelmikra

@ Kyth'Py1k “Bitecek ağaç” ile ne demek istiyorsun? Karma tabloların amacı, ağacın tamamına değil bir kerede bir değere erişmektir, aksi takdirde bir liste veya dizi daha iyi çalışır. Ortalama değerin önemli olduğu durumlarda (örneğin, her zaman durum böyle değildir, örneğin gerçek zamanlı kısıtlamalar olduğunda), genellikle tablo üzerinde tekdüze olmayan belirli bir durumda yapılan isteklerin ortalamasıdır. - örneğin belirli bir ön eke önyargılı olma.
Gilles 'SO- kötülük' dur
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.