У меня есть дерево префикса для хранения огромной коллекции слов. Прямо сейчас, если я хочу найти все слова с общим префиксом, скажем, "a", я сначала извлекаю первый узел, содержащий a, а затем исчерпывающий поиск в методе Depth First в дочерних узлах первого узла. Хотя эта идея выглядит наивной и простой, она на самом деле патетически медленна, если возможное количество слов с общим префиксом ОЧЕНЬ ВЫСОКО (> 20 КБ). Есть ли другой способ эффективно получить все слова, начинающиеся с общего префикса? Или я должен принять другую структуру данных? Спасибо вам за продвижение.
EDIT1 В основном я создаю полное слово, посещая каждый узел и добавляя символ постепенно. Все слова затем сохраняются в векторном контейнере. И да, у меня рекурсивная реализация.
EDIT2
vector<int> getNonEmptyEdgeIndices(Node* parent) {
vector<int> indices;
for(int i=0; i<EDGE; i++) {
if (parent->edges[i] != NULL) {
indices.push_back(i);
}
}
return indices;
}
vector<string> getSubsequentStrings(vector<string> wordsStartingWith, Node* node, string prefix) {
vector<int> indices = getNonEmptyEdgeIndices(node);
// push the word to the container if node is a leaf
if (indices.empty()) {
wordsStartingWith.push_back(prefix);
return wordsStartingWith;
}
// if frequency is set in node, push the word but still continue recursion
if (node->frequency != 0) {
wordsStartingWith.push_back(prefix);
}
// look all the children of the node
for(unsigned int i=0; i<indices.size(); i++) {
string newPrefix = prefix + getNodeChar(indices[i]);
Node* child = node->edges[indices[i]];
// recursively get the prefix for all children
wordsStartingWith = getSubsequentStrings(wordsStartingWith, child, newPrefix);
}
return wordsStartingWith;
}
vector<string> Trie::getWordsStartingWith(string prefix) {
vector<string> wordsStartingWith;
Node* lastNode = getLastNode(prefix);
if (lastNode != NULL) {
wordsStartingWith = getSubsequentStrings(wordsStartingWith, lastNode, prefix);
}
return wordsStartingWith;
}
ИЗМЕНИТЬ 3 РЕШЕНЫ !!! На самом деле была проблема с моей реализацией. Я передавал этот огромный контейнер векторной строки в рекурсивных вызовах, что было на самом деле проблемой. Спасибо всем за ваш добрый совет.
На самом деле TRIE + DFT уже является достаточно хорошим решением для вашего дела. Его временной сложностью является O(M+B^M)
где M
- максимальная длина слова, а B
- постоянное число возможных букв (обычно B=26
). Хотя это экспоненциально, но на практике это может быть намного быстрее, чем вы думаете, потому что дерево TRIE очень редкое, а M
- небольшое число.
Более простым (не гарантированным лучшим) решением является сортировка всех слов в массиве. Затем вы можете получить то, что хотите, путем двоичного поиска массива для первого и последнего слова, у которого есть целевой префикс, точно так же, как вы используете английский словарь. Сортировка принимает O(NlogN)
а поиск принимает O(MlogN)
где N
- количество слов. Это многочлен.
Если вам действительно нужна скорость, вы можете почти всегда оплачивать пространство памяти для обмена. В этом случае вы можете связать каждое слово со всеми его префиксными узлами указателями во время построения дерева TRIE. Тогда временная сложность будет сведена к O(M+N)
которая будет чрезвычайно быстрой. Но, с другой стороны, это займет пространственную сложность O(NM)
. Предположим, что у вас есть миллион слов с 5 буквами в среднем, вы потратили бы около 150 КБ памяти на указатели.