I'm trying to improve my understanding of inheritance and template classes in C++ so I coded a hash table (see compilable source code below).
I would be greatly appreciative of any comments on how to improve this code.
Description: The LinkedList
template class inherits from the template class Stack
. The Stack
class contains a member variable of type LLNode
which inherits from the template class Node
. The HashTable
class contains insert, delete and search operations. The constructor accepts a table size and two function pointers representing the hash-map and the prehash function.
#include<iostream>
#include<cmath>
template<class T>
class Node {
public:
Node(T key);
T getKey();
protected:
T key_;
};
template<class T> Node<T>::Node(T key):key_(key)
{}
template<class T> T Node<T>::getKey()
{
return key_;
}
template<class T>
class LLNode : public Node<T> {
public:
LLNode(T key);
LLNode<T>* getNext();
void setNext(LLNode<T>* next);
protected:
LLNode<T>* next_;
};
template<class T> LLNode<T>::LLNode(T key):Node<T>(key), next_(0)
{}
template<class T> LLNode<T>* LLNode<T>::getNext()
{
return next_;
}
template<class T> void LLNode<T>::setNext(LLNode<T>* next)
{
next_ = next;
}
template<class T>
class Stack {
public:
// constructor
Stack();
// destructor
virtual ~Stack();
// implements stack data structure
virtual void push(T data);
virtual void pop();
// return the number of nodes in the stack
int getSize() const;
int peek();
// wrapper functions for printing the list
void reversePrintList() const;
void printList() const;
protected:
LLNode<T>* createNode(T insertionKey);
LLNode<T>* head_;
int size_;
void reversePrintWorker(LLNode<T>*) const;
void printWorker(LLNode<T>*) const;
};
template<class T> Stack<T>::Stack():head_(0), size_(0)
{}
template<class T> Stack<T>::~Stack()
{
while(head_!=0)
{
pop();
}
}
template<class T> int Stack<T>::getSize() const
{
return size_;
}
template<class T> int Stack<T>::peek()
{
return head_->getKey();
}
template<class T> LLNode<T>* Stack<T>::createNode(T insertionKey)
{
LLNode<T>* insertionPtr = new LLNode<T>(insertionKey);
return insertionPtr;
}
template<class T> void Stack<T>::push(T data)
{
// create new node on the heap
LLNode<T>* newNode = createNode(data);
if(head_ == 0)
{
// assign the head pointer to the address of the new node
head_ = newNode;
}
else
{
// first link the new node to the first node in the list
newNode->setNext(head_);
// now assign the head pointer to the address of the first new node
head_ = newNode;
}
size_++;
}
template<class T> void Stack<T>::pop()
{
if(head_ != 0)
{
LLNode<T>* tp = head_;
head_ = head_->getNext();
delete tp;
size_--;
}
}
template<class T> void Stack<T>::reversePrintWorker(LLNode<T>* currPtr) const
{
if(currPtr != 0)
{
reversePrintWorker(currPtr->getNext());
std::cout << currPtr->getKey() << " ";
}
}
template<class T> void Stack<T>::printWorker(LLNode<T>* currPtr) const
{
if(currPtr != 0)
{
std::cout << currPtr->getKey() << " ";
printWorker(currPtr->getNext());
}
}
template<class T> void Stack<T>::reversePrintList() const
{
reversePrintWorker(head_);
}
template<class T> void Stack<T>::printList() const
{
//if(head_ != 0)
printWorker(head_);
}
template<class T>
class LinkedList : public Stack<T> {
public:
// deletes node at index 0 <= i <= size - 1
void deleteNode(int i);
// returns the index of the node with a given key
int indexOfKey(T key);
// return the key of a node at index i
int getData(int i) const;
};
template<class T> void LinkedList<T>::deleteNode(int idx)
{
if(idx == 0)
{
this->pop();
}
else
{
// declare traversal pointer and assign to head
LLNode<T>* tp = this->head_;
// declare previous pointer
LLNode<T>* pp = 0;
for(int i = 0; i < idx; i++)
{
// assign previous pointer to address of current node
pp=tp;
// advance traversal pointer
tp = tp->getNext();
}
pp->setNext( tp->getNext() );
delete tp;
(this->size_)--;
}
}
template<class T> int LinkedList<T>::getData(int idx) const
{
// declare traversal pointer and assign to head
LLNode<T>* tp = this->head_;
// iterate traversal pointer through the linked list
for(int i = 0; i < idx; i++) tp = tp->getNext();
// access traversal pointer's data
return tp->getKey();
}
template<class T> int LinkedList<T>::indexOfKey(T targetKey)
{
// declare traversal pointer and assign to head
LLNode<T>* tp = this->head_;
for(int i=0; i < this->size_; i++)
{
// check if the traversal pointer is pointing to a node which contains the key
if( tp->getKey() == targetKey ) return i;
// advance traversal pointer
tp = tp->getNext();
}
return -1;
}
class HashTable {
public:
// constructor
HashTable(int tableSize,
int (*prehash)(std::string key),
int (*hashmap)(int prehashedKey, int tableSize));
// dictionary operations
const bool search(std::string key) const;
void insert(std::string key);
void del(std::string key);
void printTable() const;
private:
// function pointers for prehash and hash map
int (*prehash_)(std::string key);
int (*hashmap_)(int prehashedKey, int m);
int tableSize_;
LinkedList<int>* table_;
};
HashTable::HashTable(int tableSize,
int (*prehash)(std::string key),
int (*hashmap)(int prehashedKey, int m))
:tableSize_(tableSize)
{
table_ = new LinkedList<int>[tableSize];
prehash_ = prehash;
hashmap_ = hashmap;
}
const bool HashTable::search(std::string key) const
{
// compute the slot associated with the key
int prehashedKey = (*prehash_)(key);
int slot = (*hashmap_)(prehashedKey, tableSize_);
// search the linked list for prehashedKey
int idx = table_[slot].indexOfKey(prehashedKey);
bool isThere = false;
if(idx != -1) isThere = true;
return isThere;
}
void HashTable::insert(std::string key)
{
// compute the slot associated with the key
int prehashedKey = (*prehash_)(key);
int slot = (*hashmap_)(prehashedKey, tableSize_);
// search the linked list for prehashedKey
int idx = table_[slot].indexOfKey(prehashedKey);
if(idx == -1)
{
// push the item into the linked list
table_[slot].push(prehashedKey);
}
else
{
std::cout << "Hash table already contains target key.\n";
}
}
void HashTable::del(std::string key)
{
// compute the slot associated with the key
int prehashedKey = (*prehash_)(key);
int slot = (*hashmap_)(prehashedKey, tableSize_);
// search the linked list for prehashedKey
int idx = table_[slot].indexOfKey(prehashedKey);
if(idx != -1)
{
// delete node at index idx
table_[slot].deleteNode(prehashedKey);
}
else
{
std::cout << "Hash table does not contain target key.\n";
}
}
void HashTable::printTable() const
{
for(int i=0; i < tableSize_; i++)
{
table_[i].printList();
std::cout << std::endl;
}
}
int prehash(std::string key)
{
int len = key.length();
int prehashedKey = 0;
for(int i = 0; i < len;i++) prehashedKey += ( (int)key[i] - 96 ) * pow(26,len-1-i);
return prehashedKey;
}
int hashmap(int prehashedKey, int tableSize)
{
return prehashedKey % tableSize;
}
int main()
{
// initializations
int NUMSLOTS = 21;
int NUMNAMES = 20;
std::string names[] =
{ "james", "jill", "eric", "sam", "alex", "sarah", "kate", "renee", "david", "jenny",
"felix", "henry", "peter", "alexis", "jing", "alfred", "anthony", "jeremy", "thomas", "ben"};
// fill table
HashTable myHashTable(NUMSLOTS,prehash,hashmap);
for(int i=0; i<NUMNAMES; i++) myHashTable.insert(names[i]);
// Print contents of hash table
myHashTable.printTable();
}