diff --git a/CMakeLists.txt b/CMakeLists.txt index d7fa88559..bfaeccdbe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ add_subdirectory(math) add_subdirectory(others) add_subdirectory(search) add_subdirectory(ciphers) +add_subdirectory(hashing) add_subdirectory(strings) add_subdirectory(sorting) add_subdirectory(geometry) diff --git a/hashing/CMakeLists.txt b/hashing/CMakeLists.txt new file mode 100644 index 000000000..d8bad16c1 --- /dev/null +++ b/hashing/CMakeLists.txt @@ -0,0 +1,18 @@ +# If necessary, use the RELATIVE flag, otherwise each source file may be listed +# with full pathname. RELATIVE may makes it easier to extract an executable name +# automatically. +file( GLOB APP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp ) +# file( GLOB APP_SOURCES ${CMAKE_SOURCE_DIR}/*.c ) +# AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} APP_SOURCES) +foreach( testsourcefile ${APP_SOURCES} ) + # I used a simple string replace, to cut off .cpp. + string( REPLACE ".cpp" "" testname ${testsourcefile} ) + add_executable( ${testname} ${testsourcefile} ) + + set_target_properties(${testname} PROPERTIES LINKER_LANGUAGE CXX) + if(OpenMP_CXX_FOUND) + target_link_libraries(${testname} OpenMP::OpenMP_CXX) + endif() + install(TARGETS ${testname} DESTINATION "bin/hash") + +endforeach( testsourcefile ${APP_SOURCES} ) diff --git a/hashing/chaining.cpp b/hashing/chaining.cpp index 6afd2d13b..ae902c90e 100644 --- a/hashing/chaining.cpp +++ b/hashing/chaining.cpp @@ -1,110 +1,179 @@ -#include +/** + * @file chaining.cpp + * @author [vasutomar](https://github.com/vasutomar) + * @author [Krishna Vedala](https://github.com/kvedala) + * @brief Implementation of [hash + * chains](https://en.wikipedia.org/wiki/Hash_chain). + */ +#include #include -using namespace std; +#include +#include -struct Node { - int data; - struct Node *next; -} * head[100], *curr; +/** + * @brief Chain class with a given modulus + */ +class hash_chain { + private: + /** + * @brief Define a linked node + */ + using Node = struct Node { + int data{}; ///< data stored in the node + std::shared_ptr next; ///< pointer to the next node + }; -void init() { - for (int i = 0; i < 100; i++) head[i] = NULL; -} + std::vector> head; ///< array of nodes + int _mod; ///< modulus of the class -void add(int x, int h) { - struct Node *temp = new Node; - temp->data = x; - temp->next = NULL; - if (!head[h]) { - head[h] = temp; - curr = head[h]; - } else { - curr = head[h]; - while (curr->next) curr = curr->next; - curr->next = temp; + public: + /** + * @brief Construct a new chain object + * + * @param mod modulus of the chain + */ + explicit hash_chain(int mod) : _mod(mod) { + while (mod--) head.push_back(nullptr); } -} -void display(int mod) { - struct Node *temp; - int i; - for (i = 0; i < mod; i++) { - if (!head[i]) { - cout << "Key " << i << " is empty" << endl; + /** + * @brief create and add a new node with a give value and at a given height + * + * @param x value at the new node + * @param h height of the node + */ + void add(int x, int h) { + std::shared_ptr curr; + std::shared_ptr temp(new Node); + temp->data = x; + temp->next = nullptr; + if (!head[h]) { + head[h] = temp; + curr = head[h]; } else { - cout << "Key " << i << " has values = "; - temp = head[i]; - while (temp->next) { - cout << temp->data << " "; - temp = temp->next; - } - cout << temp->data; - cout << endl; + curr = head[h]; + while (curr->next) curr = curr->next; + curr->next = temp; } } -} -int hash(int x, int mod) { return x % mod; } - -void find(int x, int h) { - struct Node *temp = head[h]; - if (!head[h]) { - cout << "Element not found"; - return; + /** + * @brief Display the chain + */ + void display() { + std::shared_ptr temp = nullptr; + int i = 0; + for (i = 0; i < _mod; i++) { + if (!head[i]) { + std::cout << "Key " << i << " is empty" << std::endl; + } else { + std::cout << "Key " << i << " has values = "; + temp = head[i]; + while (temp->next) { + std::cout << temp->data << " "; + temp = temp->next; + } + std::cout << temp->data; + std::cout << std::endl; + } + } } - while (temp->data != x && temp->next) temp = temp->next; - if (temp->next) - cout << "Element found"; - else { - if (temp->data == x) - cout << "Element found"; - else - cout << "Element not found"; - } -} -int main(void) { - init(); - int c, x, mod, h; - cout << "Enter the size of Hash Table. = "; - cin >> mod; + /** + * @brief Compute the hash of a value for current chain + * + * @param x value to compute modulus of + * @return modulus of `x` + * @note declared as a + * [`virtual`](https://en.cppreference.com/w/cpp/language/virtual) so that + * custom implementations of the class can modify the hash function. + */ + virtual int hash(int x) const { return x % _mod; } + + /** + * @brief Find if a value and corresponding hash exist + * + * @param x value to search for + * @param h corresponding hash key + * @returns `true` if element found + * @returns `false` if element not found + */ + bool find(int x, int h) const { + std::shared_ptr temp = head[h]; + if (!head[h]) { + // index does not exist! + std::cout << "Element not found"; + return false; + } + + // scan for data value + while (temp->data != x && temp->next) temp = temp->next; + + if (temp->next) { + std::cout << "Element found"; + return true; + } + + // implicit else condition + // i.e., temp->next == nullptr + if (temp->data == x) { + std::cout << "Element found"; + return true; + } + + // further implicit else condition + std::cout << "Element not found"; + return false; + } +}; + +/** Main function + * @returns `0` always + */ +int main() { + int c = 0, x = 0, mod = 0, h = 0; + std::cout << "Enter the size of Hash Table. = "; + std::cin >> mod; + + hash_chain mychain(mod); + bool loop = true; while (loop) { - cout << endl; - cout << "PLEASE CHOOSE -" << endl; - cout << "1. Add element." << endl; - cout << "2. Find element." << endl; - cout << "3. Generate Hash." << endl; - cout << "4. Display Hash table." << endl; - cout << "5. Exit." << endl; - cin >> c; + std::cout << std::endl; + std::cout << "PLEASE CHOOSE -" << std::endl; + std::cout << "1. Add element." << std::endl; + std::cout << "2. Find element." << std::endl; + std::cout << "3. Generate Hash." << std::endl; + std::cout << "4. Display Hash table." << std::endl; + std::cout << "5. Exit." << std::endl; + std::cin >> c; switch (c) { - case 1: - cout << "Enter element to add = "; - cin >> x; - h = hash(x, mod); - h = fabs(h); - add(x, h); - break; - case 2: - cout << "Enter element to search = "; - cin >> x; - h = hash(x, mod); - find(x, h); - break; - case 3: - cout << "Enter element to generate hash = "; - cin >> x; - cout << "Hash of " << x << " is = " << hash(x, mod); - break; - case 4: - display(mod); - break; - default: - loop = false; - break; + case 1: + std::cout << "Enter element to add = "; + std::cin >> x; + h = mychain.hash(x); + h = std::abs(h); + mychain.add(x, h); + break; + case 2: + std::cout << "Enter element to search = "; + std::cin >> x; + h = mychain.hash(x); + mychain.find(x, h); + break; + case 3: + std::cout << "Enter element to generate hash = "; + std::cin >> x; + std::cout << "Hash of " << x << " is = " << mychain.hash(x); + break; + case 4: + mychain.display(); + break; + default: + loop = false; + break; } - cout << endl; + std::cout << std::endl; } /*add(1,&head1); add(2,&head1); @@ -113,4 +182,4 @@ int main(void) { display(&head1); display(&head2);*/ return 0; -} \ No newline at end of file +} diff --git a/hashing/double_hash_hash_table.cpp b/hashing/double_hash_hash_table.cpp index 7ee2757de..f5d803ba2 100644 --- a/hashing/double_hash_hash_table.cpp +++ b/hashing/double_hash_hash_table.cpp @@ -1,128 +1,170 @@ -// Copyright 2019 - -#include -#include +/** + * @file double_hash_hash_table.cpp + * @author [achance6](https://github.com/achance6) + * @author [Krishna Vedala](https://github.com/kvedala) + * @brief Storage mechanism using [double-hashed + * keys](https://en.wikipedia.org/wiki/Double_hashing). + * @note The implementation can be optimized by using OOP style. + */ #include -#include - -using std::cin; -using std::cout; -using std::endl; -using std::string; +#include +#include +/** + * @addtogroup open_addressing Open Addressing + * @{ + * @namespace double_hashing + * @brief An implementation of hash table using [double + * hashing](https://en.wikipedia.org/wiki/Double_hashing) algorithm. + */ +namespace double_hashing { // fwd declarations -struct Entry; -bool putProber(Entry entry, int key); -bool searchingProber(Entry entry, int key); +using Entry = struct Entry; +bool putProber(const Entry& entry, int key); +bool searchingProber(const Entry& entry, int key); void add(int key); -// globals +// Undocumented globals int notPresent; -struct Entry* table; +std::vector table; int totalSize; int tomb = -1; int size; bool rehashing; -// Node that holds key +/** Node object that holds key */ struct Entry { - explicit Entry(int key = notPresent) : key(key) {} - int key; + explicit Entry(int key = notPresent) : key(key) {} ///< constructor + int key; ///< key value }; -// Hash a key -int hashFxn(int key) { +/** + * @brief Hash a key. Uses the STL library's `std::hash()` function. + * + * @param key value to hash + * @return hash value of the key + */ +size_t hashFxn(int key) { std::hash hash; return hash(key); } -// Used for second hash function -int otherHashFxn(int key) { +/** + * @brief Used for second hash function + * + * @param key key value to hash + * @return hash value of the key + */ +size_t otherHashFxn(int key) { std::hash hash; return 1 + (7 - (hash(key) % 7)); } -// Performs double hashing to resolve collisions +/** + * @brief Performs double hashing to resolve collisions + * + * @param key key value to apply double-hash on + * @param searching `true` to check for conflicts + * @return Index of key when found + * @return new hash if no conflicts present + */ int doubleHash(int key, bool searching) { - int hash = static_cast(fabs(hashFxn(key))); + int hash = static_cast(hashFxn(key)); int i = 0; Entry entry; do { - int index = static_cast(fabs((hash + (i * otherHashFxn(key))))) % - totalSize; + int index = + static_cast(hash + (i * otherHashFxn(key))) % totalSize; entry = table[index]; if (searching) { if (entry.key == notPresent) { return notPresent; } if (searchingProber(entry, key)) { - cout << "Found key!" << endl; + std::cout << "Found key!" << std::endl; return index; } - cout << "Found tombstone or equal hash, checking next" << endl; + std::cout << "Found tombstone or equal hash, checking next" + << std::endl; i++; } else { if (putProber(entry, key)) { - if (!rehashing) - cout << "Spot found!" << endl; + if (!rehashing) { + std::cout << "Spot found!" << std::endl; + } return index; } - if (!rehashing) - cout << "Spot taken, looking at next (next index:" - << " " - << static_cast( - fabs((hash + (i * otherHashFxn(key))))) % - totalSize - << ")" << endl; + if (!rehashing) { + std::cout << "Spot taken, looking at next (next index:" + << " " + << static_cast(hash + (i * otherHashFxn(key))) % + totalSize + << ")" << std::endl; + } i++; } if (i == totalSize * 100) { - cout << "DoubleHash probe failed" << endl; + std::cout << "DoubleHash probe failed" << std::endl; return notPresent; } } while (entry.key != notPresent); return notPresent; } -// Finds empty spot -bool putProber(Entry entry, int key) { +/** Finds empty spot in a vector + * @param entry vector to search in + * @param key key to search for + * @returns `true` if key is not present or is a `toumb` + * @returns `false` is already occupied + */ +bool putProber(const Entry& entry, int key) { if (entry.key == notPresent || entry.key == tomb) { return true; } return false; } -// Looks for a matching key -bool searchingProber(Entry entry, int key) { - if (entry.key == key) +/** Looks for a matching key + * @param entry vector to search in + * @param key key value to search + * @returns `true` if found + * @returns `false` if not found + */ +bool searchingProber(const Entry& entry, int key) { + if (entry.key == key) { return true; + } return false; } -// Displays the table +/** Displays the table + * @returns None + */ void display() { for (int i = 0; i < totalSize; i++) { if (table[i].key == notPresent) { - cout << " Empty "; + std::cout << " Empty "; } else if (table[i].key == tomb) { - cout << " Tomb "; + std::cout << " Tomb "; } else { - cout << " "; - cout << table[i].key; - cout << " "; + std::cout << " "; + std::cout << table[i].key; + std::cout << " "; } } - cout << endl; + std::cout << std::endl; } -// Rehashes the table into a bigger table +/** Rehashes the table into a bigger table + * @returns None + */ void rehash() { // Necessary so wall of add info isn't printed all at once rehashing = true; int oldSize = totalSize; - Entry* oldTable = table; + std::vector oldTable(table); // Really this should use the next prime number greater than totalSize * 2 - table = new Entry[totalSize * 2]; + table = std::vector(totalSize * 2); totalSize *= 2; for (int i = 0; i < oldSize; i++) { if (oldTable[i].key != -1 && oldTable[i].key != notPresent) { @@ -130,112 +172,132 @@ void rehash() { add(oldTable[i].key); } } - delete[] oldTable; + // delete[] oldTable; + // oldTable.reset(); + rehashing = false; - cout << "Table was rehashed, new size is: " << totalSize << endl; + std::cout << "Table was rehashed, new size is: " << totalSize << std::endl; } -// Checks for load factor here +/** Checks for load factor here + * @param key key value to add to the table + */ void add(int key) { - Entry* entry = new Entry(); - entry->key = key; + // auto* entry = new Entry(); + // entry->key = key; int index = doubleHash(key, false); - table[index] = *entry; + table[index].key = key; // Load factor greater than 0.5 causes resizing if (++size / static_cast(totalSize) >= 0.5) { rehash(); } } -// Removes key. Leaves tombstone upon removal. +/** Removes key. Leaves tombstone upon removal. + * @param key key value to remove + */ void remove(int key) { int index = doubleHash(key, true); if (index == notPresent) { - cout << "key not found" << endl; + std::cout << "key not found" << std::endl; } table[index].key = tomb; - cout << "Removal successful, leaving tombstone" << endl; + std::cout << "Removal successful, leaving tombstone" << std::endl; size--; } -// Information about the adding process +/** Information about the adding process + * @param key key value to add to table + */ void addInfo(int key) { - cout << "Initial table: "; + std::cout << "Initial table: "; display(); - cout << endl; - cout << "hash of " << key << " is " << hashFxn(key) << " % " << totalSize - << " == " << fabs(hashFxn(key) % totalSize); - cout << endl; + std::cout << std::endl; + std::cout << "hash of " << key << " is " << hashFxn(key) << " % " + << totalSize << " == " << hashFxn(key) % totalSize; + std::cout << std::endl; add(key); - cout << "New table: "; + std::cout << "New table: "; display(); } -// Information about removal process +/** Information about removal process + * @param key key value to remove from table + */ void removalInfo(int key) { - cout << "Initial table: "; + std::cout << "Initial table: "; display(); - cout << endl; - cout << "hash of " << key << " is " << hashFxn(key) << " % " << totalSize - << " == " << hashFxn(key) % totalSize; - cout << endl; + std::cout << std::endl; + std::cout << "hash of " << key << " is " << hashFxn(key) << " % " + << totalSize << " == " << hashFxn(key) % totalSize; + std::cout << std::endl; remove(key); - cout << "New table: "; + std::cout << "New table: "; display(); } +} // namespace double_hashing +/** + * @} + */ -// I/O -int main(void) { - int cmd, hash, key; - cout << "Enter the initial size of Hash Table. = "; - cin >> totalSize; - table = new Entry[totalSize]; +using double_hashing::Entry; +using double_hashing::table; +using double_hashing::totalSize; + +/** Main program + * @returns 0 on success + */ +int main() { + int cmd = 0, hash = 0, key = 0; + std::cout << "Enter the initial size of Hash Table. = "; + std::cin >> totalSize; + table = std::vector(totalSize); bool loop = true; while (loop) { - system("pause"); - cout << endl; - cout << "PLEASE CHOOSE -" << endl; - cout << "1. Add key. (Numeric only)" << endl; - cout << "2. Remove key." << endl; - cout << "3. Find key." << endl; - cout << "4. Generate Hash. (Numeric only)" << endl; - cout << "5. Display Hash table." << endl; - cout << "6. Exit." << endl; - cin >> cmd; + std::cout << std::endl; + std::cout << "PLEASE CHOOSE -" << std::endl; + std::cout << "1. Add key. (Numeric only)" << std::endl; + std::cout << "2. Remove key." << std::endl; + std::cout << "3. Find key." << std::endl; + std::cout << "4. Generate Hash. (Numeric only)" << std::endl; + std::cout << "5. Display Hash table." << std::endl; + std::cout << "6. Exit." << std::endl; + std::cin >> cmd; switch (cmd) { - case 1: - cout << "Enter key to add = "; - cin >> key; - addInfo(key); - break; - case 2: - cout << "Enter key to remove = "; - cin >> key; - removalInfo(key); - break; - case 3: { - cout << "Enter key to search = "; - cin >> key; - Entry entry = table[doubleHash(key, true)]; - if (entry.key == notPresent) { - cout << "Key not present"; + case 1: + std::cout << "Enter key to add = "; + std::cin >> key; + double_hashing::addInfo(key); + break; + case 2: + std::cout << "Enter key to remove = "; + std::cin >> key; + double_hashing::removalInfo(key); + break; + case 3: { + std::cout << "Enter key to search = "; + std::cin >> key; + Entry entry = table[double_hashing::doubleHash(key, true)]; + if (entry.key == double_hashing::notPresent) { + std::cout << "Key not present"; + } + break; } - break; + case 4: + std::cout << "Enter element to generate hash = "; + std::cin >> key; + std::cout << "Hash of " << key + << " is = " << double_hashing::hashFxn(key); + break; + case 5: + double_hashing::display(); + break; + default: + loop = false; + break; + // delete[] table; } - case 4: - cout << "Enter element to generate hash = "; - cin >> key; - cout << "Hash of " << key << " is = " << fabs(hashFxn(key)); - break; - case 5: - display(); - break; - default: - loop = false; - break; - delete[] table; - } - cout << endl; + std::cout << std::endl; } return 0; } diff --git a/hashing/linear_probing_hash_table.cpp b/hashing/linear_probing_hash_table.cpp index 393504c1d..d87cb9cf7 100644 --- a/hashing/linear_probing_hash_table.cpp +++ b/hashing/linear_probing_hash_table.cpp @@ -1,229 +1,277 @@ -// Copyright 2019 - -#include -#include +/** + * @file + * @author [achance6](https://github.com/achance6) + * @author [Krishna Vedala](https://github.com/kvedala) + * @brief Storage mechanism using [linear probing + * hash](https://en.wikipedia.org/wiki/Linear_probing) keys. + * @note The implementation can be optimized by using OOP style. + */ #include -#include - -using std::cin; -using std::cout; -using std::endl; -using std::string; +#include +/** + * @addtogroup open_addressing Open Addressing + * @{ + * @namespace linear_probing + * @brief An implementation of hash table using [linear + * probing](https://en.wikipedia.org/wiki/Linear_probing) algorithm. + */ +namespace linear_probing { // fwd declarations -struct Entry; -bool putProber(Entry entry, int key); -bool searchingProber(Entry entry, int key); +using Entry = struct Entry; +bool putProber(const Entry& entry, int key); +bool searchingProber(const Entry& entry, int key); void add(int key); -// globals +// Undocumented globals int notPresent; -struct Entry* table; +std::vector table; int totalSize; int tomb = -1; int size; bool rehashing; -// Node that holds key +/** Node object that holds key */ struct Entry { - explicit Entry(int key = notPresent) : key(key) {} - int key; + explicit Entry(int key = notPresent) : key(key) {} ///< constructor + int key; ///< key value }; -// Hash a key -int hashFxn(int key) { +/** + * @brief Hash a key. Uses the STL library's `std::hash()` function. + * + * @param key value to hash + * @return hash value of the key + */ +size_t hashFxn(int key) { std::hash hash; return hash(key); } -// Performs linear probing to resolve collisions +/** Performs linear probing to resolve collisions + * @param key key value to hash + * @return hash value of the key + */ int linearProbe(int key, bool searching) { - int hash = static_cast(fabs(hashFxn(key))); + int hash = static_cast(hashFxn(key)); int i = 0; Entry entry; do { - int index = static_cast(fabs((hash + i) % totalSize)); + int index = static_cast((hash + i) % totalSize); entry = table[index]; if (searching) { if (entry.key == notPresent) { return notPresent; } if (searchingProber(entry, key)) { - cout << "Found key!" << endl; + std::cout << "Found key!" << std::endl; return index; } - cout << "Found tombstone or equal hash, checking next" << endl; + std::cout << "Found tombstone or equal hash, checking next" + << std::endl; i++; } else { if (putProber(entry, key)) { - if (!rehashing) - cout << "Spot found!" << endl; + if (!rehashing) { + std::cout << "Spot found!" << std::endl; + } return index; } - if (!rehashing) - cout << "Spot taken, looking at next" << endl; + if (!rehashing) { + std::cout << "Spot taken, looking at next" << std::endl; + } i++; } if (i == totalSize) { - cout << "Linear probe failed" << endl; + std::cout << "Linear probe failed" << std::endl; return notPresent; } } while (entry.key != notPresent); return notPresent; } -// Finds empty spot -bool putProber(Entry entry, int key) { +/** Finds empty spot + * @param entry instance to check in + * @param key key value to hash + * @return hash value of the key + */ +bool putProber(const Entry& entry, int key) { if (entry.key == notPresent || entry.key == tomb) { return true; } return false; } -// Looks for a matching key -bool searchingProber(Entry entry, int key) { - if (entry.key == key) +/** Looks for a matching key + * @param entry instance to check in + * @param key key value to hash + * @return hash value of the key + */ +bool searchingProber(const Entry& entry, int key) { + if (entry.key == key) { return true; + } return false; } -// Displays the table +/** Function to displays the table + * @returns none + */ void display() { for (int i = 0; i < totalSize; i++) { if (table[i].key == notPresent) { - cout << " Empty "; + std::cout << " Empty "; } else if (table[i].key == tomb) { - cout << " Tomb "; + std::cout << " Tomb "; } else { - cout << " "; - cout << table[i].key; - cout << " "; + std::cout << " "; + std::cout << table[i].key; + std::cout << " "; } } - cout << endl; + std::cout << std::endl; } -// Rehashes the table into a bigger table +/** Rehashes the table into a bigger table + * @returns None + */ void rehash() { // Necessary so wall of add info isn't printed all at once rehashing = true; int oldSize = totalSize; - Entry* oldTable = table; - // Really this should use the next prime number greater than totalSize * 2 - table = new Entry[totalSize * 2]; + std::vector oldTable(table); + // Really this should use the next prime number greater than totalSize * + // 2 totalSize *= 2; + table = std::vector(totalSize); for (int i = 0; i < oldSize; i++) { if (oldTable[i].key != -1 && oldTable[i].key != notPresent) { size--; // Size stays the same (add increments size) add(oldTable[i].key); } } - delete[] oldTable; + // delete[] oldTable; rehashing = false; - cout << "Table was rehashed, new size is: " << totalSize << endl; + std::cout << "Table was rehashed, new size is: " << totalSize << std::endl; } -// Adds entry using linear probing. Checks for load factor here +/** Adds entry using linear probing. Checks for load factor here + * @param key key value to hash and add + */ void add(int key) { - Entry* entry = new Entry(); - entry->key = key; int index = linearProbe(key, false); - table[index] = *entry; + table[index].key = key; // Load factor greater than 0.5 causes resizing if (++size / static_cast(totalSize) >= 0.5) { rehash(); } } -// Removes key. Leaves tombstone upon removal. +/** Removes key. Leaves tombstone upon removal. + * @param key key value to hash and remove + */ void remove(int key) { int index = linearProbe(key, true); if (index == notPresent) { - cout << "key not found" << endl; + std::cout << "key not found" << std::endl; } - cout << "Removal Successful, leaving tomb" << endl; + std::cout << "Removal Successful, leaving tomb" << std::endl; table[index].key = tomb; size--; } -// Information about the adding process +/** Information about the adding process + * @param key key value to hash and add + */ void addInfo(int key) { - cout << "Initial table: "; + std::cout << "Initial table: "; display(); - cout << endl; - cout << "hash of " << key << " is " << hashFxn(key) << " % " << totalSize - << " == " << fabs(hashFxn(key) % totalSize); - cout << endl; + std::cout << std::endl; + std::cout << "hash of " << key << " is " << hashFxn(key) << " % " + << totalSize << " == " << hashFxn(key) % totalSize; + std::cout << std::endl; add(key); - cout << "New table: "; + std::cout << "New table: "; display(); } -// Information about removal process +/** Information about removal process + * @param key key value to hash and remove + */ void removalInfo(int key) { - cout << "Initial table: "; + std::cout << "Initial table: "; display(); - cout << endl; - cout << "hash of " << key << " is " << hashFxn(key) << " % " << totalSize - << " == " << hashFxn(key) % totalSize; - cout << endl; + std::cout << std::endl; + std::cout << "hash of " << key << " is " << hashFxn(key) << " % " + << totalSize << " == " << hashFxn(key) % totalSize; + std::cout << std::endl; remove(key); - cout << "New table: "; + std::cout << "New table: "; display(); } +} // namespace linear_probing +/** + * @} + */ -// I/O -int main(void) { - int cmd, hash, key; - cout << "Enter the initial size of Hash Table. = "; - cin >> totalSize; - table = new Entry[totalSize]; +using linear_probing::Entry; +using linear_probing::table; +using linear_probing::totalSize; + +/** Main function + * @returns 0 on success + */ +int main() { + int cmd = 0, hash = 0, key = 0; + std::cout << "Enter the initial size of Hash Table. = "; + std::cin >> totalSize; + table = std::vector(totalSize); bool loop = true; while (loop) { - system("pause"); - cout << endl; - cout << "PLEASE CHOOSE -" << endl; - cout << "1. Add key. (Numeric only)" << endl; - cout << "2. Remove key." << endl; - cout << "3. Find key." << endl; - cout << "4. Generate Hash. (Numeric only)" << endl; - cout << "5. Display Hash table." << endl; - cout << "6. Exit." << endl; - cin >> cmd; + std::cout << std::endl; + std::cout << "PLEASE CHOOSE -" << std::endl; + std::cout << "1. Add key. (Numeric only)" << std::endl; + std::cout << "2. Remove key." << std::endl; + std::cout << "3. Find key." << std::endl; + std::cout << "4. Generate Hash. (Numeric only)" << std::endl; + std::cout << "5. Display Hash table." << std::endl; + std::cout << "6. Exit." << std::endl; + std::cin >> cmd; switch (cmd) { - case 1: - cout << "Enter key to add = "; - cin >> key; - addInfo(key); - break; - case 2: - cout << "Enter key to remove = "; - cin >> key; - removalInfo(key); - break; - case 3: { - cout << "Enter key to search = "; - cin >> key; - Entry entry = table[linearProbe(key, true)]; - if (entry.key == notPresent) { - cout << "Key not present"; + case 1: + std::cout << "Enter key to add = "; + std::cin >> key; + linear_probing::addInfo(key); + break; + case 2: + std::cout << "Enter key to remove = "; + std::cin >> key; + linear_probing::removalInfo(key); + break; + case 3: { + std::cout << "Enter key to search = "; + std::cin >> key; + Entry entry = table[linear_probing::linearProbe(key, true)]; + if (entry.key == linear_probing::notPresent) { + std::cout << "Key not present"; + } + break; } - break; + case 4: + std::cout << "Enter element to generate hash = "; + std::cin >> key; + std::cout << "Hash of " << key + << " is = " << linear_probing::hashFxn(key); + break; + case 5: + linear_probing::display(); + break; + default: + loop = false; + break; + // delete[] table; } - case 4: - cout << "Enter element to generate hash = "; - cin >> key; - cout << "Hash of " << key << " is = " << fabs(hashFxn(key)); - break; - case 5: - display(); - break; - default: - loop = false; - break; - delete[] table; - } - cout << endl; + std::cout << std::endl; } return 0; } diff --git a/hashing/quadratic_probing_hash_table.cpp b/hashing/quadratic_probing_hash_table.cpp index 971c2182d..258c009d0 100644 --- a/hashing/quadratic_probing_hash_table.cpp +++ b/hashing/quadratic_probing_hash_table.cpp @@ -1,244 +1,301 @@ -// Copyright 2019 - -#include +/** + * @file + * @author [achance6](https://github.com/achance6) + * @author [Krishna Vedala](https://github.com/kvedala) + * @brief Storage mechanism using [quadratic probing + * hash](https://en.wikipedia.org/wiki/Quadratic_probing) keys. + * @note The implementation can be optimized by using OOP style. + */ #include -#include #include -#include - -using std::cin; -using std::cout; -using std::endl; -using std::string; +#include +/** + * @addtogroup open_addressing Open Addressing + * @{ + * @namespace quadratic_probing + * @brief An implementation of hash table using [quadratic + * probing](https://en.wikipedia.org/wiki/Quadratic_probing) algorithm. + */ +namespace quadratic_probing { // fwd declarations -struct Entry; -bool putProber(Entry entry, int key); -bool searchingProber(Entry entry, int key); +using Entry = struct Entry; +bool putProber(const Entry& entry, int key); +bool searchingProber(const Entry& entry, int key); void add(int key); // globals int notPresent; -struct Entry* table; +std::vector table; int totalSize; int tomb = -1; int size; bool rehashing; -// Node that holds key +/** Node that holds key + */ struct Entry { - explicit Entry(int key = notPresent) : key(key) {} - int key; + explicit Entry(int key = notPresent) : key(key) {} ///< constructor + int key; ///< key value }; -// Hash a key -int hashFxn(int key) { +/** Hash a key + * @param key key value to hash + * @returns hash of the key + */ +size_t hashFxn(int key) { std::hash hash; return hash(key); } -// Performs quadratic probing to resolve collisions +/** Performs quadratic probing to resolve collisions + * @param key key value to search/probe + * @param searching `true` if only searching, `false1 if assigning + * @returns value of `notPresent`. + */ int quadraticProbe(int key, bool searching) { - int hash = static_cast(fabs(hashFxn(key))); + int hash = static_cast(hashFxn(key)); int i = 0; Entry entry; do { - int index = std::round(fabs( - (hash + static_cast(std::round(std::pow(i, 2)))) % totalSize)); + size_t index = + (hash + static_cast(std::round(std::pow(i, 2)))) % + totalSize; entry = table[index]; if (searching) { if (entry.key == notPresent) { return notPresent; } if (searchingProber(entry, key)) { - cout << "Found key!" << endl; + std::cout << "Found key!" << std::endl; return index; } - cout << "Found tombstone or equal hash, checking next" << endl; + std::cout << "Found tombstone or equal hash, checking next" + << std::endl; i++; } else { if (putProber(entry, key)) { - if (!rehashing) - cout << "Spot found!" << endl; + if (!rehashing) { + std::cout << "Spot found!" << std::endl; + } return index; } if (!rehashing) { - cout << "Spot taken, looking at next (next index = " - << std::round(fabs((hash + static_cast(std::round( - std::pow(i + 1, 2)))) % - totalSize)) - << endl; + std::cout << "Spot taken, looking at next (next index = " + << (hash + static_cast( + std::round(std::pow(i + 1, 2)))) % + totalSize + << std::endl; } i++; } if (i == totalSize * 100) { - cout << "Quadratic probe failed (infinite loop)" << endl; + std::cout << "Quadratic probe failed (infinite loop)" << std::endl; return notPresent; } } while (entry.key != notPresent); return notPresent; } -// Finds empty spot -bool putProber(Entry entry, int key) { +/** Finds empty spot + * @param entry Instance of table entry + * @param key key value to search/probe + * @returns `true` if key is present + * @returns `false` if key is absent + */ +bool putProber(const Entry& entry, int key) { if (entry.key == notPresent || entry.key == tomb) { return true; } return false; } -// Looks for a matching key -bool searchingProber(Entry entry, int key) { - if (entry.key == key) +/** Looks for a matching key + * @param entry Instance of table entry + * @param key key value to search/probe + * @returns `true` if key matches the entry + * @returns `false` if key does not match the entry + */ +bool searchingProber(const Entry& entry, int key) { + if (entry.key == key) { return true; + } return false; } -// Helper +/** Get the entry instance corresponding to a key + * @param key key value to search/probe + * @returns if present, the entry instance + * @returns if not present, a new instance + */ Entry find(int key) { int index = quadraticProbe(key, true); - if (index == notPresent) + if (index == notPresent) { return Entry(); + } return table[index]; } -// Displays the table +/** Displays the table + * @returns None + */ void display() { for (int i = 0; i < totalSize; i++) { if (table[i].key == notPresent) { - cout << " Empty "; + std::cout << " Empty "; } else if (table[i].key == tomb) { - cout << " Tomb "; + std::cout << " Tomb "; } else { - cout << " "; - cout << table[i].key; - cout << " "; + std::cout << " "; + std::cout << table[i].key; + std::cout << " "; } } - cout << endl; + std::cout << std::endl; } -// Rehashes the table into a bigger table +/** Rehashes the table into a bigger table + * @returns none + */ void rehash() { // Necessary so wall of add info isn't printed all at once rehashing = true; int oldSize = totalSize; - Entry* oldTable = table; + std::vector oldTable(table); // Really this should use the next prime number greater than totalSize * 2 - table = new Entry[totalSize * 2]; totalSize *= 2; + table = std::vector(totalSize); for (int i = 0; i < oldSize; i++) { if (oldTable[i].key != -1 && oldTable[i].key != notPresent) { size--; // Size stays the same (add increments size) add(oldTable[i].key); } } - delete[] oldTable; + // delete[] oldTable; rehashing = false; - cout << "Table was rehashed, new size is: " << totalSize << endl; + std::cout << "Table was rehashed, new size is: " << totalSize << std::endl; } -// Checks for load factor here +/** Checks for load factor here + * @param key key value to hash and add to table + */ void add(int key) { - Entry* entry = new Entry(); - entry->key = key; int index = quadraticProbe(key, false); - table[index] = *entry; + table[index].key = key; // Load factor greater than 0.5 causes resizing if (++size / static_cast(totalSize) >= 0.5) { rehash(); } } -// Removes key. Leaves tombstone upon removal. +/** Removes key. Leaves tombstone upon removal. + * @param key key value to hash and remove from table + */ void remove(int key) { int index = quadraticProbe(key, true); if (index == notPresent) { - cout << "key not found" << endl; + std::cout << "key not found" << std::endl; } table[index].key = tomb; - cout << "Removal successful, leaving tombstone" << endl; + std::cout << "Removal successful, leaving tombstone" << std::endl; size--; } -// Information about the adding process +/** Information about the adding process + * @param key key value to hash and add to table + */ void addInfo(int key) { - cout << "Initial table: "; + std::cout << "Initial table: "; display(); - cout << endl; - cout << "hash of " << key << " is " << hashFxn(key) << " % " << totalSize - << " == " << fabs(hashFxn(key) % totalSize); - cout << endl; + std::cout << std::endl; + std::cout << "hash of " << key << " is " << hashFxn(key) << " % " + << totalSize << " == " << hashFxn(key) % totalSize; + std::cout << std::endl; add(key); - cout << "New table: "; + std::cout << "New table: "; display(); } -// Information about removal process +/** Information about removal process + * @param key key value to hash and remove from table + */ void removalInfo(int key) { - cout << "Initial table: "; + std::cout << "Initial table: "; display(); - cout << endl; - cout << "hash of " << key << " is " << hashFxn(key) << " % " << totalSize - << " == " << hashFxn(key) % totalSize; - cout << endl; + std::cout << std::endl; + std::cout << "hash of " << key << " is " << hashFxn(key) << " % " + << totalSize << " == " << hashFxn(key) % totalSize; + std::cout << std::endl; remove(key); - cout << "New table: "; + std::cout << "New table: "; display(); } -// I/O -int main(void) { - int cmd, hash, key; - cout << "Enter the initial size of Hash Table. = "; - cin >> totalSize; - table = new Entry[totalSize]; +} // namespace quadratic_probing +/** + * @} + */ + +using quadratic_probing::Entry; +using quadratic_probing::table; +using quadratic_probing::totalSize; + +/** Main function + * @returns None + */ +int main() { + int cmd = 0, hash = 0, key = 0; + std::cout << "Enter the initial size of Hash Table. = "; + std::cin >> totalSize; + table = std::vector(totalSize); bool loop = true; while (loop) { - system("pause"); - cout << endl; - cout << "PLEASE CHOOSE -" << endl; - cout << "1. Add key. (Numeric only)" << endl; - cout << "2. Remove key." << endl; - cout << "3. Find key." << endl; - cout << "4. Generate Hash. (Numeric only)" << endl; - cout << "5. Display Hash table." << endl; - cout << "6. Exit." << endl; - cin >> cmd; + std::cout << std::endl; + std::cout << "PLEASE CHOOSE -" << std::endl; + std::cout << "1. Add key. (Numeric only)" << std::endl; + std::cout << "2. Remove key." << std::endl; + std::cout << "3. Find key." << std::endl; + std::cout << "4. Generate Hash. (Numeric only)" << std::endl; + std::cout << "5. Display Hash table." << std::endl; + std::cout << "6. Exit." << std::endl; + std::cin >> cmd; switch (cmd) { - case 1: - cout << "Enter key to add = "; - cin >> key; - addInfo(key); - break; - case 2: - cout << "Enter key to remove = "; - cin >> key; - removalInfo(key); - break; - case 3: { - cout << "Enter key to search = "; - cin >> key; - Entry entry = table[quadraticProbe(key, true)]; - if (entry.key == notPresent) { - cout << "Key not present"; + case 1: + std::cout << "Enter key to add = "; + std::cin >> key; + quadratic_probing::addInfo(key); + break; + case 2: + std::cout << "Enter key to remove = "; + std::cin >> key; + quadratic_probing::removalInfo(key); + break; + case 3: { + std::cout << "Enter key to search = "; + std::cin >> key; + quadratic_probing::Entry entry = + quadratic_probing::table[quadratic_probing::quadraticProbe( + key, true)]; + if (entry.key == quadratic_probing::notPresent) { + std::cout << "Key not present"; + } + break; } - break; + case 4: + std::cout << "Enter element to generate hash = "; + std::cin >> key; + std::cout << "Hash of " << key + << " is = " << quadratic_probing::hashFxn(key); + break; + case 5: + quadratic_probing::display(); + break; + default: + loop = false; + break; + // delete[] table; } - case 4: - cout << "Enter element to generate hash = "; - cin >> key; - cout << "Hash of " << key << " is = " << fabs(hashFxn(key)); - break; - case 5: - display(); - break; - default: - loop = false; - break; - delete[] table; - } - cout << endl; + std::cout << std::endl; } return 0; }