diff --git a/06-hash-tables/CMakeLists.txt b/06-hash-tables/CMakeLists.txt index d87bbaa..bdacd4b 100644 --- a/06-hash-tables/CMakeLists.txt +++ b/06-hash-tables/CMakeLists.txt @@ -4,4 +4,4 @@ project(06_hash_tables) set(CMAKE_CXX_STANDARD 20) add_executable(06_hash_tables main.cpp - HashTable.cpp) + Student.cpp) diff --git a/06-hash-tables/HashNode.h b/06-hash-tables/HashNode.h index 3ebf93c..c4385ca 100644 --- a/06-hash-tables/HashNode.h +++ b/06-hash-tables/HashNode.h @@ -1,16 +1,16 @@ // Specification file for the HashNode class +// Written By: Iurii Tatishchev +// Changed by: Iurii Tatishchev #ifndef _HASH_NODE #define _HASH_NODE -#include "Student.h" - -using std::string; - +// To do: convert to a template +template class HashNode { private: - Student item; - int occupied; // 1 -> occupied, 0 -> empty + T item; + int occupied; // 1 -> occupied, 0 -> empty from start, -1 -> empty after removal int noCollisions; public: @@ -20,27 +20,27 @@ public: noCollisions = 0; } - HashNode(Student anItem) { + HashNode(T anItem) { item = anItem; occupied = 1; noCollisions = 0; } - HashNode(Student anItem, int ocp, int nCol) { + HashNode(T anItem, int ocp, int nCol) { item = anItem; occupied = ocp; noCollisions = nCol; } // setters - void setItem(const Student &anItem) { item = anItem; } + void setItem(const T &anItem) { item = anItem; } void setOccupied(int ocp) { occupied = ocp; } void setNoCollisions(int nCol) { noCollisions = nCol; } // getters - Student getItem() const { return item; } + T getItem() const { return item; } int getOccupied() const { return occupied; } diff --git a/06-hash-tables/HashTable.h b/06-hash-tables/HashTable.h index 8ae678d..e1f322d 100644 --- a/06-hash-tables/HashTable.h +++ b/06-hash-tables/HashTable.h @@ -1,15 +1,17 @@ // Specification file for the Hash class +// Written By: Iurii Tatishchev +// Changed by: Iurii Tatishchev + #ifndef HASHTABLE_H_ #define HASHTABLE_H_ #include "HashNode.h" -using std::string; - +template class HashTable { private: - HashNode *hashAry; + HashNode *hashAry; int hashSize; int count; @@ -17,13 +19,13 @@ public: HashTable() { count = 0; hashSize = 53; - hashAry = new HashNode[hashSize]; + hashAry = new HashNode[hashSize]; } HashTable(int n) { count = 0; hashSize = n; - hashAry = new HashNode[hashSize]; + hashAry = new HashNode[hashSize]; } ~HashTable() { delete[] hashAry; } @@ -38,14 +40,81 @@ public: bool isFull() const { return count == hashSize; } - bool insert(const Student &itemIn); + bool insert(const ItemType &itemIn, int h(const ItemType &key, int size)); - bool remove(Student &itemOut, string key); + bool remove(ItemType &itemOut, const ItemType &key, int h(const ItemType &key, int size)); - int search(Student &itemOut, string key) const; - -private: - int _hash(string key) const; + int search(ItemType &itemOut, const ItemType &key, int h(const ItemType &key, int size)) const; }; +/*~*~*~* + Insert an item into the hash table + It does not reject duplicates +*~**/ +template +bool HashTable::insert(const ItemType &itemIn, int h(const ItemType &key, int size)) { + if (count == hashSize) + return false; + + int pos = h(itemIn, hashSize); + int collisions = 0; + while (hashAry[pos].getOccupied() == 1) { + ++pos; + ++collisions; + pos = pos % hashSize; + } + hashAry[pos].setItem(itemIn); + hashAry[pos].setOccupied(1); + hashAry[pos].setNoCollisions(collisions); + count++; + + return true; +} + +/*~*~*~* + Removes the item with the matching key from the hash table + if found: + - copies data in the hash node to itemOut + - replaces data in the hash node with an empty record (occupied = -1: deleted!) + - returns true + if not found: + - returns false +*~**/ +template +bool HashTable::remove(ItemType &itemOut, const ItemType &key, int h(const ItemType &key, int size)) { + int pos = h(key, hashSize); + for (int collisions = 0; collisions < count; collisions++) { + if (hashAry[pos].getOccupied() == 1 && hashAry[pos].getItem() == key) { + itemOut = hashAry[pos].getItem(); + // -1 means freed + hashAry[pos].setOccupied(-1); + count--; + return true; + } + pos = (pos + 1) % hashSize; + } + return false; +} + +/*~*~*~* + hash search - linear probe + if found: + - copy data to itemOut + - returns the number of collisions for this key + if not found, returns -1 +*~**/ +template +int HashTable::search(ItemType &itemOut, const ItemType &key, int h(const ItemType &key, int size)) const { + int pos = h(key, hashSize); + for (int collisions = 0; collisions < count; collisions++) { + if (hashAry[pos].getOccupied() == 0) return -1; + if (hashAry[pos].getOccupied() == 1 && hashAry[pos].getItem() == key) { + itemOut = hashAry[pos].getItem(); + return hashAry[pos].getNoCollisions(); + } + pos = (pos + 1) % hashSize; + } + return -1; +} + #endif // HASHTABLE_H_ diff --git a/06-hash-tables/Student.cpp b/06-hash-tables/Student.cpp new file mode 100644 index 0000000..c63f8eb --- /dev/null +++ b/06-hash-tables/Student.cpp @@ -0,0 +1,16 @@ +// Implementation file for the Student class +// Written By: Iurii Tatishchev + +#include +#include "Student.h" + +/*~*~*~* + Hash function: takes the key and returns the index in the hash table + *~**/ +int key_to_index(const Student &key, int size) { + string k = key.name; + int sum = 0; + for (int i = 0; k[i]; i++) + sum += k[i]; + return sum % size; +}; diff --git a/06-hash-tables/Student.h b/06-hash-tables/Student.h index 9de686f..9c7a7af 100644 --- a/06-hash-tables/Student.h +++ b/06-hash-tables/Student.h @@ -1,10 +1,17 @@ // Specification file for the Student class +// Modified by: Iurii Tatishchev +// IDE: CLion #ifndef STUDENT_H #define STUDENT_H using std::string; +class Student; // Forward Declaration + +// Function Prototypes for friend functions +int key_to_index(const Student &key, int size); + class Student { private: double gpa; @@ -16,7 +23,7 @@ public: gpa = -1; } // Constructor Student(string n, double g) { - name = n; + name = n;/* Write your code here */ gpa = g; } // Overloaded Constructor @@ -29,6 +36,11 @@ public: double getGpa() const { return gpa; } + // Overloaded operators + bool operator==(const Student& other) { return name == other.name; } + + // friend functions + friend int key_to_index(const Student& key, int size); }; #endif diff --git a/06-hash-tables/main.cpp b/06-hash-tables/main.cpp index 4ee4925..f911a0c 100644 --- a/06-hash-tables/main.cpp +++ b/06-hash-tables/main.cpp @@ -1,27 +1,26 @@ /* CIS 22C - Hashing - Linear Probe: insert, search, and delete + Hash Tables ADT - Linear Probe Written By: Iurii Tatishchev Reviewed & Modified by: Iurii Tatishchev -*/ + */ #include -#include - #include "HashTable.h" +#include "Student.h" using namespace std; -void buildHash(HashTable &hash); +void buildHash(HashTable &hash); -void searchManager(const HashTable &hash); +void searchManager(const HashTable &hash); -void deleteManager(HashTable &hash); +void deleteManager(HashTable &hash); -void insertManager(HashTable &hash); +void insertManager(HashTable &hash); int main() { - HashTable hash; + HashTable hash; buildHash(hash); cout << "Load Factor: " << hash.getLoadFactor() << endl; @@ -36,7 +35,7 @@ int main() { This function builds a hash table with data from an array It calls the insert() function that inserts the new data at the right location in the hash table. ************************************************** */ -void buildHash(HashTable &hash) { +void buildHash(HashTable &hash) { Student list[] = {{"Tom", 2.5}, {"Bob", 3.2}, @@ -54,7 +53,7 @@ void buildHash(HashTable &hash) { {"", 0}}; for (int i = 0; list[i].getName() != ""; i++) { - hash.insert(list[i]); + hash.insert(list[i], key_to_index); } } @@ -64,14 +63,15 @@ This function searches a hash table with user provided data. It calls the hash search function in a loop. To stop searching enter "#" ************************************************** */ -void searchManager(const HashTable &hash) { +void searchManager(const HashTable &hash) { cout << endl << "~*~ Test Search ~*~" << endl; cout << "Enter name [# to stop searching]:" << endl; string name; getline(cin, name); while (name != "#") { Student item; - int nc = hash.search(item, name); + item.setName(name); + int nc = hash.search(item, item, key_to_index); if (nc != -1) { cout << item.getName() << " " << item.getGpa() << " (" << nc << " collisions!)" << endl; } else { @@ -79,6 +79,7 @@ void searchManager(const HashTable &hash) { } getline(cin, name); } + cout << "Load Factor: " << hash.getLoadFactor() << endl; } /* ************************************************** @@ -86,21 +87,22 @@ This function deletes user provided data data from a hash table It calls the hash delete function in a loop. To stop deleting enter "#" ************************************************** */ -void deleteManager(HashTable &hash) { +void deleteManager(HashTable &hash) { cout << endl << "~*~ Test Delete ~*~" << endl; cout << "Enter name [# to stop deleting]:" << endl; string name; getline(cin, name); while (name != "#") { Student itemOut; - if (hash.remove(itemOut, name)) { + itemOut.setName(name); + if (hash.remove(itemOut, itemOut, key_to_index)) { cout << itemOut.getName() << " " << itemOut.getGpa() << " - deleted!" << endl; } else { cout << name << " not found!" << endl; } - cout << "Load Factor: " << hash.getLoadFactor() << endl; getline(cin, name); } + cout << "Load Factor: " << hash.getLoadFactor() << endl; } /* ************************************************** @@ -109,14 +111,15 @@ It rejects duplicates. It calls hash search and hash insert in a loop. To stop getting user input enter "#" ************************************************** */ -void insertManager(HashTable &hash) { +void insertManager(HashTable &hash) { cout << endl << "~*~ Test Insert - reject duplicates ~*~" << endl; cout << "Enter name [# to stop reading]:" << endl; string name; getline(cin, name); while (name != "#") { Student found; - if (hash.search(found, name) != -1) { + found.setName(name); + if (hash.search(found, found, key_to_index) != -1) { cout << "Duplicate key: " << found.getName() << " - rejected!" << endl; } else { cout << "Enter gpa:" << endl; @@ -124,11 +127,11 @@ void insertManager(HashTable &hash) { cin >> gpa; cin.ignore(); Student newStudent(name, gpa); - hash.insert(newStudent); - cout << name << " - inserted (" << hash.search(found, name) << " collisions)" << endl; + hash.insert(newStudent, key_to_index); + cout << name << " - inserted (" << hash.search(found, newStudent, key_to_index) << " collisions)" << endl; } - cout << "Load Factor: " << hash.getLoadFactor() << endl; cout << "Enter name [# to stop reading]:" << endl; getline(cin, name); } + cout << "Load Factor: " << hash.getLoadFactor() << endl; }