From 1f55da944474bc67ef5db27c2c783dd1784a0fe2 Mon Sep 17 00:00:00 2001 From: Anmol3299 Date: Sat, 30 May 2020 01:49:13 +0530 Subject: [PATCH 1/3] feat: Modern trie code with better memory management --- data_structure/trie_modern.cpp | 156 +++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 data_structure/trie_modern.cpp diff --git a/data_structure/trie_modern.cpp b/data_structure/trie_modern.cpp new file mode 100644 index 000000000..0214969f0 --- /dev/null +++ b/data_structure/trie_modern.cpp @@ -0,0 +1,156 @@ +#include +#include // for std::shared_ptr<> +#include // for std::string class + +/** + * A basic implementation of trie class to store only lower-case strings. + * You can extend the implementation to all the ASCII characters by changing the + * value of @ ALPHABETS to 128. + */ +class Trie { + private: + static constexpr size_t ALPHABETS = 26; + + /** + * Structure of trie node. + * This struct doesn't need a constructor as we are initializing using + * intializer list which is more efficient than if we had done so with + * constructor. + */ + struct TrieNode { + // An array of pointers of size 26 which tells if a character of word is + // present or not. + std::shared_ptr character[ALPHABETS]{nullptr}; + + bool isEndOfWord{false}; + }; + + /** + * Function to check if a node has some children which can form words. + * @param node whose character array of pointers need to be checked for + * children. + * @return if a child is found, it returns @ true, else it returns @ false. + */ + inline static bool hasChildren(std::shared_ptr node) { + for (size_t i = 0; i < ALPHABETS; i++) { + if (node->character[i]) { + return true; + } + } + return false; + } + + /** + * A recursive helper function to remove a word from the trie. First, it + * recursively traverses to the location of last character of word in the + * trie. However, if the word is not found, the function returns a runtime + * error. Upon successfully reaching the last character of word in trie, if + * sets the isEndOfWord to false and deletes the node if and only if it has + * no children, else it returns the current node. + * @param word is the string which needs to be removed from trie. + * @param curr is the current node we are at. + * @param index is the index of the @word we are at. + * @return if current node has childern, it returns @ curr, else it returns + * nullptr. In case @ word is not found in the trie, the program stops and + * gives a runtime error. + */ + std::shared_ptr removeWordHelper(const std::string& word, + std::shared_ptr curr, + size_t index) { + if (word.size() == index) { + if (curr->isEndOfWord) { + curr->isEndOfWord = false; + } + if (hasChildren(curr)) { + return curr; + } + return nullptr; + } + + size_t idx = word[index] - 'a'; + + // Throw a runtime error in case the user enters a word which is not + // present in the trie. + if (!curr->character[idx]) { + throw std::runtime_error(std::move(std::string("Word not found."))); + } + + curr->character[idx] = + removeWordHelper(word, curr->character[idx], index + 1); + + // This if condition checks if the node has some childern. + // The 1st if check, i.e. (curr->character[idx]) is checked specifically + // because if the older string is a prefix of some other string, then, + // there would be no need to check all 26 characters. Example- str1 = + // abbey, str2 = abbex and we want to delete string "abbey", then in + // this case, there would be no need to check all characters for the + // chars a,b,b. + if (curr->character[idx] || hasChildren(curr)) { + return curr; + } + return nullptr; + } + + public: + // constructor to initialise the root of the trie. + Trie() : m_root(std::make_shared()){}; + + /** + * Insert a word into the trie. + * @param word which needs to be inserted into the string. + */ + void insert(const std::string& word) { + auto curr = m_root; + for (char ch : word) { + size_t index = ch - 'a'; + + // if a node for current word is not already present in trie, create + // a new node for it. + if (!curr->character[index]) { + curr->character[index] = std::make_shared(); + } + + curr = curr->character[index]; + } + curr->isEndOfWord = true; + } + + /** + * Search if a word is present in trie or not. + * @param word which is needed to be searched in the trie. + * @return if the word is found in the trie and isEndOfWord is set to true, + * then it returns @ true, else it returns @ false. + */ + bool search(const std::string& word) { + auto curr = m_root; + for (char ch : word) { + size_t index = ch - 'a'; + + // if any node for a character is not found, then return that the + // word cannot be formed. + if (!curr->character[index]) { + return false; + } + curr = curr->character[index]; + } + return curr->isEndOfWord; + } + + // Function to remove the word which calls the helper function. + void removeWord(const std::string& word) { + m_root = removeWordHelper(word, m_root, 0); + } + + private: + std::shared_ptr m_root; +}; + +int main() { + Trie trie; + trie.insert("hel"); + trie.insert("hello"); + trie.removeWord("hel"); + std::cout << trie.search("hello") << '\n'; + + return 0; +} From 517f71a2dd8c214a31495761ff0b5dd548838e16 Mon Sep 17 00:00:00 2001 From: Anmol3299 Date: Sat, 30 May 2020 01:57:16 +0530 Subject: [PATCH 2/3] minor fix to code style --- data_structure/trie_modern.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_structure/trie_modern.cpp b/data_structure/trie_modern.cpp index 0214969f0..098ba1bb9 100644 --- a/data_structure/trie_modern.cpp +++ b/data_structure/trie_modern.cpp @@ -93,7 +93,7 @@ class Trie { public: // constructor to initialise the root of the trie. - Trie() : m_root(std::make_shared()){}; + Trie() : m_root(std::make_shared()) {} /** * Insert a word into the trie. From 0101f0ce2877b725fafc90f9949a1e9dd6e472c6 Mon Sep 17 00:00:00 2001 From: Anmol3299 Date: Sat, 30 May 2020 02:24:14 +0530 Subject: [PATCH 3/3] Add copyright header and improve comments --- data_structure/trie_modern.cpp | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/data_structure/trie_modern.cpp b/data_structure/trie_modern.cpp index 098ba1bb9..218c90b68 100644 --- a/data_structure/trie_modern.cpp +++ b/data_structure/trie_modern.cpp @@ -1,6 +1,12 @@ -#include -#include // for std::shared_ptr<> -#include // for std::string class +/** + * Copyright 2020 @author Anmol3299 + * @file + * + * A basic implementation of trie class to store only lower-case strings. + */ +#include // for io operations +#include // for std::shared_ptr<> +#include // for std::string class /** * A basic implementation of trie class to store only lower-case strings. @@ -51,8 +57,8 @@ class Trie { * @param curr is the current node we are at. * @param index is the index of the @word we are at. * @return if current node has childern, it returns @ curr, else it returns - * nullptr. In case @ word is not found in the trie, the program stops and - * gives a runtime error. + * nullptr. + * @throw a runtime error in case @ word is not found in the trie. */ std::shared_ptr removeWordHelper(const std::string& word, std::shared_ptr curr, @@ -118,8 +124,9 @@ class Trie { /** * Search if a word is present in trie or not. * @param word which is needed to be searched in the trie. - * @return if the word is found in the trie and isEndOfWord is set to true, - * then it returns @ true, else it returns @ false. + * @return True if the word is found in trie and isEndOfWord is set to true. + * @return False if word is not found in trie or isEndOfWord is set to + * false. */ bool search(const std::string& word) { auto curr = m_root; @@ -142,9 +149,13 @@ class Trie { } private: + // data member to store the root of the trie. std::shared_ptr m_root; }; +/** + * Main function + */ int main() { Trie trie; trie.insert("hel");