3. Код для добавления новых последовательностей элементов выглядит довольно просто. Пользователь предоставляет пару итераторов (начальный и конечный), и мы проходим по ним рекурсивно. Если он ввел данные {1, 2, 3}
1
в поддереве, а затем ищем значение 2
в следующем поддереве, чтобы получить поддерево для значения 3
. Какое-то из этих поддеревьев, ранее не существовавшее, будет добавлено с помощью оператора []
контейнера std::map
.public:
template
void insert(It it, It end_it) {
if (it == end_it) { return; }
tries[*it].insert(next(it), end_it);
}
4. Для удобства определим также отдельные функции, они дают пользователю возможность получить контейнер элементов, которые будут автоматически опрошены на предмет итераторов:
template
void insert(const C &container) {
insert(begin(container), end(container));
}
5. Чтобы позволить пользователю написать конструкцию my_trie.insert({"a", "b", "c"});
initializer_list
: void insert(const initializer_list
insert(begin(il), end(il));
}
6. Мы также хотим видеть содержимое дерева, поэтому нужна функция print
tries.empty()
возвращает значение true
. После рекурсивного вызова функции print
мы снова выталкиваем последний элемент с полезной нагрузкой. void print(vector
if (tries.empty()) {
copy(begin(v), end(v), ostream
_iterator
cout << '\n';
}
for (const auto &p : tries) {
v.push_back(p.first);
p.second.print(v);
v.pop_back();
}
}
7. Рекурсивная функция print
print
без параметров, в которой создается вспомогательный объект списка: void print() const {
vector
print(v);
}
8. Теперь, когда мы научились создавать деревья и выводить их на экран, может понадобиться выполнить поиск по поддеревьям. Идея заключается в следующем: если дерево содержит последовательности наподобие {a,b,c} {a,b,d,e}
{a,b}
для поиска, то поиск вернет поддерево, которое содержит части {c}
и {d, e}
. При обнаружении поддерева возвращаем ссылку const
на нее. Существует вероятность того, что такого поддерева не существует, если дерево не содержит искомой последовательности. В подобных случаях все равно нужно std::optional
, поскольку можно вернуть template
optional
subtrie(It it, It end_it) const {
if (it == end_it) { return ref(*this); }
auto found (tries.find(*it));
if (found == end(tries)) { return {}; }
return found->second.subtrie(next(it), end_it);
}
9. Аналогично методу insert
subtrie
с одним параметром, которая автоматически принимает итераторы из входного контейнера: template
auto subtrie(const C &c) {
return subtrie(begin(c), end(c));
}
};
10. На этом все. Воспользуемся нашим новым классом trie
main
, создав экземпляр класса trie
, работающего с объектами класса std::string
, и заполнив его каким-нибудь содержимым:int main()
{
trie
t.insert({"hi", "how", "are", "you"});
t.insert({"hi", "i", "am", "great", "thanks"});
t.insert({"what", "are", "you", "doing"});
t.insert({"i", "am", "watching", "a", "movie"});
11. Сначала выведем на экран все дерево:
cout << "recorded sentences:\n";
t.print();