Мне нужно реализовать самодельный 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);
};





Я рад видеть, что Tries все еще преподают, это важная структура данных, которой часто пренебрегают.
В вашем коде может быть проблема с дизайном, поскольку у вас, вероятно, должны быть класс Trie и класс Node. То, как вы это написали, похоже, что каждый узел в вашем Trie - это свое собственное дерево, которое может работать, но усложняет некоторые вещи.
Из вашего вопроса не совсем понятно, с чем у вас возникла проблема: с расчетом порядка или с расчетом фактического кода?
Судя по названию итератора, похоже, что он должен работать в порядке префикса. Поскольку в вашем дереве хранятся слова, а его дочерние узлы организованы по буквам, вы, по сути, должны перебирать все слова в алфавитном порядке. Каждое приращение приведет вас к следующему слову.
Инвариант вашего итератора заключается в том, что в любой момент (пока он действителен) он должен указывать на узел с «символом-терминатором» для допустимого слова. Чтобы вычислить это слово, нужно просто сканировать вверх по родительской цепочке, пока не найдете всю строку. Переход к следующему слову означает выполнение поиска DFS: поднимитесь один раз, просканируйте ссылки в более поздних «братьях», посмотрите, найдете ли вы слово, если не рекурсивно, поднимитесь вверх и т. д.
Возможно, вы захотите увидеть мои модифицированные реализации trie по адресу:
В частности, вы можете обнаружить, что обсуждение, который у меня был на comp.lang.C++., Модерировался по поводу реализации итераторов для trie в соответствии с STL, что является проблемой, поскольку все контейнеры stl, к сожалению, вынуждены использовать std :: pair <>, а Поэтому итератор должен содержать значение, а не просто ссылку на единственный узел в дереве.
На вашей странице ничего не найдено?
Извините - сервер был перемещен, теперь URL-адрес должен работать. Источник находится в ртутном по адресу opensource.jdkoftinoff.com/hg-oss/jdktrie, а также в svn по адресу opensource.jdkoftinoff.com/jdks/svn/trunk/jdktrie/trunk
Эти ссылки снова мертвы: /
Снова простите! github лучше: github.com/jdkoftinoff/jdktrie/tree/master/include/jdktrie
Во-первых, показанный код на самом деле не описывает дерево. Скорее, это дерево, содержащее пару элементов в каждом узле (T* и unsigned). Вы можете по дисциплине использовать дерево кортежей в качестве дерева, но это только по соглашению, а не принудительно. Это одна из причин, почему вам так сложно реализовать operator++.
Что вам нужно сделать, так это сделать так, чтобы каждый Trie содержал непересекающийся лево-правый ADT, а не только необработанные элементы. Это уровень абстракции, который чаще встречается в функциональных языках (например, в Scala Либо). К сожалению, система типов 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;
Я играю быстро и свободно с вашими указателями, но вы уловили суть того, о чем я говорю. Важный бит заключается в том, что ни один данный узел не может содержать оба, unsigned и T*.
Попробуйте это и посмотрите, поможет ли это. Я думаю, вы обнаружите, что возможность легко определить, находитесь ли вы на листе или на ветке, очень поможет вам в вашей попытке повторить итерацию.
Хороший ответ. Кстати, иногда лучше хранить записи материалов в обратном порядке, например, доменные имена.