diff --git a/QVectorSearch/mainwindow.cpp b/QVectorSearch/mainwindow.cpp index c37b2a6..9d9652f 100644 --- a/QVectorSearch/mainwindow.cpp +++ b/QVectorSearch/mainwindow.cpp @@ -5,10 +5,11 @@ #include "linear_finder.h" #include "parallel_finder.h" #include "timer.h" +#include "tree_finder.h" #include "word_list_generator.h" -#include #include +#include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { @@ -26,6 +27,7 @@ MainWindow::~MainWindow() { delete ui; } void MainWindow::setupAlgorithmSelector() { search_algorithms_.appendRow(new QStandardItem("Linear search")); search_algorithms_.appendRow(new QStandardItem("Parallel search")); + search_algorithms_.appendRow(new QStandardItem("Tree search")); } void MainWindow::generateWordList() { @@ -36,13 +38,6 @@ void MainWindow::generateWordList() { std::stringstream status_message; status_message << "generated " << word_list_.size() << " words in " << timer; ui->mainStatusBar->showMessage(QString::fromStdString(status_message.str())); - - QStringList words; - for (const auto &word : word_list_) { - words.append(QString::fromStdString(word)); - } - - result_model_.setStringList(words); } void MainWindow::search(const QString &search_term) { @@ -53,32 +48,37 @@ void MainWindow::search(const QString &search_term) { } Timer timer; - const auto finder = createSelectedFinder(); - const auto results = finder->find_prefix(search_term.toStdString()); + const auto results = finder_->find_prefix(search_term.toStdString()); timer.stop(); std::stringstream status_message; - status_message << "search took " << timer << ", found " << results.size() - << " results"; + status_message << "search took " << timer << ", found " + << std::distance(results.begin(), results.end()) << " results"; ui->mainStatusBar->showMessage(QString::fromStdString(status_message.str())); showResults(results); } -std::unique_ptr MainWindow::createSelectedFinder() const { +void MainWindow::createSelectedFinder() { auto selectedFinder = ui->searchAlgorithmSelector->currentIndex(); switch (selectedFinder) { case 0: default: - return std::make_unique(word_list_); + finder_ = std::make_unique(word_list_); + break; case 1: - return std::make_unique( + finder_ = std::make_unique( word_list_, std::thread::hardware_concurrency()); + break; + case 2: + finder_ = std::make_unique(word_list_); + break; } } -void MainWindow::showResults(const std::vector &results) { +void MainWindow::showResults( + const std::forward_list &results) { QStringList ui_results; for (auto word : results) { ui_results.append(QString::fromStdString(*word)); @@ -92,5 +92,6 @@ void MainWindow::on_searchInput_textChanged(const QString &search_term) { } void MainWindow::on_searchAlgorithmSelector_currentIndexChanged(int) { + createSelectedFinder(); search(ui->searchInput->displayText()); } diff --git a/QVectorSearch/mainwindow.h b/QVectorSearch/mainwindow.h index 15ea3f8..0a38215 100644 --- a/QVectorSearch/mainwindow.h +++ b/QVectorSearch/mainwindow.h @@ -19,6 +19,7 @@ private: std::vector word_list_; QStringListModel result_model_; QStandardItemModel search_algorithms_; + std::unique_ptr finder_; public: MainWindow(QWidget *parent = nullptr); @@ -28,8 +29,8 @@ private: void setupAlgorithmSelector(); void generateWordList(); void search(const QString &search_term); - std::unique_ptr createSelectedFinder() const; - void showResults(const std::vector &results); + void createSelectedFinder(); + void showResults(const std::forward_list &results); private slots: void on_searchInput_textChanged(const QString &search_term); diff --git a/lib_vector_search/CMakeLists.txt b/lib_vector_search/CMakeLists.txt index edb6b42..6c00ac7 100644 --- a/lib_vector_search/CMakeLists.txt +++ b/lib_vector_search/CMakeLists.txt @@ -5,8 +5,18 @@ project( VERSION 0.1.0 LANGUAGES CXX) -add_library(vector_search STATIC src/word_list_generator.cpp src/timer.cpp - src/linear_finder.cpp src/parallel_finder.cpp) +add_library( + vector_search STATIC + src/word_list_generator.cpp + include/word_list_generator.h + src/timer.cpp + include/timer.h + src/linear_finder.cpp + include/linear_finder.h + src/parallel_finder.cpp + include/parallel_finder.h + src/tree_finder.cpp + include/tree_finder.h) target_include_directories(vector_search PUBLIC ${PROJECT_SOURCE_DIR}/include) diff --git a/lib_vector_search/include/finder.h b/lib_vector_search/include/finder.h index 9543adf..1266392 100644 --- a/lib_vector_search/include/finder.h +++ b/lib_vector_search/include/finder.h @@ -1,12 +1,14 @@ #pragma once +#include #include -#include -using std::string, std::string_view, std::vector; +using std::string, std::string_view, std::forward_list; class Finder { public: virtual ~Finder() = default; - virtual vector find_prefix(string_view search_term) const = 0; + + virtual forward_list + find_prefix(string_view search_term) const = 0; }; diff --git a/lib_vector_search/include/linear_finder.h b/lib_vector_search/include/linear_finder.h index e9f3419..646c248 100644 --- a/lib_vector_search/include/linear_finder.h +++ b/lib_vector_search/include/linear_finder.h @@ -2,11 +2,16 @@ #include "finder.h" +#include + +using std::vector; + class LinearFinder : public Finder { private: const vector &word_list_; public: LinearFinder(const vector &word_list); - vector find_prefix(string_view search_term) const override; + forward_list + find_prefix(string_view search_term) const override; }; diff --git a/lib_vector_search/include/parallel_finder.h b/lib_vector_search/include/parallel_finder.h index 556a167..7226e0c 100644 --- a/lib_vector_search/include/parallel_finder.h +++ b/lib_vector_search/include/parallel_finder.h @@ -2,12 +2,16 @@ #include "finder.h" +#include + class ParallelFinder : public Finder { private: const size_t thread_count_; - const vector &word_list_; + const std::vector &word_list_; public: - ParallelFinder(const vector &word_list, size_t thread_count); - vector find_prefix(string_view search_term) const override; + ParallelFinder(const std::vector &word_list, size_t thread_count); + + std::forward_list + find_prefix(std::string_view search_term) const override; }; diff --git a/lib_vector_search/include/tree_finder.h b/lib_vector_search/include/tree_finder.h new file mode 100644 index 0000000..6117573 --- /dev/null +++ b/lib_vector_search/include/tree_finder.h @@ -0,0 +1,34 @@ +#pragma once + +#include "finder.h" + +#include +#include + +class SearchTreeNode { +private: + std::forward_list words_; + std::map children_; + +public: + void insert(std::string_view partial_word, const string *original_word); + const SearchTreeNode *find(std::string_view search_term) const; + + std::forward_list words() const; +}; + +class SearchTree : public SearchTreeNode { +public: + SearchTree(const std::vector &word_list); +}; + +class TreeFinder : public Finder { +private: + SearchTree search_tree_; + +public: + TreeFinder(const std::vector &word_list); + + virtual std::forward_list + find_prefix(std::string_view search_term) const override; +}; diff --git a/lib_vector_search/src/linear_finder.cpp b/lib_vector_search/src/linear_finder.cpp index f6c76c1..0c4705e 100644 --- a/lib_vector_search/src/linear_finder.cpp +++ b/lib_vector_search/src/linear_finder.cpp @@ -3,13 +3,13 @@ LinearFinder::LinearFinder(const vector &word_list) : word_list_(word_list) {} -vector +forward_list LinearFinder::find_prefix(string_view search_term) const { - vector matching_words; + forward_list matching_words; for (const auto ¤t_word : word_list_) { if (current_word.starts_with(search_term)) { - matching_words.push_back(¤t_word); + matching_words.push_front(¤t_word); } } diff --git a/lib_vector_search/src/parallel_finder.cpp b/lib_vector_search/src/parallel_finder.cpp index d83b97a..1ea4676 100644 --- a/lib_vector_search/src/parallel_finder.cpp +++ b/lib_vector_search/src/parallel_finder.cpp @@ -3,15 +3,15 @@ #include #include -using std::mutex, std::thread, std::lock_guard; +using std::mutex, std::thread, std::lock_guard, std::vector, std::forward_list; ParallelFinder::ParallelFinder(const vector &word_list, size_t thread_count) : word_list_(word_list), thread_count_(thread_count) {} -vector +forward_list ParallelFinder::find_prefix(string_view search_term) const { - vector result; + forward_list result; mutex result_mutex; vector threads; @@ -25,15 +25,19 @@ ParallelFinder::find_prefix(string_view search_term) const { threads.emplace_back( [](const vector &word_list, const string_view &search_term, - vector &result, size_t start_index, size_t end_index, - mutex &result_mutex) { + forward_list &result, size_t start_index, + size_t end_index, mutex &result_mutex) { + forward_list thread_results; for (size_t index = start_index; index < end_index; ++index) { const auto ¤t_word = word_list[index]; if (current_word.starts_with(search_term)) { - const lock_guard lock(result_mutex); - result.push_back(¤t_word); + thread_results.push_front(¤t_word); } } + if (!thread_results.empty()) { + const lock_guard lock(result_mutex); + result.merge(thread_results); + } }, cref(word_list_), cref(search_term), ref(result), first_word_index, last_word_index, ref(result_mutex)); diff --git a/lib_vector_search/src/tree_finder.cpp b/lib_vector_search/src/tree_finder.cpp new file mode 100644 index 0000000..9b94682 --- /dev/null +++ b/lib_vector_search/src/tree_finder.cpp @@ -0,0 +1,53 @@ +#include "tree_finder.h" + +void SearchTreeNode::insert(std::string_view partial_word, + const string *original_word) { + if (partial_word.empty()) { + words_.push_front(original_word); + } else { + children_[partial_word.front()].insert( + std::string_view(partial_word).substr(1, partial_word.length()), + original_word); + } +} + +const SearchTreeNode *SearchTreeNode::find(std::string_view search_term) const { + if (search_term.empty()) { + return this; + } + + auto child = children_.find(search_term.front()); + if (child != children_.cend()) { + return child->second.find(search_term.substr(1, search_term.length())); + } + + return nullptr; +} + +std::forward_list SearchTreeNode::words() const { + std::forward_list results(words_); + for (const auto &child : children_) { + results.merge(child.second.words()); + } + + return results; +}; + +SearchTree::SearchTree(const std::vector &word_list) { + for (const auto &word : word_list) { + insert(std::string_view(word), &word); + } +} + +TreeFinder::TreeFinder(const std::vector &word_list) + : search_tree_(word_list) {} + +std::forward_list +TreeFinder::find_prefix(std::string_view search_term) const { + const auto *result_node = search_tree_.find(search_term); + if (result_node == nullptr) { + return {}; + } + + return result_node->words(); +} diff --git a/vector_search_cli/main.cpp b/vector_search_cli/main.cpp index ace12f7..0d31e5b 100644 --- a/vector_search_cli/main.cpp +++ b/vector_search_cli/main.cpp @@ -1,6 +1,7 @@ #include "linear_finder.h" #include "parallel_finder.h" #include "timer.h" +#include "tree_finder.h" #include "word_list_generator.h" #include @@ -26,16 +27,15 @@ vector generate_word_list() { void test_linear_finder(const vector &word_list) { cout << "\nrunning linear finder" << endl; - Timer constructor_timer; LinearFinder linear_finder(word_list); - constructor_timer.stop(); Timer find_timer; auto result = linear_finder.find_prefix("ABCD"); find_timer.stop(); cout << "linear finder took " << find_timer << endl; - cout << "result list is " << result.size() << " element(s) long" << endl; + cout << "result list is " << std::distance(result.cbegin(), result.cend()) + << " element(s) long" << endl; } void test_parallel_finder(const vector &word_list) { @@ -45,16 +45,33 @@ void test_parallel_finder(const vector &word_list) { cout << "using " << thread_count << " threads" << endl; - Timer constructor_timer; ParallelFinder parallel_finder(word_list, thread_count); - constructor_timer.stop(); Timer find_timer; auto result = parallel_finder.find_prefix("ABCD"); find_timer.stop(); cout << "parallel finder took " << find_timer << endl; - cout << "result list is " << result.size() << " element(s) long" << endl; + cout << "result list is " << std::distance(result.cbegin(), result.cend()) + << " element(s) long" << endl; +} + +void test_tree_finder(const vector &word_list) { + cout << "\nrunning tree finder" << endl; + + Timer constructor_timer; + TreeFinder tree_finder(word_list); + constructor_timer.stop(); + + cout << "tree finder constructor took " << constructor_timer << endl; + + Timer find_timer; + auto result = tree_finder.find_prefix("ABCD"); + find_timer.stop(); + + cout << "tree finder took " << find_timer << endl; + cout << "result list is " << std::distance(result.cbegin(), result.cend()) + << " element(s) long" << endl; } int main(int argc, char *argv[]) { @@ -64,6 +81,7 @@ int main(int argc, char *argv[]) { test_linear_finder(word_list); test_parallel_finder(word_list); + test_tree_finder(word_list); return EXIT_SUCCESS; }