Es gibt einige Datenstrukturen, die wirklich nützlich sind, aber den meisten Programmierern unbekannt sind. Welche sind sie?
Jeder kennt verknüpfte Listen, Binärbäume und Hashes, aber was ist zum Beispiel mit Überspringlisten und Bloom-Filtern ? Ich würde gerne mehr Datenstrukturen kennenlernen, die nicht so häufig vorkommen, aber wissenswert sind, weil sie auf großartigen Ideen beruhen und die Toolbox eines Programmierers bereichern.
PS: Ich interessiere mich auch für Techniken wie Dancing Links, die die Eigenschaften einer gemeinsamen Datenstruktur geschickt nutzen.
BEARBEITEN : Bitte versuchen Sie, Links zu Seiten aufzunehmen, die die Datenstrukturen detaillierter beschreiben. Versuchen Sie auch, ein paar Worte darüber hinzuzufügen, warum eine Datenstruktur cool ist (wie Jonas Kölker bereits betont hat). Versuchen Sie auch, eine Datenstruktur pro Antwort bereitzustellen . Dadurch können die besseren Datenstrukturen allein aufgrund ihrer Stimmen nach oben schweben.
Antworten:
Versuche , auch als Präfixbäume oder Crit-Bit-Bäume bekannt , existieren seit über 40 Jahren, sind aber noch relativ unbekannt. Eine sehr coole Verwendung von Versuchen wird in " TRASH - Eine dynamische LC-Trie- und Hash-Datenstruktur " beschrieben, die einen Trie mit einer Hash-Funktion kombiniert.
quelle
Bloom-Filter : Bit-Array von m Bits, anfangs alle auf 0 gesetzt.
Um ein Element hinzuzufügen, führen Sie es durch k Hash-Funktionen aus, die Ihnen k Indizes in dem Array geben, die Sie dann auf 1 setzen.
Um zu überprüfen, ob sich ein Element in der Menge befindet, berechnen Sie die k- Indizes und prüfen Sie, ob alle auf 1 gesetzt sind.
Dies gibt natürlich eine gewisse Wahrscheinlichkeit für falsch positive Ergebnisse (laut Wikipedia sind es ungefähr 0,61 ^ (m / n), wobei n die Anzahl der eingefügten Elemente ist). Falsch-Negative sind nicht möglich.
Ein Element zu entfernen , ist unmöglich, aber Sie können implementieren Zählen Bloom - Filter durch Anordnung von ints und Erhöhen / Verringern, dargestellt.
quelle
Seil : Es ist eine Schnur, die billige Voranstellungen, Teilzeichenfolgen, mittlere Einfügungen und Anhänge ermöglicht. Ich habe es wirklich nur einmal benutzt, aber keine andere Struktur hätte ausgereicht. Regelmäßige Prepends für Strings und Arrays waren einfach viel zu teuer für das, was wir tun mussten, und es kam nicht in Frage, alles umzukehren.
quelle
Überspringlisten sind ziemlich ordentlich.
Sie können als Alternative zu ausgeglichenen Bäumen verwendet werden (unter Verwendung eines probalistischen Ausgleichs anstelle einer strikten Durchsetzung des Ausgleichs). Sie sind einfach zu implementieren und schneller als beispielsweise ein rot-schwarzer Baum. Ich denke, sie sollten in jedem guten Programmierer-Toolchest enthalten sein.
Wenn Sie eine ausführliche Einführung in die Überspringlisten erhalten möchten, finden Sie hier einen Link zu einem Video der Vorlesung des MIT über die Einführung in Algorithmen.
Auch hier ist ein Java - Applet demonstriert Skip-Liste visuell.
quelle
Raumindizes , insbesondere R-Bäume und KD-Bäume , speichern Geodaten effizient. Sie eignen sich für geografische Kartenkoordinatendaten und VLSI-Orts- und Routenalgorithmen sowie manchmal für die Suche nach dem nächsten Nachbarn.
Bit-Arrays speichern einzelne Bits kompakt und ermöglichen schnelle Bitoperationen.
quelle
Reißverschlüsse - Ableitungen von Datenstrukturen, die die Struktur so ändern, dass sie den natürlichen Begriff "Cursor" haben - aktuelle Position. Diese sind wirklich nützlich, da sie garantieren, dass Indikatoren nicht unbegrenzt sind - z. B. im xmonad-Fenstermanager, um zu verfolgen, welches Fenster fokussiert hat.
Erstaunlicherweise können Sie sie ableiten, indem Sie Techniken aus der Analysis auf den Typ der ursprünglichen Datenstruktur anwenden !
quelle
Hier sind ein paar:
Suffix versucht. Nützlich für fast alle Arten der Zeichenfolgensuche (http://en.wikipedia.org/wiki/Suffix_trie#Functionality ). Siehe auch Suffix-Arrays; Sie sind nicht ganz so schnell wie Suffixbäume, aber viel kleiner.
Spreizbäume (wie oben erwähnt). Der Grund, warum sie cool sind, ist dreifach:
Heap-geordnete Suchbäume: Sie speichern eine Reihe von (Schlüssel-, Prio-) Paaren in einem Baum, sodass es sich um einen Suchbaum in Bezug auf die Schlüssel handelt und Heap-geordnet in Bezug auf die Prioritäten. Man kann zeigen, dass ein solcher Baum eine einzigartige Form hat (und nicht immer vollständig links gepackt ist). Mit zufälligen Prioritäten erhalten Sie die erwartete O (log n) Suchzeit IIRC.
Eine Nische sind Adjazenzlisten für ungerichtete planare Graphen mit O (1) Nachbarabfragen. Dies ist weniger eine Datenstruktur als vielmehr eine besondere Art, eine vorhandene Datenstruktur zu organisieren. So geht's: Jedes planare Diagramm hat einen Knoten mit höchstens 6 Grad. Wählen Sie einen solchen Knoten aus, fügen Sie seine Nachbarn in die Nachbarliste ein, entfernen Sie ihn aus dem Diagramm und wiederholen Sie den Vorgang, bis das Diagramm leer ist. Wenn Sie ein Paar (u, v) erhalten, suchen Sie in der Nachbarliste von v nach u und in der Nachbarliste von u nach v. Beide haben höchstens 6, also ist dies O (1).
Wenn nach dem obigen Algorithmus u und v Nachbarn sind, haben Sie nicht sowohl u in der Liste von v als auch v in der Liste von u. Wenn Sie dies benötigen, fügen Sie einfach die fehlenden Nachbarn jedes Knotens zur Nachbarliste dieses Knotens hinzu, speichern Sie jedoch, wie viel von der Nachbarliste Sie für eine schnelle Suche durchsuchen müssen.
quelle
Ich denke, sperrenfreie Alternativen zu Standarddatenstrukturen, dh sperrenfreie Warteschlange, Stapel und Liste, werden oft übersehen.
Sie werden immer relevanter, da die Parallelität eine höhere Priorität erhält und ein viel bewundernswerteres Ziel ist als die Verwendung von Mutexen oder Sperren für das gleichzeitige Lesen / Schreiben.
Hier sind einige Links
http://www.cl.cam.ac.uk/research/srg/netos/lock-free/
http://www.research.ibm.com/people/m/michael/podc-1996.pdf [Links zu PDF]
http://www.boyet.com/Articles/LockfreeStack.html
Mike Actons (oft provokanter) Blog enthält einige hervorragende Artikel über schlossfreies Design und Ansätze
quelle
Ich denke, Disjoint Set ist ziemlich geschickt für Fälle, in denen Sie eine Reihe von Elementen in verschiedene Sets aufteilen und die Mitgliedschaft abfragen müssen. Eine gute Implementierung der Union- und Find-Operationen führt zu amortisierten Kosten, die effektiv konstant sind (umgekehrt zu Ackermnans Funktion, wenn ich meine Datenstrukturklasse korrekt zurückrufe).
quelle
Fibonacci-Haufen
Sie werden in einigen der schnellsten bekannten Algorithmen (asymptotisch) für viele grafische Probleme verwendet, z. B. für das Problem des kürzesten Pfades. Der Dijkstra-Algorithmus wird in O-Zeit (E log V) mit Standard-Binärhaufen ausgeführt. Die Verwendung von Fibonacci-Heaps verbessert dies auf O (E + V log V), was eine enorme Beschleunigung für dichte Graphen darstellt. Leider haben sie einen hohen konstanten Faktor, was sie in der Praxis oft unpraktisch macht.
quelle
Jeder, der Erfahrung mit 3D-Rendering hat, sollte mit BSP-Bäumen vertraut sein . Im Allgemeinen ist es die Methode, eine 3D-Szene so zu strukturieren, dass sie für das Rendern mit Kenntnis der Kamerakoordinaten und der Peilung verwaltbar ist.
quelle
Huffman-Bäume - werden zur Komprimierung verwendet.
quelle
Schauen Sie sich Finger Trees an , besonders wenn Sie ein Fan der zuvor erwähnten rein funktionalen Datenstrukturen sind. Sie sind eine funktionale Darstellung persistenter Sequenzen, die den Zugriff auf die Enden in amortisierter konstanter Zeit sowie die Verkettung und zeitliche Aufteilung in der Größe des kleineren Stücks logarithmisch unterstützen.
Gemäß Originalartikel :
Ein Fingerbaum kann mit einem Monoid parametrisiert werden. Die Verwendung unterschiedlicher Monoide führt zu unterschiedlichen Verhaltensweisen für den Baum. Auf diese Weise können Finger Trees andere Datenstrukturen simulieren.
quelle
Rund- oder Ringpuffer - wird unter anderem zum Streamen verwendet.
quelle
Ich bin überrascht, dass niemand Merkle-Bäume (dh Hash-Bäume ) erwähnt hat.
Wird in vielen Fällen verwendet (P2P-Programme, digitale Signaturen), in denen Sie den Hash einer gesamten Datei überprüfen möchten, wenn Ihnen nur ein Teil der Datei zur Verfügung steht.
quelle
Ich denke, es wäre nützlich zu wissen, warum sie cool sind. Im Allgemeinen ist die Frage "warum" am wichtigsten zu stellen;)
Meine Antwort ist, dass sie Ihnen O (log log n) Wörterbücher mit {1..n} Schlüsseln geben, unabhängig davon, wie viele der Schlüssel verwendet werden. Genau wie die wiederholte Halbierung O (log n) ergibt, ergibt die wiederholte Quadrierung O (log log n), was im vEB-Baum der Fall ist.
quelle
Wie wäre es mit Spreizbäumen ?
Auch Chris Okasakis rein funktionale Datenstrukturen kommen in den Sinn.
quelle
Eine interessante Variante der Hash-Tabelle heißt Cuckoo Hashing . Es werden mehrere Hash-Funktionen anstelle von nur 1 verwendet, um Hash-Kollisionen zu behandeln. Kollisionen werden behoben, indem das alte Objekt von dem durch den primären Hash angegebenen Speicherort entfernt und an einen durch eine alternative Hash-Funktion angegebenen Speicherort verschoben wird. Cuckoo Hashing ermöglicht eine effizientere Nutzung des Speicherplatzes, da Sie Ihren Ladefaktor mit nur 3 Hash-Funktionen um bis zu 91% erhöhen können und dennoch eine gute Zugriffszeit haben.
quelle
Ein Min-Max-Heap ist eine Variation eines Heaps , der eine Warteschlange mit doppelter Priorität implementiert. Dies wird durch eine einfache Änderung der Heap-Eigenschaft erreicht: Ein Baum wird als min-max-geordnet bezeichnet, wenn jedes Element auf geraden (ungeraden) Ebenen kleiner (größer) ist als alle Kinder und Enkelkinder. Die Ebenen sind ab 1 nummeriert.
http://internet512.chonbuk.ac.kr/datastructure/heap/img/heap8.jpg
quelle
Ich mag Cache Oblivious-Datenstrukturen . Die Grundidee besteht darin, einen Baum in rekursiv kleineren Blöcken auszulegen, sodass Caches mit vielen verschiedenen Größen die Vorteile von Blöcken nutzen, die bequem in sie passen. Dies führt zu einer effizienten Nutzung des Cachings für alles, vom L1-Cache im RAM bis hin zu großen Datenblöcken, die von der Festplatte gelesen werden, ohne dass die Besonderheiten der Größe einer dieser Caching-Schichten bekannt sein müssen.
quelle
Links schiefen rot-schwarze Bäume . Eine deutlich vereinfachte Implementierung von rot-schwarzen Bäumen durch Robert Sedgewick, die 2008 veröffentlicht wurde (~ die Hälfte der zu implementierenden Codezeilen). Wenn Sie jemals Probleme hatten, sich mit der Implementierung eines Rot-Schwarz-Baums zu beschäftigen, lesen Sie diese Variante.
Sehr ähnlich (wenn nicht identisch) zu Andersson Trees.
quelle
Work Stealing Queue
Sperrfreie Datenstruktur zum gleichmäßigen Aufteilen der Arbeit auf mehrere Threads Implementierung einer Warteschlange zum Stehlen von Arbeit in C / C ++?
quelle
Bootstrapped Skew-Binomial-Haufen von Gerth Stølting Brodal und Chris Okasaki:
Trotz ihres langen Namens bieten sie selbst in einer Funktionseinstellung asymptotisch optimale Heap-Operationen.
O(1)
Größe, Vereinigung , Einsatz, MinimumO(log n)
deleteMinBeachten Sie, dass die Vereinigung im Gegensatz zu den bekannteren Heaps, die üblicherweise in Lehrbüchern zur Datenstruktur behandelt werden, wie z. B. linken Heaps,
O(1)
eherO(log n)
Zeit als Zeit in Anspruch nimmt . Und im Gegensatz zu Fibonacci-Haufen sind diese Asymptotika eher im schlimmsten Fall als amortisiert, selbst wenn sie dauerhaft verwendet werden!In Haskell gibt es mehrere Implementierungen .
Sie wurden gemeinsam von Brodal und Okasaki abgeleitet, nachdem Brodal einen imperativen Haufen mit den gleichen Asymptoten gefunden hatte.
quelle
Die meisten, wenn nicht alle, sind im NIST Dictionary of Algorithms and Data Structures dokumentiert
quelle
Kugelbäume. Nur weil sie die Leute zum Kichern bringen.
Ein Kugelbaum ist eine Datenstruktur, die Punkte in einem metrischen Raum indiziert. Hier ist ein Artikel über das Erstellen. Sie werden oft verwendet, um die nächsten Nachbarn zu einem Punkt zu finden oder k-Mittel zu beschleunigen.
quelle
Nicht wirklich eine Datenstruktur; eher eine Art und Weise dynamisch zugewiesenen Arrays, aber die Optimierung Spaltpuffer in Emacs sind Art von kühlem verwendet.
quelle
Fenwick Tree. Es ist eine Datenstruktur, um die Summe aller Elemente in einem Vektor zwischen zwei gegebenen Subindizes i und j zu zählen. Die triviale Lösung, die Summe von Anfang an vorab zu berechnen, erlaubt es nicht, ein Element zu aktualisieren (Sie müssen O (n) arbeiten, um Schritt zu halten).
Mit Fenwick Trees können Sie in O (log n) aktualisieren und abfragen, und wie es funktioniert, ist wirklich cool und einfach. Es ist wirklich gut erklärt in Fenwicks Originalarbeit, die hier frei verfügbar ist:
http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol24/issue3/spe884.pdf
Sein Vater, der RQM-Baum, ist ebenfalls sehr cool: Er ermöglicht es Ihnen, Informationen über das minimale Element zwischen zwei Indizes des Vektors zu speichern, und er funktioniert auch bei der Aktualisierung und Abfrage von O (log n). Ich unterrichte gerne zuerst das RQM und dann den Fenwick Tree.
quelle
Van Emde-Boas Bäume . Ich habe sogar eine C ++ - Implementierung für bis zu 2 ^ 20 Ganzzahlen.
quelle
Verschachtelte Mengen eignen sich gut, um Bäume in den relationalen Datenbanken darzustellen und Abfragen darauf auszuführen. Zum Beispiel enthält ActiveRecord (Ruby on Rails Standard-ORM) ein sehr einfaches Plugin für verschachtelte Sets , das das Arbeiten mit Bäumen trivial macht.
quelle
Es ist ziemlich domänenspezifisch, aber die Datenstruktur mit halber Kante ist ziemlich ordentlich. Es bietet eine Möglichkeit, über Polygonnetze (Flächen und Kanten) zu iterieren, was in der Computergrafik und in der Computergeometrie sehr nützlich ist.
quelle