forked from TheAlgorithms/C-Plus-Plus
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[code fix] Fixed trie_tree for code quality and docs & clang-tidy err…
…or check (TheAlgorithms#1059) * attempt to fix trie-tree code * clang-tidy fixes * remove performance-unnecessary-value-param as error - this has auto-fix * make test() static * added original author to the description * added namespace * doc updates * fix true spelling * extended to small & big caps alphabets - 52 characters * clang-format and clang-tidy fixes for edc2247 * move const parameter to after the parameters Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
- Loading branch information
Showing
2 changed files
with
189 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
--- | ||
Checks: '-*,google-*,clang-analyzer-*,-clang-analyzer-security.insecureAPI.*,cppcoreguidelines-*,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-pro-bounds-*,openmp-*,performance-*,portability-*,modernize-*,-modernize-use-trailing-*' | ||
WarningsAsErrors: '*,-google-readability-*,-google-explicit-constructor,-modernize-*,modernize-avoid-c-arrays,-performance-move-const-arg,-performance-noexcept-move-constructor,-cppcoreguidelines-init-variables,-cppcoreguidelines-pro-*,-cppcoreguidelines-owning-memory,-clang-analyzer-cplusplus.Move' | ||
WarningsAsErrors: '*,-google-readability-*,-google-explicit-constructor,-modernize-*,modernize-avoid-c-arrays,-performance-move-const-arg,-performance-noexcept-move-constructor,-performance-unnecessary-value-param,-cppcoreguidelines-init-variables,-cppcoreguidelines-pro-*,-cppcoreguidelines-owning-memory,-clang-analyzer-cplusplus.Move' | ||
HeaderFilterRegex: '' | ||
AnalyzeTemporaryDtors: false | ||
FormatStyle: '{ BasedOnStyle: Google, UseTab: Never, IndentWidth: 4, TabWidth: 4, AllowShortIfStatementsOnASingleLine: false, IndentCaseLabels: true, ColumnLimit: 80, AccessModifierOffset: -3, AlignConsecutiveMacros: true }' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,91 +1,209 @@ | ||
#include <stdbool.h> | ||
#include <stdio.h> | ||
|
||
/** | ||
* @file | ||
* @author [@Arctic2333](https://github.com/Arctic2333) | ||
* @author [Krishna Vedala](https://github.com/kvedala) | ||
* @brief Implementation of [Trie](https://en.wikipedia.org/wiki/Trie) data | ||
* structure for English alphabets in small characters. | ||
* @note the function ::data_structure::trie::deleteString might be erroneous | ||
* @see trie_modern.cpp | ||
*/ | ||
#include <array> | ||
#include <cassert> | ||
#include <iostream> | ||
#include <memory> | ||
#include <string> | ||
#include <vector> | ||
|
||
// structure definition | ||
typedef struct trie { | ||
struct trie* arr[26]; | ||
bool isEndofWord; | ||
} trie; | ||
|
||
// create a new node for trie | ||
trie* createNode() { | ||
trie* nn = new trie(); | ||
for (int i = 0; i < 26; i++) nn->arr[i] = NULL; | ||
nn->isEndofWord = false; | ||
return nn; | ||
} | ||
/** \namespace data_structure | ||
* \brief Data-structure algorithms | ||
*/ | ||
namespace data_structure { | ||
/** | ||
* @brief [Trie](https://en.wikipedia.org/wiki/Trie) implementation for | ||
* small-case English alphabets `a-z` | ||
*/ | ||
class trie { | ||
private: | ||
static constexpr uint8_t NUM_CHARS = 26; ///< Number of alphabets | ||
/** @brief Recursive tree nodes as an array of shared-pointers */ | ||
std::array<std::shared_ptr<trie>, NUM_CHARS << 1> arr; | ||
bool isEndofWord = false; ///< identifier if a node is terminal node | ||
|
||
// insert string into the trie | ||
void insert(trie* root, std::string str) { | ||
for (int i = 0; i < str.length(); i++) { | ||
int j = str[i] - 'a'; | ||
if (root->arr[j]) { | ||
root = root->arr[j]; | ||
} else { | ||
root->arr[j] = createNode(); | ||
root = root->arr[j]; | ||
/** | ||
* @brief Convert a character to integer for indexing | ||
* | ||
* @param ch character to index | ||
* @return unsigned integer index | ||
*/ | ||
uint8_t char_to_int(const char& ch) const { | ||
if (ch >= 'A' && ch <= 'Z') { | ||
return ch - 'A'; | ||
} else if (ch >= 'a' && ch <= 'z') { | ||
return ch - 'a' + NUM_CHARS; | ||
} | ||
|
||
std::cerr << "Invalid character present. Exiting..."; | ||
std::exit(EXIT_FAILURE); | ||
return 0; | ||
} | ||
root->isEndofWord = true; | ||
} | ||
|
||
// search a string exists inside the trie | ||
bool search(trie* root, std::string str, int index) { | ||
if (index == str.length()) { | ||
if (!root->isEndofWord) | ||
/** search a string exists inside a given root trie | ||
* @param str string to search for | ||
* @param index start index to search from | ||
* @returns `tre` if found | ||
* @returns `false` if not found | ||
*/ | ||
bool search(const std::shared_ptr<trie>& root, const std::string& str, | ||
int index) { | ||
if (index == str.length()) { | ||
if (!root->isEndofWord) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
int j = char_to_int(str[index]); | ||
if (!root->arr[j]) { | ||
return false; | ||
return true; | ||
} | ||
return search(root->arr[j], str, index + 1); | ||
} | ||
int j = str[index] - 'a'; | ||
if (!root->arr[j]) | ||
return false; | ||
return search(root->arr[j], str, index + 1); | ||
} | ||
|
||
/* | ||
removes the string if it is not a prefix of any other | ||
string, if it is then just sets the endofword to false, else | ||
removes the given string | ||
*/ | ||
bool deleteString(trie* root, std::string str, int index) { | ||
if (index == str.length()) { | ||
if (!root->isEndofWord) | ||
return false; | ||
root->isEndofWord = false; | ||
for (int i = 0; i < 26; i++) return false; | ||
return true; | ||
public: | ||
trie() = default; ///< Class default constructor | ||
|
||
/** insert string into the trie | ||
* @param str String to insert in the tree | ||
*/ | ||
void insert(const std::string& str) { | ||
std::shared_ptr<trie> root(nullptr); | ||
|
||
for (const char& ch : str) { | ||
int j = char_to_int(ch); | ||
if (root) { | ||
if (root->arr[j]) { | ||
root = root->arr[j]; | ||
} else { | ||
std::shared_ptr<trie> temp(new trie()); | ||
root->arr[j] = temp; | ||
root = temp; | ||
} | ||
} else if (arr[j]) { | ||
root = arr[j]; | ||
} else { | ||
std::shared_ptr<trie> temp(new trie()); | ||
arr[j] = temp; | ||
root = temp; | ||
} | ||
} | ||
root->isEndofWord = true; | ||
} | ||
int j = str[index] - 'a'; | ||
if (!root->arr[j]) | ||
return false; | ||
bool var = deleteString(root, str, index + 1); | ||
if (var) { | ||
root->arr[j] = NULL; | ||
if (root->isEndofWord) { | ||
|
||
/** search a string exists inside the trie | ||
* @param str string to search for | ||
* @param index start index to search from | ||
* @returns `true` if found | ||
* @returns `false` if not found | ||
*/ | ||
bool search(const std::string& str, int index) { | ||
if (index == str.length()) { | ||
if (!isEndofWord) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
int j = char_to_int(str[index]); | ||
if (!arr[j]) { | ||
return false; | ||
} else { | ||
int i; | ||
for (i = 0; i < 26; i++) | ||
if (root->arr[i]) | ||
return false; | ||
} | ||
return search(arr[j], str, index + 1); | ||
} | ||
|
||
/** | ||
* removes the string if it is not a prefix of any other | ||
* string, if it is then just sets the ::data_structure::trie::isEndofWord | ||
* to false, else removes the given string | ||
* @note the function ::data_structure::trie::deleteString might be | ||
* erroneous | ||
* @todo review the function ::data_structure::trie::deleteString and the | ||
* commented lines | ||
* @param str string to remove | ||
* @param index index to remove from | ||
* @returns `true` if successful | ||
* @returns `false` if unsuccessful | ||
*/ | ||
bool deleteString(const std::string& str, int index) { | ||
if (index == str.length()) { | ||
if (!isEndofWord) { | ||
return false; | ||
} | ||
isEndofWord = false; | ||
// following lines - possible source of error? | ||
// for (int i = 0; i < NUM_CHARS; i++) | ||
// if (!arr[i]) | ||
// return false; | ||
return true; | ||
} | ||
int j = char_to_int(str[index]); | ||
if (!arr[j]) { | ||
return false; | ||
} | ||
bool var = deleteString(str, index + 1); | ||
if (var) { | ||
arr[j].reset(); | ||
if (isEndofWord) { | ||
return false; | ||
} else { | ||
int i = 0; | ||
for (i = 0; i < NUM_CHARS; i++) { | ||
if (arr[i]) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
} | ||
|
||
/* should not return here */ | ||
std::cout << __func__ << ":" << __LINE__ | ||
<< "Should not reach this line\n"; | ||
return false; | ||
} | ||
}; | ||
} // namespace data_structure | ||
|
||
/** | ||
* @brief Testing function | ||
* @returns void | ||
*/ | ||
static void test() { | ||
data_structure::trie root; | ||
root.insert("Hello"); | ||
root.insert("World"); | ||
|
||
assert(!root.search("hello", 0)); | ||
std::cout << "hello - " << root.search("hello", 0) << "\n"; | ||
|
||
assert(root.search("Hello", 0)); | ||
std::cout << "Hello - " << root.search("Hello", 0) << "\n"; | ||
|
||
/* should not return here */ | ||
std::cout << __func__ << ":" << __LINE__ << "Should not reach this line\n"; | ||
return false; | ||
assert(!root.search("Word", 0)); | ||
std::cout << "Word - " << root.search("Word", 0) << "\n"; | ||
|
||
assert(root.search("World", 0)); | ||
std::cout << "World - " << root.search("World", 0) << "\n"; | ||
|
||
// Following lines of code give erroneous output | ||
// root.deleteString("hello", 0); | ||
// assert(!root.search("hello", 0)); | ||
// std::cout << "hello - " << root.search("world", 0) << "\n"; | ||
} | ||
|
||
/** | ||
* @brief Main function | ||
* @return 0 on exit | ||
*/ | ||
int main() { | ||
trie* root = createNode(); | ||
insert(root, "hello"); | ||
insert(root, "world"); | ||
int a = search(root, "hello", 0); | ||
int b = search(root, "word", 0); | ||
printf("%d %d ", a, b); | ||
test(); | ||
|
||
return 0; | ||
} |