Certaines structures de données sont vraiment utiles mais inconnues de la plupart des programmeurs. Lesquels sont-ils?
Tout le monde connaît les listes chaînées, les arbres binaires et les hachages, mais qu'en est-il de Ignorer les listes et Filtres de Bloom par exemple. J'aimerais connaître davantage de structures de données qui ne sont pas si courantes, mais qui valent la peine d'être connues, car elles reposent sur de bonnes idées et enrichissent la boîte à outils d'un programmeur.
PS: Je suis également intéressé par des techniques telles que liens dansants , qui exploitent intelligemment les propriétés d’une structure de données commune.
EDIT: Veuillez essayer d'inclure des liens vers des pages décrivant les structures de données plus en détail. Aussi, essayez d’ajouter quelques mots sur pourquoi une structure de données est cool (comme Jonas Kölker déjà souligné). Essayez également de fournir une structure de données par réponse . Cela permettra aux meilleures structures de données de flotter au sommet en fonction de leurs votes uniquement.
Essais , également connu sous le nom d'arbre de préfixe ou arbre de crit-bit , existent depuis plus de 40 ans mais sont encore relativement inconnus. Une utilisation très intéressante de try est décrite dans " TRASH - Structure de données de hachage LC-dynamique et dynamique ", qui combine un essai et une fonction de hachage.
Filtre de Bloom : Tableau de bits de m bits, tous initialement réglés sur 0.
Pour ajouter un élément, vous devez l'exécuter k fonctions de hachage qui vous donneront k des indices dans le tableau que vous définissez ensuite sur 1.
Pour vérifier si un élément est dans l'ensemble, calculez les indices k et vérifiez s'ils ont tous la valeur 1.
Bien sûr, cela donne une certaine probabilité de faux positifs (selon wikipedia, c'est environ 0,61 ^ (m/n), où n est le nombre d'éléments insérés). Les faux négatifs ne sont pas possibles.
Supprimer un élément est impossible, mais vous pouvez appliquer un filtre de bloom de comptage , représenté par un tableau d'ints et une incrémentation/réduction.
Rope : C'est une chaîne qui permet d'effectuer des ajouts, des sous-chaînes, des insertions intermédiaires et des ajouts bon marché. Je ne l'ai vraiment utilisée qu'une seule fois, mais aucune autre structure n'aurait suffi. Les chaînes normales et les prépositions de tableaux étaient tout simplement trop chères pour ce que nous devions faire, et il était hors de question d'inverser tout.
Les listes de saut sont plutôt chouettes.
Wikipedia
Une liste à sauter est une structure de données probabiliste, basée sur plusieurs listes chaînées parallèles et triées, avec une efficacité comparable à un arbre de recherche binaire (journal des commandes n temps moyen pour la plupart des opérations).
Ils peuvent être utilisés comme une alternative aux arbres équilibrés (en utilisant un équilibrage probaliste plutôt qu'une application stricte de l'équilibrage). Ils sont faciles à mettre en œuvre et plus rapides qu’un arbre rouge-noir. Je pense qu'ils devraient figurer dans tous les bons instruments de programmation.
Si vous souhaitez obtenir une introduction détaillée aux listes à accès direct, voici un lien vers une vidéo du cours Introduction to Algorithms du MIT.
En outre, ici est un applet Java montrant les listes de sauts de manière visuelle.
Indices spatiaux , en particulier Arbres-R et Arbres-KD , stockent efficacement les données spatiales. Ils conviennent aux données de coordonnées géographiques et aux algorithmes de lieu et de route VLSI, et parfois à la recherche le plus proche.
Bit Arrays stocke des bits individuels de manière compacte et permet des opérations de bits rapides.
Zippers - dérivés de structures de données qui modifient la structure pour avoir une notion naturelle de 'curseur' - emplacement actuel. Celles-ci sont vraiment utiles car elles garantissent que les indices ne peuvent pas être utilisés hors-limite, par exemple dans le gestionnaire de fenêtres xmonad pour savoir quelle fenêtre est active.
Étonnamment, vous pouvez les déduire par en appliquant des techniques de calcul au type de la structure de données d'origine!
Voici quelques-uns:
Suffixe essaye. Utile pour presque tous les types de recherche de chaînes ( http://en.wikipedia.org/wiki/Suffix_trie#Functionality ). Voir aussi les tableaux de suffixes; ils ne sont pas aussi rapides que les arbres suffixés, mais beaucoup plus petits.
Arbres splay (comme mentionné ci-dessus). La raison pour laquelle ils sont cool est triple:
Arbres de recherche ordonnés par tas: vous stockez un groupe de paires (clé, prio) dans un arbre, tel qu’il s’agisse d’un arbre de recherche par rapport aux clés et ordonné par ordre de tas par rapport aux priorités. On peut montrer qu’un tel arbre a une forme unique (et il n’est pas toujours complètement emballé vers la gauche). Avec des priorités aléatoires, il vous donne le temps de recherche O (log n) prévu, IIRC.
Une niche est celle des listes de contiguïté pour les graphes planaires non dirigés avec O(1) requêtes voisines. Ce n'est pas tant une structure de données qu'un moyen particulier d'organiser une structure de données existante. Voici comment procéder: chaque graphe planaire a un nœud dont le degré est au plus égal à 6. Choisissez un tel nœud, placez-le dans la liste des voisins, supprimez-le du graphique et relancez jusqu'à ce que le graphe soit vide. Lorsqu'une paire (u, v) est donnée, recherchez la liste des voisins de u in v et la liste des voisins de v in u. Les deux ont une taille maximale de 6, donc 0 (1).
Par l'algorithme ci-dessus, si u et v sont voisins, vous n'aurez pas à la fois la liste de u in v et la liste de v in u. Si vous en avez besoin, ajoutez simplement les voisins manquants de chaque nœud à la liste des voisins de ce nœud, mais stockez la quantité de la liste des voisins que vous devez parcourir pour effectuer une recherche rapide.
Je pense que les alternatives sans verrouillage aux structures de données standard, à savoir la file d'attente, la pile et la liste sans verrouillage sont très négligées.
Ils sont de plus en plus pertinents à mesure que la concurrence d'accès devient une priorité plus importante et constitue un objectif beaucoup plus admirable que d'utiliser des mutex ou des verrous pour gérer des lectures/écritures simultanées.
Voici quelques liens
http://www.cl.cam.ac.uk/research/srg/netos/lock-free/
http://www.research.ibm.com/people/m/michael/podc-1996.pdf [Liens vers PDF ]
http://www.boyet.com/Articles/LockfreeStack.html
Le blog (souvent provocateur) de Mike Acton contient d'excellents articles sur la conception et les approches sans verrouillage.
Je pense que Disjoint Set est assez chouette pour les cas où vous devez diviser un tas d’éléments en ensembles distincts et demander l’appartenance à une requête. Une bonne mise en œuvre des opérations Union et Find entraîne des coûts amortis qui sont effectivement constants (inverse de la fonction Ackermnan, si je me souviens bien de ma classe de structures de données).
Ils sont utilisés dans certains des algorithmes les plus rapides connus (asymptotiquement) pour un grand nombre de problèmes liés aux graphes, tels que le problème du plus court chemin. L'algorithme de Dijkstra s'exécute en temps O (E log V) avec des tas binaires standard; utiliser des tas de Fibonacci améliore cela en O (E + V log V), ce qui est une énorme accélération pour les graphes denses. Malheureusement, ils ont un facteur constant élevé, les rendant souvent peu pratiques dans la pratique.
Toute personne ayant une expérience du rendu 3D devrait être familiarisée avec arbres BSP . Généralement, il s’agit de la méthode permettant de structurer une scène 3D de manière à ce qu’elle rende possible le rendu en connaissant les coordonnées et le relèvement de la caméra.
Le partitionnement d’espace binaire (BSP) est une méthode de subdivision récursive d’un espace en ensembles convexes par hyperplans. Cette subdivision donne lieu à une représentation de la scène au moyen d’une structure de données arborescente appelée arbre BSP.
En d’autres termes, c’est un moyen de diviser des polygones aux formes complexes en ensembles convexes, ou des polygones plus petits entièrement constitués d’angles non réflexes (angles inférieurs à 180 °). Pour une description plus générale du partitionnement de l'espace, voir Partitionnement de l'espace.
À l'origine, cette approche avait été proposée en infographie 3D pour améliorer l'efficacité du rendu. Certaines autres applications incluent l'exécution d'opérations géométriques avec des formes (géométrie solide constructive) dans la CAO, la détection de collision dans la robotique et les jeux informatiques en 3D, ainsi que d'autres applications informatiques impliquant le traitement de scènes spatiales complexes.
arbres de Huffman - utilisé pour la compression.
Jetez un oeil à arbres à doigts , surtout si vous êtes un fan des déjà mentionnées structures de données purement fonctionnelles. Ils sont une représentation fonctionnelle de séquences persistantes permettant l'accès aux extrémités en temps constant amorti, ainsi que la concaténation et la division logarithmique temporelle de la taille du plus petit élément.
Selon l'article original :
Nos arbres fonctionnels à deux ou trois doigts sont un exemple d'une technique de conception générale introduite par Okasaki (1998), appelée ralentissement récursif implicite . Nous avons déjà noté que ces arbres sont une extension de sa structure deque implicite, remplaçant les paires par 2-3 nœuds pour fournir la flexibilité requise pour une concaténation et une division efficaces.
Un Finger Tree peut être paramétré avec un monoïde , et l’utilisation de différents monoïdes entraînera des comportements différents pour l’arbre. Cela permet aux arbres à doigts de simuler d'autres structures de données.
tampon circulaire ou circulaire - utilisé, entre autres, pour la diffusion en continu.
Je suis surpris que personne n'ait mentionné Merkle Tree (c'est-à-dire Hash Trees ).
Utilisé dans de nombreux cas (programmes P2P, signatures numériques) lorsque vous souhaitez vérifier le hachage d'un fichier complet lorsque vous ne disposez que d'une partie du fichier.
<zvrba> Arbres de Van Emde-Boas
Je pense qu'il serait utile de savoir pourquoi ils sont cool. En général, la question "pourquoi" est la plus importante à poser;)
Ma réponse est qu’ils vous donnent des dictionnaires O (log log n) avec des clés {1..n}, quel que soit le nombre de clés utilisées. Tout comme la réduction répétée de moitié vous donne O (log n), le réécriture répétée vous donne O (log log n), ce qui se produit dans l'arborescence des vEB.
Qu'en est-il des arbres splay ?
Nous pensons également aux structures de données purement fonctionnelles de Chris Okasaki .
Une variante intéressante de la table de hachage est appelée Cuckoo Hashing . Il utilise plusieurs fonctions de hachage au lieu de 1 pour traiter les collisions de hachage. Les conflits sont résolus en supprimant l'ancien objet de l'emplacement spécifié par le hachage principal et en le déplaçant vers un emplacement spécifié par une autre fonction de hachage. Cuckoo Hashing permet une utilisation plus efficace de l'espace mémoire car vous pouvez augmenter votre facteur de charge jusqu'à 91% avec seulement 3 fonctions de hachage tout en conservant un bon temps d'accès.
Un tas min-max est une variante d'un tas qui implémente une file d'attente prioritaire à deux extrémités. Pour ce faire, il suffit simplement de modifier la propriété heap: un arbre est dit ordonné min-max si chaque élément des niveaux pairs (impairs) est inférieur (plus grand) à tous les enfants et petits-enfants. Les niveaux sont numérotés à partir de 1.
http://internet512.chonbuk.ac.kr/datastructure/heap/img/heap8.jpg
J'aime cache des infrastructures de données Oblivious . L'idée de base est de disposer un arbre dans des blocs récursivement plus petits de manière à ce que des caches de différentes tailles tirent parti des blocs qui y trouvent leur place. Cela conduit à une utilisation efficace de la mise en cache de tout, du cache L1 dans RAM aux gros morceaux de données lues sur le disque sans avoir besoin de connaître les spécificités de la taille de l'une de ces couches de mise en cache.
Arbres Rouge-Noir penchés à gauche . Une mise en œuvre considérablement simplifiée des arbres rouge-noir de Robert Sedgewick publiée en 2008 (environ la moitié des lignes de code à mettre en œuvre). Si vous avez déjà eu du mal à comprendre l’implémentation d’un arbre rouge-noir, découvrez cette variante.
Très semblable (sinon identique) à Andersson Trees.
File d'attente de travail
Structure de données sans verrouillage permettant de diviser le travail de manière égale entre plusieurs threads Implémentation d'une file d'attente de vol de travail en C/C++?
amas de binomiaux asymétriques avec amorçage par Gerth Stølting Brodal et Chris Okasaki:
Malgré leur nom long, ils fournissent des opérations de tas asymptotiquement optimales, même dans un paramètre de fonction.
O(1)
taille, union , insérer, minimumO(log n)
deleteMinNotez que l'union prend O(1)
plutôt que O(log n)
contrairement aux tas plus connus qui sont généralement traités dans les manuels de structure de données, tels que tas de gauche . Et contrairement à tas de Fibonacci , ces asymptotiques sont dans le pire des cas, plutôt qu’amortis, même s’ils sont utilisés de façon persistante!
Il y a multipleimplémentations dans Haskell.
Ils ont été dérivés conjointement par Brodal et Okasaki, après que Brodal eut proposé un tas impératif avec les mêmes asymptotiques.
La plupart, sinon la totalité, sont documentés dans le NIST Dictionnaire des algorithmes et des structures de données
Pas vraiment une structure de données; plus d’un moyen d’optimiser les tableaux alloués dynamiquement, mais les gap buffers utilisés dans Emacs sont plutôt cool.
Fenwick Tree. C'est une structure de données pour garder le compte de la somme de tous les éléments d'un vecteur, entre deux sous-indices donnés i et j. La solution triviale, le précalcul de la somme depuis le début ne permet pas de mettre à jour un élément (vous devez faire O(n) travailler pour suivre).
Les arbres de Fenwick vous permettent de mettre à jour et d'interroger dans O (log n), et son fonctionnement est vraiment simple et sympa. C'est très bien expliqué dans l'article original de Fenwick, disponible gratuitement ici:
http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol24/issue3/spe884.pdf
Son père, l’arbre RQM, est également très pratique: il vous permet de conserver des informations sur l’élément minimum entre deux index du vecteur et fonctionne également dans O (log n) update and query. J'aime enseigner d'abord le RQM puis le Fenwick Tree.
arbres Van Emde-Boas . J'ai même un C++ implémentation , pour un maximum de 2 ^ 20 entiers.
Les ensembles imbriqués sont très pratiques pour représenter les arbres dans les bases de données relationnelles et y exécuter des requêtes. Par exemple, ActiveRecord (ORM par défaut de Ruby on Rails) est livré avec un très simple plugin jeu imbriqué , ce qui rend le travail avec des arbres trivial.
Un liste liée déroulée est une variante de la liste liée qui stocke plusieurs éléments dans chaque nœud. Cela peut considérablement augmenter les performances du cache, tout en réduisant la surcharge de mémoire associée au stockage de métadonnées de liste, telles que les références. C'est lié au B-tree.
record node {
node next // reference to next node in list
int numElements // number of elements in this node, up to maxElements
array elements // an array of numElements elements, with space allocated for maxElements elements
}
Arbres de boucs émissaires. Un problème classique des arbres binaires simples est qu’ils deviennent déséquilibrés (par exemple, lorsque les clés sont insérées dans un ordre croissant.)
Les arbres binaires équilibrés (arbres AKA AVL) perdent beaucoup de temps à équilibrer après chaque insertion.
Les arbres rouge-noir restent équilibrés, mais nécessitent un peu d'espace de stockage pour chaque nœud.
Les arbres de bouc émissaire restent en équilibre comme les arbres rouge-noir, mais ne nécessitent AUCUN entreposage supplémentaire. Ils le font en analysant l’arbre après chaque insertion et en effectuant des ajustements mineurs. Voir http://en.wikipedia.org/wiki/Scapegoat_tree .
C'est assez spécifique à un domaine, mais structure de données half-Edge est plutôt soigné. Il fournit un moyen d'itérer sur des mailles de polygones (faces et arêtes), ce qui est très utile en infographie et en géométrie de calcul.
2-3 arbres à doigts par Hinze et Paterson sont un excellent couteau fonctionnel pour l'armée suisse doté d'une structure de données fonctionnelle, avec une excellente asymptotique pour un large éventail d'opérations. Bien que complexes, elles sont beaucoup plus simples que les structures impératives de Listes persistantes avec caténation via un ralentissement récursif par Kaplan et Tarjan qui les ont précédées.
Ils fonctionnent comme un deque catenable avec O(1)
accès à l'une ou l'autre extrémité, O(log min(n,m))
append, et fournissent O(log min(n,length - n))
indexation avec accès direct à une somme de préfixes monoïdale sur toute partie de la séquence.
Des implémentations existent dans Haskell , Coq , F # , Scala , Java , - C , Clojure , C # et autres langues.
Vous pouvez les utiliser pour implémenter files de recherche prioritaires , cartes d'intervalle , cordes avec accès rapide à la tête , cartes, ensembles, séquences modifiables ou à peu près n'importe quelle structure où vous pouvez l'exprimer comme collectant un résultat monoidal sur une séquence rapidement indexable/corrigeable.
J'ai aussi quelques slides décrivant leur dérivation et leur utilisation.
Une structure de données moins connue, mais assez astucieuse, est la structure Fenwick Tree (aussi parfois appelée arbre binaire indexé ou BIT). Il stocke les sommes cumulées et prend en charge les opérations O(log(n)). Bien que les sommes cumulées ne semblent pas très intéressantes, elles peuvent être adaptées pour résoudre de nombreux problèmes nécessitant une structure de données triée/log (n).
OMI, son principal argument de vente est la facilité avec laquelle il peut être implémenté . Très utile pour résoudre des problèmes algorithmiques qui impliqueraient sinon de coder un arbre rouge-noir/avl.
Les paires d'appariement sont un type de structure de données en tas avec une implémentation relativement simple et d'excellentes performances amorties dans la pratique.
Liste liée XOR utilise deux pointeurs XOR pour réduire les exigences de stockage pour la liste à double liaison. Un peu obscure mais soignée!
J'aime vraiment vraiment arbres d'intervalle . Ils vous permettent de prendre un tas d’intervalles (c’est-à-dire les heures de début/de fin, ou autre) et de demander quels intervalles contiennent une heure donnée ou quels intervalles ont été "actifs" au cours d’une période donnée. Les requêtes peuvent être effectuées dans O (log n) et le prétraitement est O (n log n).
Tableaux Splash sont excellents. Ils ressemblent à une table de hachage normale, sauf qu'ils garantissent une recherche en temps constant et peuvent gérer 90% d'utilisation sans perte de performance. Ils sont une généralisation de Cuckoo Hash (également une excellente structure de données). Ils semblent être breveté , mais comme pour la plupart des brevets logiciels, je ne m'inquiéterais pas trop.
Les algorithmes de hachage améliorés sont très intéressants. Le hachage linéaire est soigné, car il permet de scinder un "compartiment" de votre table de hachage à la fois, plutôt que de ressasser l'ensemble de la table. Ceci est particulièrement utile pour les caches distribués. Cependant, avec les stratégies de fractionnement les plus simples, vous séparez rapidement tous les compartiments et le facteur de charge de la table oscille assez mal.
Je pense que hachage en spirale est vraiment très chouette aussi. Comme pour le hachage linéaire, un compartiment à la fois est divisé et un peu moins de la moitié des enregistrements du compartiment sont placés dans le même nouveau compartiment. C'est très propre et rapide. Cependant, il peut être inefficace que chaque "compartiment" soit hébergé par une machine ayant des spécifications similaires. Pour utiliser pleinement le matériel, vous voulez un mélange de machines moins puissantes et plus puissantes.
Le quadrilatère de la région
(cité de Wikipedia )
Le quadrilatère de région représente une partition d'espace en deux dimensions en décomposant la région en quatre quadrants, sous-quadrants égaux, etc., chaque nœud feuille contenant des données correspondant à une sous-région spécifique. Chaque nœud de l’arborescence a exactement quatre enfants ou n’a pas d’enfants (un nœud feuille).
De tels quadruples sont parfaits pour stocker des données spatiales, par exemple. latitude et longitude ou autres types de coordonnées.
C'était de loin ma structure de données préférée au collège. Coder ce gars et le voir fonctionner était plutôt cool. Je le recommande vivement si vous recherchez un projet qui demande un peu de réflexion et qui sort un peu des sentiers battus. Quoi qu’il en soit, c’est beaucoup plus amusant que les dérivés BST standard que vous avez généralement affectés dans votre classe de structures de données!
En fait, en prime, j'ai trouvé les notes de la conférence qui a précédé le projet de classe (de Virginia Tech) ici (pdf warning) .
Diagramme de décision binaire est l'une de mes structures de données préférées, ou en fait, le diagramme de décision binaire ordonnée réduite (ROBDD).
Ce type de structure peut par exemple être utilisé pour:
Notez que de nombreux problèmes peuvent être représentés comme une expression booléenne. Par exemple, la solution à un suduku peut être exprimée sous la forme d'une expression booléenne. Et créer un BDD pour cette expression booléenne donnera immédiatement la (les) solution (s).
J'aime les treaps - pour l’idée simple mais efficace de superposer une structure de tas avec une priorité aléatoire sur un arbre de recherche binaire afin de l’équilibrer.
Essais Rapides Compact:
tableaux de Judy : Tableaux dynamiques clairsemés ordonnés très rapides et efficaces en termes de mémoire pour les bits, les entiers et les chaînes. Les tableaux Judy sont plus rapides et utilisent plus efficacement la mémoire que n’importe quel arbre de recherche binaire.
HAT-trie: une structure de données basée sur la requête et basée sur le cache pour les chaînes
J'utilise parfois Inversion LIsts pour stocker des plages et elles sont souvent utilisées pour stocker des classes de caractères dans des expressions régulières. Voir par exemple http://www.ibm.com/developerworks/linux/library/l-cpinv.html
Un autre cas d'utilisation de Nice concerne les décisions aléatoires pondérées. Supposons que vous disposiez d'une liste de symboles et des probabilités associées et que vous souhaitiez les sélectionner au hasard en fonction de ces probabilités.
a => 0.1 b => 0.5 c => 0.4
Ensuite, vous faites une somme courante de toutes les probabilités:
(0.1, 0.6, 1.0)
Ceci est votre liste d'inversion. Vous générez un nombre aléatoire compris entre 0 et 1 et recherchez l'index de l'entrée la plus élevée suivante dans la liste. Vous pouvez le faire avec une recherche binaire, car elle est triée. Une fois que vous avez obtenu l'index, vous pouvez rechercher le symbole dans la liste d'origine.
Si vous avez les symboles n
, vous avez le temps de préparation O(n), puis le temps d'accès O(log(n)) _ pour chaque symbole choisi au hasard - indépendamment de la distribution des poids.
Une variante des listes d'inversion utilise des nombres négatifs pour indiquer le point final des plages, ce qui permet de compter facilement le nombre de plages qui se chevauchent à un moment donné. Voir http://www.perlmonks.org/index.pl?node_id=841368 pour un exemple.
DAWG s sont un type particulier de Trie où des arbres enfants similaires sont compressés en parents uniques. J'ai étendu les fichiers DAWG modifiés et mis au point une structure de données astucieuse appelée ASSDAWG (Anagram Search Sorted DAWG). Cela fonctionne de la manière suivante: chaque fois qu'une chaîne est insérée dans le DAWG, elle est triée en premier, puis insérée et les noeuds feuille tiennent un nombre supplémentaire indiquant les permutations valides si nous atteignons ce noeud feuille de racine. Cela présente deux avantages intéressants:
Compté bières équilibrées non triées.
Parfait pour les tampons d'éditeur de texte.
http://www.chiark.greenend.org.uk/~sgtatham/algorithms/cbtree.html
Arne Andersson trees sont une alternative plus simple aux arbres rouge-noir, dans lesquels seuls les bons liens peuvent être rouges. Cela simplifie grandement la maintenance, tout en maintenant les performances au même niveau que les arbres rouge-noir. Le document original donne un implémentation de Nice et courte pour insertion et suppression.
Les arbres de Fenwick (ou les arbres indexés binaires) sont un complément utile à la boîte à outils. Si vous avez un tableau de compteurs et que vous devez constamment les mettre à jour lors de la recherche de comptes cumulés (comme dans PPM compression), les arbres Fenwick effectueront toutes les opérations en temps O (log n) et ne nécessiteront aucun espace supplémentaire. . Voir aussi ceci tutoriel topcoder pour une bonne introduction.
Zobrist Hashing est une fonction de hachage généralement utilisée pour représenter une position de plateau de jeu (comme dans Chess) mais a sûrement d'autres utilisations. Une des bonnes choses à ce sujet est qu’il peut être mis à jour progressivement à mesure que le tableau est mis à jour.
BK-Trees ou Burkhard-Keller Trees sont une structure de données arborescente pouvant être utilisée pour rechercher rapidement des quasi-correspondances avec une chaîne.
J'aime arbre de suffixe et tableaux pour le traitement des chaînes, ignorer les listes pour les listes pondérées et arbres évasés pour les arbres d'équilibrage automatique
Jetez un coup d'œil au tas latéral présenté par Donald Knuth.
http://stanford-online.stanford.edu/seminars/knuth/071203-knuth-300.asx
Les arbres splay sont cool. Ils se réorganisent de manière à rapprocher les éléments les plus souvent interrogés de la racine.
C'est une variété d'arbres B qui est efficace pour la recherche au prix d'une insertion plus coûteuse.
Vous pouvez utiliser un minimum-tas pour rechercher l'élément minimum en temps constant ou un maximum-tas pour rechercher l'élément maximum. Mais si vous vouliez faire les deux opérations? Vous pouvez utiliser un Min-Max pour effectuer les deux opérations en temps constant. Cela fonctionne en utilisant la commande min max: alternant entre la comparaison de tas min et max entre les niveaux d'arborescence consécutifs.
En m'éloignant de toutes ces structures graphiques, j'adore le simple Ring-Buffer.
Une fois correctement implémenté, vous pouvez réduire sérieusement votre empreinte mémoire tout en maintenant, voire en améliorant vos performances.
Selon les indications du filtre Bloom, les filtres Bloom supprimables (DlBF) sont à certains égards meilleurs que les variantes de comptage de base. Voir http://arxiv.org/abs/1005.0352
Une file d'attente implémentée à l'aide de 2 piles est plutôt efficace en termes d'espace (par opposition à une liste chaînée qui aura au moins un temps système supplémentaire de pointeur/référence).
Comment implémenter une file d'attente en utilisant deux piles?
Cela a bien fonctionné pour moi lorsque les files d'attente sont énormes. Si je sauvegarde 8 octets sur un pointeur, cela signifie que les files d'attente contenant un million d'entrées permettent d'économiser environ 8 Mo de RAM.
Assez facile à mettre en œuvre.
La priorité deque est moins chère que de devoir maintenir deux files d'attente prioritaires séparées avec des ordres différents. http://www.alexandria.ucsb.edu/middleware/javadoc/edu/ucsb/adl/middleware/PriorityDeque.htmlhttp://cphstl.dk/Report/PriorityDeque.html deque/cphstl-report-2001-14.pdf) ==
Les listes de sauts sont en fait assez impressionnantes: http://en.wikipedia.org/wiki/Skip_list
Je pense que le FM-index de Paolo Ferragina et Giovanni Manzini est vraiment cool. Surtout en bioinformatique. Il s'agit essentiellement d'un index de texte intégral compressé utilisant une combinaison d'un tableau de suffixes et d'une transformation "terrier-fou" du texte de référence. L'index peut être recherché sans décompresser l'intégralité de l'index.
Je ne sais pas si cette structure de données a un nom, mais la structure de données proposée tokenmap
à inclure dans Boost est assez intéressante. C'est une carte redimensionnable dynamiquement où les recherches ne sont pas seulement O (1), elles sont de simples accès à des tableaux. J'ai écrit la plupart des documents de base sur cette structure de données qui décrit le principe fondamental de son fonctionnement.
Quelque chose comme une tokenmap est utilisé par les systèmes d'exploitation pour mapper les descripteurs de fichiers ou de ressources sur des structures de données représentant le fichier ou la ressource.
Une structure de données de chaîne appropriée. Presque tous les programmeurs optent pour le support natif d'une langue pour la structure, ce qui est généralement inefficace (en particulier pour la construction de chaînes, vous avez besoin d'une classe séparée ou autre).
Le pire est de traiter une chaîne comme un tableau de caractères en C et de s’appuyer sur l’octet NULL pour des raisons de sécurité.
Personnellement, je trouve les structures de données matricielles clairsemées très intéressantes. http://www.netlib.org/linalg/html_templates/node90.html
Les célèbres bibliothèques BLAS les utilisent. Et lorsque vous traitez avec des systèmes linéaires contenant 100 000 lignes et colonnes, il devient essentiel de les utiliser. Certains d'entre eux ressemblent également à la grille compacte (essentiellement une grille triée par seau) qui est courante en infographie. http://www.cs.kuleuven.be/~ares/publications/LD08CFRGRT/LD08CFRGRT.pdf
Aussi, en ce qui concerne l’infographie, les grilles MAC sont quelque peu intéressantes, mais uniquement parce qu’elles sont intelligentes. http://www.seas.upenn.edu/~cis665/projects/Liquation_665_Report.pdf
A structure de données cousue à l’angle . Du résumé:
La couture en coin est une technique permettant de représenter des objets rectangulaires en deux dimensions. Il semble être particulièrement bien adapté aux systèmes de montage interactifs pour les dispositions VLSI. La structure de données présente deux caractéristiques importantes: premièrement, les espaces vides sont représentés explicitement; et deuxièmement, les zones rectangulaires sont cousues à leurs coins comme une courtepointe en patchwork. Cette organisation génère des algorithmes rapides (temps linéaire ou supérieur) pour la recherche, la création, la suppression, l’étirement et le compactage. Les algorithmes sont présentés dans un modèle simplifié de circuits VLSI et les exigences de stockage de la structure sont discutées. Les mesures indiquent que la couture en coin nécessite environ trois fois plus d’espace mémoire que la représentation la plus simple possible.
transformation Burrows – Wheeler (compression par tri)
Son algorithme essentiel pour la compression. Disons que vous voulez compresser des lignes sur des fichiers texte. Vous diriez que si vous triez les lignes, vous perdez des informations. Mais BWT fonctionne comme ceci: il réduit considérablement l'entropie en triant les entrées, en conservant des index entiers pour récupérer l'ordre d'origine.
Forets Disjoint Set Forests autorisent les requêtes d'adhésion rapides et les opérations d'union. Ils sont utilisés de manière très répandue dans l'algorithme de Kruskal pour les arbres à recouvrement minimal.
La chose vraiment cool est que les deux opérations ont amorti le temps d'exécution proportionnel à inverse de Fonction Ackermann , ce qui en fait la structure de données à temps non constant la plus rapide.
Les listes/files d'attente delta sont utilisées dans des programmes tels que cron ou des simulateurs d'événements pour déterminer le moment où l'événement suivant doit se déclencher. http://everything2.com/title/delta+listhttp://www.cs.iastate.edu/~cs554/lec_notes/delta_clock.pdf
Brigade de seau
Ils sont largement utilisés dans Apache. Fondamentalement, ils sont une liste chaînée qui fait une boucle sur elle-même dans un anneau. Je ne suis pas sûr qu'ils soient utilisés en dehors des modules Apache et Apache, mais ils conviennent parfaitement à une structure de données aussi cool que moins connue. Un compartiment est un conteneur pour des données arbitraires et une brigade de compartiments est une collection de compartiments. L'idée est que vous voulez pouvoir modifier et insérer des données à n'importe quel point de la structure.
Disons que vous avez une brigade de seaux contenant un document HTML avec un caractère par seau. Vous souhaitez convertir tous les symboles <
et >
en entités <
et >
. La brigade à compartiments vous permet d'insérer des compartiments supplémentaires dans la brigade lorsque vous rencontrez un symbole <
ou >
afin d'adapter les caractères supplémentaires requis pour l'entité. Parce que la brigade de seau est dans un anneau, vous pouvez insérer en arrière ou en avant. C'est beaucoup plus facile à faire (en C) que d'utiliser un simple tampon.
Quelques références sur les brigades de seau ci-dessous:
PATRICIA - Algorithme pratique pour récupérer des informations codées en alphanumérique, D.R.Morrison (1968).
Un arbre PATRICIA est lié à un Trie. Le problème avec les essais est que lorsque l'ensemble de clés est rare, c'est-à-dire lorsque les clés réelles forment un petit sous-ensemble de l'ensemble de clés potentielles, comme c'est très souvent le cas, la plupart des nœuds internes du Trie ont uniquement un descendant. Cela entraîne une grande complexité d'espace dans le Trie.
http://www.csse.monash.edu.au/~lloyd/tildeAlgDS/Tree/PATRICIA/
structure de données Half Edge et Edge ailé pour les mailles polygonales.
Utile pour les algorithmes de géométrie de calcul.
Quelqu'un d'autre a déjà proposé Burkhard-Keller-Trees, mais j'ai pensé que je pourrais les mentionner à nouveau afin de brancher ma propre implémentation. :)
http://well-adjusted.de/mspace.py/index.html
Il existe des implémentations plus rapides (voir les recettes ou les implémentations de Python d'ActiveState dans d'autres langues), mais je pense/espère que mon code aide à comprendre ces structures de données.
Par ailleurs, les arbres BK et VP peuvent être utilisés pour beaucoup plus que la recherche de chaînes similaires. Vous pouvez effectuer des recherches de similarité pour des objets arbitraires à condition que votre fonction de distance satisfasse à quelques conditions (positivité, symétrie, inégalité de triangle).
Ce sont ceux que je peux venir à penser. Il y en a encore plus sur wikipedia sur structures de données
J'ai eu de la chance avec WPL Trees avant. Une variante d'arbre qui minimise la longueur de chemin pondérée des branches. Le poids est déterminé par l'accès aux nœuds, de sorte que les nœuds fréquemment utilisés migrent plus près de la racine. Je ne sais pas comment ils se comparent aux arbres évasés, car je ne les ai jamais utilisés.
Je pense que Cycle Sort est un algorithme de tri assez soigné.
C'est un algorithme de tri utilisé pour minimiser le nombre total d'écritures. Ceci est particulièrement utile lorsque vous utilisez une mémoire flash où la durée de vie de la mémoire flash est proportionnelle à la quantité d'écritures. Voici le article Wikipedia , mais je vous recommande d'aller au premier lien. (Beaux visuels!)
Binomial heap possède de nombreuses propriétés intéressantes, dont la plus utile est la fusion.
Environnement suivi des structures récursives.
Les compilateurs utilisent une structure qui est récursive mais pas comme une arborescence. Les étendues internes ont un pointeur sur une étendue englobante, de sorte que l'imbrication est à l'envers. Vérifier si une variable est dans la portée est un appel récursif de la portée interne à la portée englobante.
public class Env
{
HashMap<String, Object> map;
Env outer;
Env()
{
outer = null;
map = new HashMap();
}
Env(Env o)
{
outer = o;
map = new HashMap();
}
void put(String key, Object value)
{
map.put(key, value);
}
Object get(String key)
{
if (map.containsKey(key))
{
return map.get(key);
}
if (outer != null)
{
return outer.get(key);
}
return null;
}
Env Push()
{
return new Env(this);
}
Env pop()
{
return outer;
}
}
Je ne suis pas sûr si cette structure a même un nom. Je l'appelle une liste interne.
Il existe une structure de données intelligente qui utilise des tableaux pour enregistrer les données des éléments, mais les tableaux sont liés ensemble dans une liste/un tableau lié.
Cela présente l'avantage que l'itération sur les éléments est très rapide (plus rapide qu'une approche par liste chaînée) et que les coûts de déplacement des tableaux contenant les éléments en mémoire et/ou de (dés) allocation sont minimaux. (Pour cette raison, cette structure de données est utile pour les tâches de simulation).
Je sais à ce sujet d'ici:
http://software.intel.com/en-us/blogs/2010/03/26/linked-list-verses-array/
"... et qu'un tableau supplémentaire soit alloué et lié à la liste de cellules de tableaux de particules. Ceci est similaire à certains égards à la façon dont TBB a implémenté son conteneur simultané." (Il s'agit de la performance des listes liées par rapport aux tableaux )
Réseaux de triangles à angle droit (RTIN)
Moyen admirablement simple de subdiviser de manière adaptative un maillage. Les opérations de fractionnement et de fusion ne représentent que quelques lignes de code.
Je suis tombé sur une autre structure de données Arbre cartésien quand j'ai lu sur certains algorithmes liés à RMQ et LCA. Dans un arbre cartésien, l'ancêtre commun le plus bas entre deux nœuds est le nœud minimal entre eux. Il est utile de convertir un problème RMQ en ACL.