226 lines
6.1 KiB
C++
226 lines
6.1 KiB
C++
#ifndef INC_08_TEAM_PROJECT_HASHTABLE_H
|
|
#define INC_08_TEAM_PROJECT_HASHTABLE_H
|
|
|
|
#include <fstream>
|
|
#include "HashNode.h"
|
|
#include "CPU.h"
|
|
#include "utils.h"
|
|
|
|
using std::string, std::ofstream, std::cout, std::endl;
|
|
|
|
template<typename T>
|
|
class HashTable {
|
|
private:
|
|
HashNode<T> *hashAry;
|
|
int hashSize;
|
|
int count;
|
|
|
|
public:
|
|
HashTable() {
|
|
count = 0;
|
|
hashSize = 97;
|
|
hashAry = new HashNode<T>[hashSize];
|
|
}
|
|
|
|
HashTable(int n) {
|
|
count = 0;
|
|
hashSize = n;
|
|
hashAry = new HashNode<T>[hashSize];
|
|
}
|
|
|
|
~HashTable() { delete[] hashAry; }
|
|
|
|
int getCount() const { return count; }
|
|
|
|
int getHashSize() const { return hashSize; }
|
|
|
|
double getLoadFactor() const { return 100.0 * count / hashSize; }
|
|
|
|
bool isEmpty() const { return count == 0; }
|
|
|
|
bool isFull() const { return count == hashSize; }
|
|
|
|
int getTotalCollisions() const;
|
|
|
|
int getMaxCollisions() const;
|
|
|
|
bool insert(const T &itemIn, int h(const T &key, int size));
|
|
|
|
bool remove(T &itemOut, const T &key, int h(const T &key, int size));
|
|
|
|
int search(T &itemOut, const T &key, int h(const T &key, int size)) const;
|
|
|
|
void outputFile(const string &filename, string visit(const T &));
|
|
|
|
void reHash(int h(const T &key, int size));
|
|
};
|
|
|
|
/*~*~*~*
|
|
Get total number of collisions throughout hash table
|
|
*~**/
|
|
template<typename T>
|
|
int HashTable<T>::getTotalCollisions() const {
|
|
int total = 0;
|
|
for (int i = 0; i < hashSize; i++) {
|
|
if (hashAry[i].getOccupied() == 1) {
|
|
total += hashAry[i].getNumCollisions();
|
|
}
|
|
}
|
|
return total;
|
|
}
|
|
|
|
/*~*~*~*
|
|
Get longest collision length in hash table
|
|
*~**/
|
|
template<typename T>
|
|
int HashTable<T>::getMaxCollisions() const {
|
|
int high = 0;
|
|
for (int i = 0; i < hashSize; i++) {
|
|
if (hashAry[i].getOccupied() == 1 && hashAry[i].getNumCollisions() > high) {
|
|
high = hashAry[i].getNumCollisions();
|
|
}
|
|
}
|
|
return high;
|
|
}
|
|
|
|
/*~*~*~*
|
|
Insert an item into the hash table
|
|
It does not reject duplicates
|
|
*~**/
|
|
template<typename T>
|
|
bool HashTable<T>::insert(const T &itemIn, int h(const T &key, int size)) {
|
|
if (count == hashSize)
|
|
return false;
|
|
|
|
int ind = h(itemIn, hashSize);
|
|
for (int i = 0; i < hashSize; i++) {
|
|
if (hashAry[(ind + i) % hashSize].getOccupied() != 1) {
|
|
hashAry[(ind + i) % hashSize].setOccupied(1);
|
|
hashAry[(ind + i) % hashSize].setItem(itemIn);
|
|
hashAry[(ind + i) % hashSize].setNumCollisions(i);
|
|
count++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
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<typename T>
|
|
bool HashTable<T>::remove(T &itemOut, const T &key, int h(const T &key, int size)) {
|
|
int ind = h(key, hashSize);
|
|
for (int i = 0; i < hashSize; i++) {
|
|
if (hashAry[(ind + i) % hashSize].getOccupied() == 1) {
|
|
if (hashAry[(ind + i) % hashSize].getItem() == key) {
|
|
itemOut = hashAry[(ind + i) % hashSize].getItem();
|
|
hashAry[(ind + i) % hashSize].setOccupied(-1);
|
|
// Do not access a node with occupied of -1
|
|
count--;
|
|
return true;
|
|
}
|
|
} else if (hashAry[(ind + 1) % hashSize].getOccupied() == 0) {
|
|
// If using linear probing and we come across a spot that has been empty since start, the item is not in the table
|
|
break;
|
|
}
|
|
}
|
|
|
|
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<typename T>
|
|
int HashTable<T>::search(T &itemOut, const T &key, int h(const T &key, int size)) const {
|
|
int ind = h(key, hashSize);
|
|
for (int i = 0; i < hashSize; i++) {
|
|
if (hashAry[(ind + i) % hashSize].getOccupied() == 1) {
|
|
if (hashAry[(ind + i) % hashSize].getItem() == key) {
|
|
itemOut = hashAry[(ind + i) % hashSize].getItem();
|
|
return hashAry[(ind + i) % hashSize].getNumCollisions();
|
|
}
|
|
} else if (hashAry[(ind + 1) % hashSize].getOccupied() == 0) {
|
|
// If using linear probing and we come across a spot that has been empty since start, the item is not in the table
|
|
break;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
template<class T>
|
|
void HashTable<T>::outputFile(const string &filename, string visit(const T &)) {
|
|
ofstream outputFile(filename);
|
|
cout << "Outputting data to \"" << filename << "\"" << endl;
|
|
|
|
if (!outputFile.good()) {
|
|
cout << "Error opening the output file: \"" << filename << "\"" << endl;
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
T aT;
|
|
|
|
if (hashAry[0].getOccupied() == 1) {
|
|
aT = hashAry[0].getItem();
|
|
outputFile << visit(aT);
|
|
}
|
|
|
|
for (int i = 1; i < hashSize; i++) {
|
|
if (hashAry[i].getOccupied() == 1) {
|
|
aT = hashAry[i].getItem();
|
|
outputFile << endl << visit(aT);
|
|
}
|
|
}
|
|
|
|
outputFile.close();
|
|
}
|
|
|
|
template<class T>
|
|
void HashTable<T>::reHash(int h(const T &key, int size)){
|
|
|
|
int nHashSize = hashSize * 2;
|
|
|
|
nHashSize = findNextPrime(nHashSize);
|
|
|
|
HashNode<T>* nHashAry = new HashNode<T>[nHashSize];
|
|
|
|
// Goes through each bucket and puts it in the new array
|
|
T aT;
|
|
for(int i = 0; i < hashSize; i++){
|
|
if(hashAry[i].getOccupied() == 1){
|
|
aT = hashAry[i].getItem();
|
|
|
|
int nIndex = h(aT, nHashSize);
|
|
|
|
for(int j = 0; j < hashSize; j++){
|
|
if(nHashAry[nIndex].getOccupied() != 1){
|
|
nHashAry[nIndex].setItem(aT);
|
|
nHashAry[nIndex].setOccupied(1);
|
|
nHashAry[nIndex].setNumCollisions(i);
|
|
break;
|
|
}
|
|
|
|
nIndex = (nIndex + 1) % hashSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
hashAry = nHashAry;
|
|
hashSize = nHashSize;
|
|
}
|
|
|
|
#endif // INC_08_TEAM_PROJECT_HASHTABLE_H
|