Vorteile von binären Suchbäumen gegenüber Hash-Tabellen


101

Was sind die Vorteile von binären Suchbäumen gegenüber Hash-Tabellen?

Hash-Tabellen können jedes Element in Theta (1) -Zeit nachschlagen, und es ist genauso einfach, ein Element hinzuzufügen ... aber ich bin mir nicht sicher, welche Vorteile umgekehrt auftreten.


Wie lauten die Laufzeiten für Hash-Tabellen für find () insert () und remove ()? Theta (1) Theta (1) und Theta (1) richtig?
Ergeben

8
Fast immer ja. Wenn Sie auf viele Kollisionen stoßen, können diese Zeiten auf O (n) anwachsen.
Christian Mann

1
Diese Zeiten hängen auch von Ihrer Hashing-Funktion ab. Wenn es aus irgendeinem seltsamen Grund nicht O (1) ist, haben Ihre Operationen offensichtlich eine Mindestgrenze für die Effizienz, mit der Ihre Hash-Funktion ausgeführt wird.
Christian Mann

Ich würde sagen, der größte Vorteil von BST liegt in einer sortierten Datenstruktur. Detaillierter Anwendungsfall, der hier bereits aufgeführt ist .
Yuantao

Mögliches Duplikat von
Binärbäumen

Antworten:


93

Denken Sie daran, dass binäre Suchbäume (referenzbasiert) speichereffizient sind. Sie reservieren nicht mehr Speicher als nötig.

Wenn eine Hash-Funktion beispielsweise einen Bereich hat R(h) = 0...100, müssen Sie ein Array von 100 (Zeiger auf) Elementen zuweisen, selbst wenn Sie nur 20 Elemente hashen. Wenn Sie einen binären Suchbaum zum Speichern derselben Informationen verwenden würden, würden Sie nur so viel Speicherplatz zuweisen, wie Sie benötigen, sowie einige Metadaten zu Links.


33
Es ist nicht wahr, dass der gesamte Bereich der Hash-Funktionsausgaben im Array vorhanden sein muss. Die Hash-Werte können einfach um die Länge des Arrays geändert werden, um ein kleineres Array zu ermöglichen. Natürlich ist die endgültige Anzahl der hinzugefügten Elemente möglicherweise nicht bekannt, sodass die Hash-Tabelle möglicherweise immer noch mehr Speicherplatz als erforderlich zuweist. Binäre Suchbäume können jedoch genauso viel Speicher oder mehr verschwenden. Verknüpfte Implementierungen benötigen Platz für mindestens zwei zusätzliche Zeiger pro Element (drei, wenn ein übergeordneter Zeiger verwendet wird), und Array-basierte BSTs können viel Speicher für nicht ausgefüllte Teile des Baums verschwenden.
Solaraeus

4
@Solaraeus: Array-basierte BSTs lassen sich am besten mit Hash-Tabellen vergleichen und sind nicht verschwenderischer als Hash-Tabellen. Sie können eine BST auch mit etwas mehr als einer Speicherkopie erweitern, verglichen mit der Neuberechnung der gesamten Tabelle.
Guvante

125

Ein Vorteil, auf den noch niemand hingewiesen hat, ist, dass Sie mit dem binären Suchbaum effizient nach Bereichen suchen können.

Um meine Idee zu veranschaulichen, möchte ich einen Extremfall machen. Angenommen, Sie möchten alle Elemente erhalten, deren Schlüssel zwischen 0 und 5000 liegen. Tatsächlich gibt es nur ein solches Element und 10000 andere Elemente, deren Schlüssel nicht im Bereich liegen. BST kann Entfernungssuchen sehr effizient durchführen, da es keinen Teilbaum durchsucht, auf den die Antwort unmöglich ist.

Wie können Sie eine Bereichssuche in einer Hash-Tabelle durchführen? Sie müssen entweder jeden Bucket Space iterieren, der O (n) ist, oder Sie müssen prüfen, ob jeder von 1,2,3,4 ... bis zu 5000 vorhanden ist. (Was ist mit den Schlüsseln zwischen 0 und 5000, die unendlich sind? Zum Beispiel können Schlüssel Dezimalstellen sein.)


11
BSTs führen Reichweitensuchen effizient durch! Für mich ist dies die beste Antwort auf den praktischen und algorithmischen Ansatz.
Ady

4
Wow, das erklärt wirklich, warum Bäume so mit Datenbanken verbunden sind. Ihre Vorteile werden am deutlichsten sichtbar, wenn Sie eine schlüsselbasierte Filterung durchführen müssen. Bei Hash-Maps müssen Sie alle Schlüssel durchlaufen, um "Alle Elemente mit Schlüssel zwischen 1000 und 3290 finden" zu lösen
Dmitry

77

Ein "Vorteil" eines Binärbaums besteht darin, dass er durchlaufen werden kann, um alle Elemente der Reihe nach aufzulisten. Dies ist mit einer Hash-Tabelle nicht unmöglich, aber keine normale Operation, die in eine Hash-Struktur umgewandelt wird.


3
Das Durchlaufen in beliebiger Reihenfolge würde auf einem Hashtisch wahrscheinlich keinen Sinn ergeben.
FrustratedWithFormsDesigner

2
@FrustratedWithFormsDesigner. Siehe Sortierte lineare Hash-Tabelle
NealB

Danke für den Link, das ist eine interessante Idee! Ich glaube nicht, dass ich jemals eine Implementierung davon gesehen oder verwendet habe (zumindest nicht wissentlich).
FrustratedWithFormsDesigner


51

Neben all den anderen guten Kommentaren:

Hash-Tabellen weisen im Allgemeinen ein besseres Cache-Verhalten auf, das im Vergleich zu einem Binärbaum weniger Speicherlesevorgänge erfordert. Bei einer Hash-Tabelle wird normalerweise nur ein einziger Lesevorgang ausgeführt, bevor Sie auf eine Referenz zugreifen können, die Ihre Daten enthält. Der Binärbaum erfordert, wenn es sich um eine ausgeglichene Variante handelt, etwas in der Größenordnung von k * lg (n), das der Speicher für eine Konstante k liest.

Wenn andererseits ein Feind Ihre Hash-Funktion kennt, kann der Feind Ihre Hash-Tabelle dazu zwingen, Kollisionen zu verursachen, was seine Leistung erheblich beeinträchtigt. Die Problemumgehung besteht darin, die Hash-Funktion zufällig aus einer Familie auszuwählen, aber ein BST hat diesen Nachteil nicht. Wenn der Druck in der Hash-Tabelle zu stark ansteigt, neigen Sie häufig dazu, die Hash-Tabelle zu vergrößern und neu zuzuweisen, was eine teure Operation sein kann. Das BST hat hier ein einfacheres Verhalten und neigt nicht dazu, plötzlich viele Daten zuzuweisen und einen Aufwärmvorgang durchzuführen.

Bäume sind in der Regel die ultimative durchschnittliche Datenstruktur. Sie können als Listen fungieren, können für den Parallelbetrieb leicht aufgeteilt werden, können schnell entfernt, eingefügt und in der Größenordnung von O (lg n) nachgeschlagen werden . Sie machen nichts besonders gut, aber sie haben auch kein übermäßig schlechtes Verhalten.

Schließlich sind BSTs in (reinen) funktionalen Sprachen im Vergleich zu Hash-Tabellen viel einfacher zu implementieren und erfordern keine Implementierung destruktiver Aktualisierungen (das Persistenzargument von Pascal oben).


3
BSTs are much easier to implement in (pure) functional languages compared to hash-tables- Ja wirklich? Ich möchte jetzt eine funktionale Sprache lernen!
Nawfal

1
Die Hash-Tabelle muss in einer funktionalen Sprache persistent sein. Dies erschwert häufig die Implementierung.
Ich gebe Crap Antworten

Wenn Sie President-Datenstrukturen in funktionalen Sprachen erstellen, müssen Sie am Ende nur denselben Code schreiben, den Sie in der Assembly verwenden würden, außer dass Sie bei jeder Operation Ihr Array von Speichern / Registern explizit transformieren oder mit einem Server sprechen, um dies vorzutäuschen das zu tun. Ich bin alles dafür, dass ich mir Ihres Zustands bewusst bin, aber es ist isomorph zum imperativen Ansatz, wenn es richtig gemacht wird (Sie können nicht realistisch eine große Datenmenge über jede Transformation im wirklichen Leben kopieren, Sie müssen betrügen).
Dmitry

27

Die Hauptvorteile eines Binärbaums gegenüber einer Hash-Tabelle bestehen darin, dass der Binärbaum Ihnen zwei zusätzliche Operationen bietet, die Sie mit einer Hash-Tabelle nicht (einfach, schnell) ausführen können

  • Finden Sie das Element, das einem beliebigen Schlüsselwert am nächsten liegt (oder nicht unbedingt gleich ist) (oder am nächsten über / unter).

  • Durchlaufen Sie den Inhalt des Baums in sortierter Reihenfolge

Die beiden sind miteinander verbunden - der Binärbaum hält seinen Inhalt in einer sortierten Reihenfolge, sodass Dinge, die diese sortierte Reihenfolge erfordern, einfach zu erledigen sind.


BST findet die engste Übereinstimmung nur, wenn die genaue Übereinstimmung nicht existiert, oder? Was ist, wenn Sie eine genaue Übereinstimmung an der Wurzel selbst finden?
Entwickler747

2
@ developer747: Dann sind die nächstgelegenen unten und oben das am weitesten rechts stehende Blatt des linken Teilbaums und das am weitesten links stehende Blatt des rechten Teilbaums.
Chris Dodd

16

Ein (ausgeglichener) binärer Suchbaum hat auch den Vorteil, dass seine asymptotische Komplexität tatsächlich eine Obergrenze darstellt, während die "konstanten" Zeiten für Hash-Tabellen amortisierte Zeiten sind: Wenn Sie eine ungeeignete Hash-Funktion haben, können Sie sich auf eine lineare Zeit verschlechtern eher als konstant.


3
Um diesen Punkt nach Hause zu bringen, ist ein entarteter Fall, wenn die Sammlung viele Kopien von nur einem Schlüssel enthält. in der BST ist Einfügen O (log n), in einer Hash-Tabelle ist Einfügen O (n)
SingleNegationElimination

2
Wenn eine Hash-Tabelle viele Kopien von nur 1 Schlüssel enthält, ist insert (noch) O (1), nicht O (n). Das Problem bei Hash-Tabellen besteht darin, dass es viele verschiedene Schlüssel mit demselben Hash gibt. Dies kann durch ein dynamisches Hash-Schema vermieden werden, das bei vielen Kollisionen zu einer anderen Hash-Funktion wechselt.
Chris Dodd

Beachten Sie, dass ein unausgeglichener Baum in eine Liste ausarten und auch nach O (n) suchen kann.
Awiebe

9

Eine Hashtabelle würde beim erstmaligen Erstellen mehr Platz beanspruchen - sie verfügt über verfügbare Slots für die Elemente, die noch eingefügt werden müssen (unabhängig davon, ob sie jemals eingefügt wurden oder nicht). Ein binärer Suchbaum ist nur so groß, wie er benötigt wird Sein. Auch wenn eine Hash-Tabelle mehr Platz benötigt, auf einem andere Struktur erweitern könnte zeitaufwändig sein, aber das könnte bei der Umsetzung abhängen.


8

Ein binärer Suchbaum kann mit einer persistenten Schnittstelle implementiert werden, wobei ein neuer Baum zurückgegeben wird, der alte Baum jedoch weiterhin vorhanden ist. Sorgfältig implementiert, teilen sich die alten und neuen Bäume die meisten ihrer Knoten. Sie können dies nicht mit einer Standard-Hash-Tabelle tun.


6

Ein binärer Baum ist langsamer zu suchen und einzufügen, hat jedoch die sehr schöne Funktion der Infix-Durchquerung, was im Wesentlichen bedeutet, dass Sie die Knoten des Baums in einer sortierten Reihenfolge durchlaufen können.

Das Durchlaufen der Einträge einer Hash-Tabelle macht einfach nicht viel Sinn, da sie alle im Speicher verstreut sind.


6

Aus Cracking the Coding Interview, 6. Ausgabe

Wir können die Hash-Tabelle mit einem Balanced Binary Search Tree (BST) implementieren. Dies gibt uns eine O (log n) Suchzeit. Dies hat möglicherweise den Vorteil, dass weniger Speicherplatz benötigt wird, da kein großes Array mehr zugewiesen wird. Wir können die Schlüssel auch der Reihe nach durchlaufen, was manchmal nützlich sein kann.


5

BSTs bieten auch die Operationen "findPredecessor" und "findSuccessor" (um die nächstkleineren und nächstgrößeren Elemente zu finden) in O (logn) -Zeit, was ebenfalls sehr praktisch sein kann. Hash Table kann in dieser Zeit keine Effizienz bieten.


Wenn Sie nach den Operationen "findPredecessor" und "findSuccessor" suchen, ist HashTable in erster Linie eine schlechte Wahl für die Datenstruktur.
AKDesai

1

Wenn Sie sortiert auf die Daten zugreifen möchten, muss parallel zur Hash-Tabelle eine sortierte Liste geführt werden. Ein gutes Beispiel ist Dictionary in .Net. (Siehe http://msdn.microsoft.com/en-us/library/3fcwy8h6.aspx ).

Dies hat den Nebeneffekt, dass nicht nur Einfügungen verlangsamt werden, sondern auch mehr Speicher als ein B-Baum verbraucht wird.

Da ein B-Baum sortiert ist, ist es außerdem einfach, Ergebnisbereiche zu finden oder Gewerkschaften oder Zusammenführungen durchzuführen.


1

Es hängt auch von der Verwendung ab, Hash ermöglicht es, die genaue Übereinstimmung zu finden. Wenn Sie einen Bereich abfragen möchten, ist BST die richtige Wahl. Angenommen, Sie haben viele Daten e1, e2, e3 ..... en.

Mit der Hash-Tabelle können Sie jedes Element in konstanter Zeit lokalisieren.

Wenn Sie Bereichswerte finden möchten, die größer als e41 und kleiner als e8 sind, kann BST dies schnell finden.

Das Wichtigste ist die Hash-Funktion, mit der eine Kollision vermieden wird. Natürlich können wir eine Kollision nicht vollständig vermeiden. In diesem Fall greifen wir auf Verkettung oder andere Methoden zurück. Dadurch ist der Abruf im schlimmsten Fall nicht mehr konstant.

Sobald die Hash-Tabelle voll ist, muss sie ihre Bucket-Größe erhöhen und alle Elemente erneut kopieren. Dies sind zusätzliche Kosten, die gegenüber BST nicht anfallen.


1

Hash-Tabellen eignen sich nicht für die Indizierung. Wenn Sie nach einem Bereich suchen, sind BSTs besser. Aus diesem Grund verwenden die meisten Datenbankindizes B + -Bäume anstelle von Hash-Tabellen


Datenbankindizes sind vom Typ Hash- und B + -Bäume. Wenn Sie Vergleiche wie größer oder kleiner als durchführen möchten, ist der B + -Baumindex hilfreich, andernfalls ist der Hash-Index für die Suche hilfreich. Denken Sie auch daran, wenn Daten nicht vergleichbar sind und wenn Sie einen Index erstellen möchten, erstellt db einen Hash-Index und keinen B + -Baumindex. @ssD
Sukhmeet Singh

1

Binäre Suchbäume sind eine gute Wahl, um ein Wörterbuch zu implementieren, wenn auf den Schlüsseln eine Gesamtreihenfolge (Schlüssel sind vergleichbar) definiert ist und Sie die Bestellinformationen beibehalten möchten.

Da BST die Bestellinformationen beibehält, stehen Ihnen vier zusätzliche dynamische Set-Operationen zur Verfügung, die mit Hash-Tabellen nicht (effizient) ausgeführt werden können. Diese Operationen sind:

  1. Maximal
  2. Minimum
  3. Nachfolger
  4. Vorgänger

Alle diese Operationen haben wie jede BST-Operation eine Zeitkomplexität von O (H). Darüber hinaus bleiben alle gespeicherten Schlüssel in der BST sortiert, sodass Sie die sortierte Reihenfolge der Schlüssel erhalten, indem Sie den Baum der Reihe nach durchlaufen.

Zusammenfassend lässt sich sagen, dass die Hash-Tabelle (meistens) in Bezug auf die Leistung unschlagbar ist, wenn Sie nur Operationen einfügen, löschen und entfernen möchten. Wenn Sie jedoch einige oder alle der oben aufgeführten Vorgänge ausführen möchten, sollten Sie eine BST verwenden, vorzugsweise eine selbstausgleichende BST.


0

Der Hauptvorteil der Hash-Tabelle besteht darin, dass fast alle Operationen in ~ = O (1) ausgeführt werden. Und es ist sehr einfach zu verstehen und umzusetzen. Es löst viele "Interviewprobleme" effizient. Also, wenn du ein Coding-Interview knacken willst, mache beste Freunde mit Hash-Tabelle ;-)


Ich denke, das OP hat nach Vorteilen von BST gegenüber Hashing gefragt.
Scharfschütze

0

Eine Hashmap ist ein festgelegtes assoziatives Array. Ihr Array von Eingabewerten wird also in Buckets zusammengefasst. In einem offenen Adressierungsschema haben Sie einen Zeiger auf einen Bucket, und jedes Mal, wenn Sie einem Bucket einen neuen Wert hinzufügen, finden Sie heraus, wo im Bucket freie Speicherplätze vorhanden sind. Es gibt verschiedene Möglichkeiten, dies zu tun: Sie beginnen am Anfang des Buckets und erhöhen den Zeiger jedes Mal und testen, ob er belegt ist. Dies wird als lineare Abtastung bezeichnet. Anschließend können Sie eine binäre Suche wie "Hinzufügen" durchführen, bei der Sie die Differenz zwischen dem Anfang des Buckets verdoppeln und bei jeder Suche nach einem freien Speicherplatz nach oben oder unten verdoppeln. Dies wird als quadratische Abtastung bezeichnet. OK. Das Problem bei beiden Methoden besteht nun darin, dass Sie Folgendes tun müssen, wenn der Bucket in die nächste Bucket-Adresse überläuft.

  1. Verdoppeln Sie jede Bucket-Größe - malloc (N Buckets) / ändern Sie die Hash-Funktion - Erforderliche Zeit: hängt von der malloc-Implementierung ab
  2. Übertragen / kopieren Sie alle früheren Bucket-Daten in die neuen Bucket-Daten. Dies ist eine O (N) -Operation, bei der N die gesamten Daten darstellt

OK. Aber wenn Sie eine verknüpfte Liste verwenden, sollte es kein solches Problem geben, oder? Ja, in verknüpften Listen haben Sie dieses Problem nicht. Wenn Sie bedenken, dass jeder Bucket mit einer verknüpften Liste beginnt, und wenn Sie 100 Elemente in einem Bucket haben, müssen Sie diese 100 Elemente durchlaufen, um das Ende der verknüpften Liste zu erreichen. Daher dauert es einige Zeit, bis List.add (Element E)

  1. Hash das Element zu einem Bucket-Normal wie in allen Implementierungen
  2. Nehmen Sie sich Zeit, um das letzte Element in der Bucket-O (N) -Operation zu finden.

Der Vorteil der Linkedlist-Implementierung besteht darin, dass Sie nicht die Speicherzuweisungsoperation und die O (N) -Übertragung / Kopie aller Buckets benötigen, wie im Fall der Open-Addressing-Implementierung.

Um die O (N) -Operation zu minimieren, konvertieren Sie die Implementierung in die eines binären Suchbaums, in dem Suchoperationen O (log (N)) sind, und fügen Sie das Element basierend auf seinem Wert an seiner Position hinzu. Das zusätzliche Merkmal eines BST ist, dass es sortiert kommt!


0

Binäre Suchbäume können schneller sein, wenn sie mit Zeichenfolgenschlüsseln verwendet werden. Besonders wenn die Saiten lang sind.

Binäre Suchbäume mit Vergleichen für weniger / größer, die für Zeichenfolgen schnell sind (wenn sie nicht gleich sind). So kann eine BST schnell antworten, wenn eine Zeichenfolge nicht gefunden wird. Wenn es gefunden wurde, muss es nur einen vollständigen Vergleich durchführen.

In einer Hash-Tabelle. Sie müssen den Hash der Zeichenfolge berechnen. Dies bedeutet, dass Sie alle Bytes mindestens einmal durchlaufen müssen, um den Hash zu berechnen. Andererseits, wenn ein passender Eintrag gefunden wird.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.