10

自家製の Trie を実装する必要があり、Iterator の部分で行き詰まっています。トライのインクリメント方法がわかりません。

誰かが私が物事を片付けるのを手伝ってくれることを願っています.

イテレータのコードは次のとおりです。

template <typename T> class Trie<T>::IteratorPrefixe{
friend class Trie<T>;
public:
    IteratorPrefixe() : tree(NULL), currentNode(NULL), currentKey("") {};
    pair<string, T*> operator*() {return make_pair(currentKey, currentNode -> element);} ;
    IteratorPrefixe operator++()throw(runtime_error);
    void operator=(IteratorPrefixe iter) {tree = iter.tree; currentNode = iter.currentNode; currentKey = iter.currentKey;};
    bool operator==(IteratorPrefixe iter) {return tree == iter.tree && currentNode == iter.currentNode;};
    bool operator!=(IteratorPrefixe iter) {return tree != iter.tree || currentNode != iter.currentNode;};
private:
    Trie<T> * tree;
    Trie<T> * currentNode;
    string currentKey;
};

そして、ここに私のトライがあります:

template <typename T> class Trie {
friend class IteratorPrefixe;
public:
    // Create a Trie<T> from the alphabet of nbletters, where nbletters must be
    // between 1 and NBLETTERSMAX inclusively
    Trie(unsigned nbletters) throw(runtime_error);

    // Add a key element of which is given in the first argument and content second argument
    // The content must be defined (different from NULL pointer)
    // The key is to be composed of valid letters (the letters between A + inclusive and exclusive nbletters
    //      Eg  if nblettres is 3, a, b and c are the only characters permitted;
    //          If nblettres is 15, only the letters between a and o inclusive are allowed.
    // Returns true if the insertion was achieved, returns false otherwise.
    bool addElement(string, T*) throw(runtime_error);
    // Deletes a key element of which is given as an argument and returns the contents of the node removed
    // The key is to be composed of letters valid (see above)
    // Can also delete at the same time the reference of the ancestors, if these ancestors are no longer used.
    // Returns NULL if the item has no delete
    T* removeElement(string cle) throw(runtime_error);
    // Find a key element of which is given as an argument and returns the associated content
    // The key is to be composed of letters valid (see above)
    // Returns NULL if the key does not exist
    T* searchElement(string cle) throw();
    // Iterator class to browse the Trie <T> in preorder mode
    class IteratorPrefixe;
    // Returns an iterator pointing to the first element
    IteratorPrefixe pbegin() throw(runtime_error);
    // Returns an iterator pointing beyond the last item
    IteratorPrefixe pend() throw();

private:
    unsigned nbLetters;
    T* element;
    vector<Trie<T> *> childs;
    Trie<T> * parent;

    // This function removes a node and its ancestors if became unnecessary. It is essentially the same work
    // as deleteElement that is how to designate remove a node that is changing. Moreover, unlike
    // deleteElement, it does not return any information on the node removed.
    void remove(Trie<T> * node) throw();

    // This function is seeking a node based on a given key. It is essentially the same work
    // searchElement but that returns a reference to the node found (or null if the node does not exist)
    // The key is to be composed of letters valid (see above)
    Trie<T>* search(string key) throw(runtime_error);
};
4

3 に答える 3

7

トライがまだ教えられているのを見てうれしく思います。トライはしばしば無視される重要なデータ構造です。

おそらく Trie クラスと Node クラスが必要なため、コードに設計上の問題がある可能性があります。あなたが書いた方法は、トライの各ノードが独自のトライであるように見えますが、これは機能しますが、いくつかのことを複雑にします。

あなたの質問からは、あなたが何に問題を抱えているのかがはっきりしていません.

イテレータの名前から、プレフィックス順に動作する必要があるように思えます。トライは単語を格納し、その子ノードは文字で編成されているため、基本的にすべての単語をアルファベット順に調べる必要があります。インクリメントするたびに、次の単語に移動します。

イテレータに関する不変条件は、(有効である限り) 任意の時点で、有効な単語の「ターミネータ文字」を持つノードを指している必要があるということです。その単語を理解するには、文字列全体が見つかるまで、親チェーンを上方向にスキャンするだけです。次の単語に移動するということは、DFS 検索を実行することを意味します。一度上に移動し、後の「兄弟」でリンクをスキャンし、単語が見つかるかどうかを確認し、再帰的に上に移動しない場合などです。

于 2008-12-08T23:39:54.097 に答える
2

私の変更されたトライ実装を次の場所で見たいと思うかもしれません:

具体的には、私が comp.lang.c++.moderated で行った STL 準拠の方法でトライの反復子を実装することについての議論を見つけることができます。そのため、反復子には、トライ内の単一ノードへの参照ではなく、値が含まれている必要があります。

于 2008-12-14T04:36:34.950 に答える
0

一つには、示されているコードは実際にはトライを記述していません。T*むしろ、各ノード(および)に要素のペアを含むツリーのように見えますunsigned規律によってタプルのツリーをトライとして使用できますが、それは慣例によるものであり、強制ではありません。これは、を実装するのに非常に苦労している理由の一部ですoperator++

あなたがする必要があるTrieのは、生の要素だけでなく、それぞれに左右の互いに素なADTを含めることです。これは、関数型言語(ScalaのEitherなど)でより一般的に見られる抽象化レイヤーです。残念ながら、C ++の型システムは、それほどエレガントなことをするのに十分なほど強力ではありません。ただし、これを妨げるものは何もありません。

template <class L, class R>
class Either
{
public:

    Either(L *l) : left(l), right(0)
    {}

    Either(R *r) : left(0), right(r)
    {}

    L *get_left() const
    {
        return left;
    }

    R *get_right() const
    {
        return right;
    }

    bool is_left() const
    {
        return left != 0;
    }

    bool is_right() const
    {
        return right != 0;
    }

private:
    L *left;
    R *right;
};

次に、Trieのデータメンバーは次のように定義されます。

private:
    Either<unsigned, T*> disjoint;

    vector<Trie<T> *> children;    // english pluralization
    Trie<T> * parent;

私はあなたのポインターで速くそして緩く遊んでいます、しかしあなたは私が言っていることの要点を理解します。重要な点は、特定のノードにanとaの両方を含めることはできないということです。unsignedT*

これを試して、それが役立つかどうかを確認してください。自分が葉の上にいるのか枝にいるのかを簡単に判断できると、反復を試みる際に非常に役立つことがわかると思います。

于 2008-12-14T05:30:53.233 に答える