web-dev-qa-db-fra.com

Implémentation de Trie

Existe-t-il des implémentations de trie efficaces en vitesse et en cache en C/C++? Je sais ce qu'est un trie, mais je ne veux pas réinventer la roue, la mettre en œuvre moi-même.

65
Anton Kazennikov

si vous cherchez une implémentation ANSI C, vous pouvez la "voler" à FreeBSD. Le fichier que vous recherchez s'appelle radix.c . Il est utilisé pour gérer le routage des données dans le noyau.

36
SashaN

Je me rends compte que la question portait sur des implémentations prêtes, mais pour référence ...

Avant de sauter sur Judy, vous devriez avoir lu " ne comparaison des performances de Judy aux tables de hachage ". Ensuite, googler le titre vous donnera probablement une vie de discussions et de réfutations à lire.

Le trie explicitement conscient du cache que je connais est le trie HAT .

Le HAT-trie, lorsqu'il est correctement implémenté, est très cool. Cependant, pour la recherche de préfixe, vous avez besoin d'une étape de tri sur les compartiments de hachage, ce qui se heurte quelque peu à l'idée d'une structure de préfixe.

Un tri un peu plus simple est le burst-trie qui vous donne essentiellement une interpolation entre un arbre standard quelconque (comme un BST) et un trie. J'aime conceptuellement et c'est beaucoup plus facile à mettre en œuvre.

19
eloj

J'ai eu de la chance avec libTrie . Il n'est peut-être pas spécifiquement optimisé pour le cache, mais les performances ont toujours été décentes pour mes applications.

7
SPWorley

GCC est livré avec une poignée de structures de données dans le cadre de ses "structures de données basées sur des politiques". Cela comprend quelques implémentations de trois.

http://gcc.gnu.org/onlinedocs/libstdc++/ext/pb_ds/trie_based_containers.html

5
tgoodhart

tableaux Judy : tableaux dynamiques ordonnés très rapides et efficaces en mémoire pour les bits, les entiers et les chaînes. Les tableaux Judy sont plus rapides et plus efficaces en mémoire que n'importe quel arbre de recherche binaire (y compris les arbres avl et rouge-noir).

4
bill

Les références,

  • A implémentation Double-Array Trie article (inclut une implémentation C)
  • CORBEILLE - Un LC-trie dynamique et une structure de données de hachage - (une référence 2006 PDF PDF décrivant un LC-trie dynamique) utilisé dans le noyau Linux pour implémenter la recherche d'adresse dans la table de routage IP
4
nik

Cedar , HAT-Trie , et JudyArray est assez génial, vous pouvez trouver la référence ici .

benchmark result

2
Kokizzu

Vous pouvez également essayer TommyDS sur http://tommyds.sourceforge.net/

Voir la page de repères sur le site pour une comparaison de vitesse avec nedtries et judy.

2
amadvance

Vous devrez probablement faire des optimisations de cache, car vous devrez adapter les données dans une seule ligne de cache qui est généralement quelque chose comme 64 octets (ce qui fonctionnera probablement si vous commencez à combiner des données, telles que des pointeurs) . Mais c'est délicat :-)

1
Jasper Bekkers

Burst Trie's semble être un peu plus économe en espace. Je ne suis pas sûr de l'efficacité du cache que vous pouvez retirer de n'importe quel index car les caches CPU sont si minuscules. Cependant, ce type de trie est suffisamment compact pour conserver de grands ensembles de données dans RAM (où un Trie normal ne le ferait pas).

J'ai écrit une implémentation Scala d'un triplet rafale qui incorpore également des techniques d'économie d'espace que j'ai trouvées dans l'implémentation de type-forward de GWT.

https://github.com/nbauernfeind/scala-burst-trie

0
Nate

Partager mon implémentation "rapide" pour Trie, testée uniquement dans le scénario de base:

#define ENG_LET 26

class Trie {
    class TrieNode {
    public:
        TrieNode* sons[ENG_LET];
        int strsInBranch;
        bool isEndOfStr;

        void print() {
            cout << "----------------" << endl;
            cout << "sons..";
            for(int i=0; i<ENG_LET; ++i) {
                if(sons[i] != NULL)
                    cout << " " << (char)('a'+i);
            }
            cout << endl;
            cout << "strsInBranch = " << strsInBranch << endl;
            cout << "isEndOfStr = " << isEndOfStr << endl;
            for(int i=0; i<ENG_LET; ++i) {
                if(sons[i] != NULL)
                    sons[i]->print();
            }

        }
        TrieNode(bool _isEndOfStr = false):isEndOfStr(_isEndOfStr), strsInBranch(0) {
            for(int i=0; i<ENG_LET; ++i) {
                sons[i] = NULL;
            }
        }
        ~TrieNode() {
            for(int i=0; i<ENG_LET; ++i) {
                delete sons[i];
                sons[i] = NULL;
            }
        }
    };

    TrieNode* head;
public:
    Trie() { head = new TrieNode();}
    ~Trie() { delete head; }
    void print() {
        cout << "Preorder Print : " << endl;
        head->print();
    }
    bool isExists(const string s) {
        TrieNode* curr = head;
        for(int i=0; i<s.size(); ++i) {
            int letIdx = s[i]-'a';
            if(curr->sons[letIdx] == NULL) {
                return false;
            }
            curr = curr->sons[letIdx];
        }

        return curr->isEndOfStr;
    }
    void addString(const string& s) {
        if(isExists(s))
            return;
        TrieNode* curr = head;
        for(int i=0; i<s.size(); ++i) {
            int letIdx = s[i]-'a';
            if(curr->sons[letIdx] == NULL) {
                curr->sons[letIdx] = new TrieNode();    
            }
            ++curr->strsInBranch;
            curr = curr->sons[letIdx];
        }
        ++curr->strsInBranch;
        curr->isEndOfStr = true;
    }
    void removeString(const string& s) {
        if(!isExists(s))
            return;
        TrieNode* curr = head;
        for(int i=0; i<s.size(); ++i) {
            int letIdx = s[i]-'a';

            if(curr->sons[letIdx] == NULL) {
                assert(false);
                return; //string not exists, will not reach here
            }
            if(curr->strsInBranch==1) {    //just 1 str that we want remove, remove the whole branch
                delete curr;
                return;
            }
            //more than 1 son
            --curr->strsInBranch;
            curr = curr->sons[letIdx];
        }
        curr->isEndOfStr = false;
    }

    void clear() {
        for(int i=0; i<ENG_LET; ++i) {
            delete head->sons[i];
            head->sons[i] = NULL;
        }
    }

};
0
Shady Sirhan

J'ai obtenu de très bons résultats (très bon équilibre entre performances et taille) avec marisa-trie par rapport à plusieurs implémentations TRIE mentionnées avec mon jeu de données.

https://github.com/s-yata/marisa-trie/tree/master

0
benjist