I have been asked to create a rehashing algorithm with instructions:
Backup the old vector Table to a temporary vector oldTable
Delete the elements in the old Table
Obtain the new table size
Expand Table to the new size
Reinsert elements from oldTable into the expanded new Table
Delete the elements in the oldTable
Hash Table.h Class:
#ifndef HASHTABLE_H
#define HASHTABLE_H
#include "Math.h"
// hash table storing class X objects using linear probing
template <class X>
class HashTable {
public:
// constructor sets the hash table size & load threshold
HashTable(int table_size, double load_threshold = 0.75);
// destructor
~HashTable() { for (int i = 0; i < Table.size(); i++) if (Table[i]) delete Table[i]; }
// search for object a in the table
size_t find(X& a); // size_t = unsigned int
// insert new object a in the table, return true if done
bool insert(X& a);
//function to return a new prime table size
size_t newTableSize();
//rehash func
void reHash();
private:
// the hash table & number of objects stored
vector<X*> Table;
size_t num_x;
// maximum load threshold
double LOAD_TH;
};
template <class X>
HashTable<X>::HashTable(int table_size, double load_threshold)
{
for (int i = 0; i < table_size; i++) Table.push_back(NULL);
num_x = 0;
LOAD_TH = load_threshold;
}
template <class X>
size_t HashTable<X>::newTableSize() {
bool found = true;
int newSize = 2 * Table.size() + 1; // = now odd because oldSize is a prime
do {
int x = sqrt(newSize);
for (int i = 3; i <= x; i += 2) {
if (newSize % i == 0) {
newSize = newSize + 2;
x = sqrt(newSize);
break;
}
else
{
found = true;
}
}
} while (!found);
return newSize;
}
template<class X>
void HashTable<X>::reHash() {
//Backup the old vector<X*> Table to a temporary vector<X*> oldTable
vector<X*> tempTable;
tempTable = Table;
//Delete the elements in the old Table
Table.clear();
//Obtain the new table size
int newPrimeSize = newTableSize();
//Expand Table to the new size
Table.resize(newPrimeSize);
//Reinsert elements from oldTable into the expanded new Table
//Table = tempTable;
//or method below??
for (int i = 0; i < tempTable.size; i++) {
if (tempTable[i] != NULL){
Table.insert(tempTable[i]);
}
}
//Delete the elements in the oldTable
tempTable.clear();
}
template <class X>
size_t HashTable<X>::find(X& a)
{
// calculate the hash index
size_t index = a.hash_index() % Table.size();
// search - find index of matching key or the 1st empty slot
while (Table[index] != NULL && Table[index]->get_key() != a.get_key())
index = (index + 1) % Table.size();
// retrieve matching value to a if found
if (Table[index] != NULL) a.set_value(Table[index]->get_value());
return index;
}
template <class X>
bool HashTable<X>::insert(X& a)
{
// calculate the load factor of the table
double load_factor = (double)num_x / (double)Table.size();
if (load_factor > LOAD_TH) {
// replace the following return by rehashing - practical work
return 0;
}
// search a in the able
size_t index = find(a);
// not found, create a new entry in the table
if (Table[index] == NULL) {
Table[index] = new X(a);
num_x++;
return 1;
}
// object already in table, do nothing
return 0;
}
#endif
Test Class:
#include <iostream>
#include <vector>
#include <string>
#include <time.h>
#include "Math.h"
using namespace std;
#include "HashTable.h"
// a class of phone records
class PhoneDir {
public:
PhoneDir(string name, int number = -1)
: name(name), number(number) {};
string get_key() { return name; }
int get_value() { return number; }
void set_value(int num) { number = num; }
size_t hash_index(); // return hash index of key: name
private:
string name; // key
int number; // value
};
size_t PhoneDir::hash_index()
{
size_t hash_index = 0;
for (int i = 0; i < name.size(); i++) {
char c = name[i];
hash_index = 37 * hash_index + c;
}
return hash_index;
}
int main()
{
int oldSize = 5;
// store phone records in hash table with size 11
HashTable<PhoneDir> HTable(7);
HTable.insert(PhoneDir("Tom", 123456));
HTable.insert(PhoneDir("Sam", 346834));
HTable.insert(PhoneDir("Pete", 347980));
HTable.insert(PhoneDir("Jack", 328709));
HTable.insert(PhoneDir("David", 335566));
// serach using name for phone number over the hash table
char yn = 'y';
do {
//test function part 1
cout << " Test " << endl;
cout << HTable.newTableSize();
cout << "Whose number are you looking for? ";
string name; cin >> name;
// form enquiry and search
PhoneDir enquiry(name);
clock_t t0 = clock();
size_t index = HTable.find(enquiry);
clock_t t1 = clock();
cout << "index = " << index;
cout << ", name = " << enquiry.get_key();
cout << ", number = " << enquiry.get_value() << endl;
cout << "time taken = " << t1 - t0 << endl << endl;
cout << "Another (y/n)? "; cin >> yn;
} while (yn == 'y');
return 0;
}
I successfully created & tested the newTableSize function & now I am on the rehash algorithm function.
The use of vectors is confusing me and I am new to this.
Am i on the right track? Will the parts of my re-hash algorithm work?
Thanks
One of the most easy way is create a new HashTable and swap the two.
Create a new HashTable<X>
insert all items into it.
call std::swap(*this,new_hashTable);
if you want minimun change to your code,
change
Table.insert(tempTable[i]); to
this->insert(*tempTable[i]); would probably work.
Don't forget the rehash as you currently use would generate collision, you need to handle this.
It is better to directly build a tempTable vector with the new size, and when done move it to the Table vector because it involve less copy operation:
save Table, clean, copy:
save is a copy of a vector: involves copying all elements (ok they are only pointers)
clear the original vector
resize the original vector
copy all elements
clear the temp vector
create temp and move:
create a temp vector with the new size
copy all elements
move the temp vector to the original one: you directly steal the internal array of the temp vector which is about to be destroyed instead of copying all of its elements.
Less and simpler operations...
Related
I'm just a humble student looking to further my knowledge about the C++ language. My professor isn't helping! I think the title along with comments in-code explain my issues clearly.
#ifndef H_Htable
#define H_Htable
//****************************************************************
// Author: D.S. Malik
//
// This class specifies the members to implement a hash table as
// an ADT. It uses quadratic probing to resolve collisions.
//****************************************************************
#include <iostream>
#include <cassert>
using namespace std;
template <class elemType>
class hashT
{
public:
void insert(int hashIndex, const elemType& rec);
//Function to insert an item in the hash table. The first
//parameter specifies the initial hash index of the item to
//be inserted. The item to be inserted is specified by the
//parameter rec.
//Postcondition: If an empty position is found in the hash
// table, rec is inserted and the length is incremented by
// one; otherwise, an appropriate error message is
// displayed.
//sequential search
bool search(int& hashIndex, const elemType& rec, bool found = false) const;
//Function to determine whether the item specified by the
//parameter rec is in the hash table. The parameter hashIndex
//specifies the initial hash index of rec.
//Postcondition: If rec is found, found is set to true and
// hashIndex specifies the position where rec is found;
// otherwise, found is set to false.
bool isItemAtEqual(int hashIndex, const elemType& rec) const;
//Function to determine whether the item specified by the
//parameter rec is the same as the item in the hash table
//at position hashIndex.
//Postcondition: Returns true if HTable[hashIndex] == rec;
// otherwise, returns false.
void retrieve(int hashIndex, elemType& rec) const;
//Function to retrieve the item at position hashIndex.
//Postcondition: If the table has an item at position
// hashIndex, it is copied into rec.
void remove(int hashIndex, const elemType& rec);
//Function to remove an item from the hash table.
//Postcondition: Given the initial hashIndex, if rec is found
// in the table it is removed; otherwise, an appropriate
// error message is displayed.
void print() const;
//Function to output the data.
//provide for both int and string data types in the hash table
hashT(int size = 101, bool isIntTable = true);
//constructor
//Postcondition: Create the arrays HTTable and indexStatusList;
// initialize the array indexStatusList to 0; length = 0;
// HTSize = size; and the default array size is 101.
~hashT();
//destructor
//Postcondition: Array HTable and indexStatusList are deleted.
private:
elemType *HTable; //pointer to the hash table
int *indexStatusList; //pointer to the array indicating the
//status of a position in the hash table
int length; //number of items in the hash table
int HTSize; //maximum size of the hash table
};
template <class elemType>
void hashT<elemType>::insert(int hashIndex, const elemType& rec)
{
int pCount;
int inc;
pCount = 0;
inc = 1;
while (indexStatusList[hashIndex] == 1
&& HTable[hashIndex] != rec
&& pCount < HTSize / 2)
{
pCount++;
hashIndex = (hashIndex + inc) % HTSize;
inc = inc + 2;
}
if (indexStatusList[hashIndex] != 1)
{
HTable[hashIndex] = rec;
indexStatusList[hashIndex] = 1;
length++;
}
else
if (HTable[hashIndex] == rec)
cerr << "Error: No duplicates are allowed." << endl;
else
cerr << "Error: The table is full. "
<< "Unable to resolve the collision." << endl;
}
//sequential search
template <class elemType>
bool hashT<elemType>::search(int& hashIndex, const elemType& rec, bool found) const
{
for (int i = 0; i < HTSize; i++) {
if (HTable[i] == rec) { //assuming no repeat data
found = true;
hashIndex = i;
break;
}
}
return found;
}
template <class elemType>
bool hashT<elemType>::isItemAtEqual(int hashIndex, const elemType& rec) const
{
//first make sure the item has not been removed
if (indexStatusList[hashIndex] != -1) {
//make equality comparison
if (HTable[hashIndex] == rec)
return true;
else
return false; //comparison fails
}
else
{
std::cerr << "isItemEqual(): Item has been removed" << endl;
return false;
}
}
template <class elemType>
void hashT<elemType>::retrieve(int hashIndex, elemType& rec) const
{
if (indexStatusList[hashIndex] != -1)
rec = HTable[hashIndex];
else
std::cerr << "retrieve(): item has been removed" << endl;
}
template <class elemType>
void hashT<elemType>::remove(int hashIndex, const elemType& rec)
{
//make sure the item hasn't already been removed
if (indexStatusList[hashIndex] != -1) {
bool isInList = hashT<elemType>::search(hashIndex, rec);
//update the status
if (isInList)
{
indexStatusList[hashIndex] = -1;
length--; //decrement length
}
else
std::cerr << "hasT::remove() could not remove the specified item" << endl;
}
else
{
std::cerr << "remove(): Item has already been removed from the table" << endl;
}
}
template <class elemType>
void hashT<elemType>::print() const
{
std::cout << "Hash Table Data: " << endl;
for (int i = 0; i < (length - 5); i++) {
elemType item = HTable[i];
//std::cout << item << " ";
}
}
template <class elemType>
hashT<elemType>::hashT(int size, bool isIntTable)
{
HTable = new elemType[]; //is this right? HTable is an array just like indexStatusList
HTSize = size;
length = 0;
indexStatusList = new int[0]; //I think this one works?
}
template <class elemType>
hashT<elemType>::~hashT() //deleting always causes heap errors!!!
//says writing to unallocated memory -- debugging shows otherwise
{
//delete[] HTable;
//delete[] indexStatusList; //still causing errors -- error now not associated with any particular line (of my code)
}
#endif
I've kept increasing my bounds checking security when instantiating hashT in main. I'm convinced it is because my data members are being initialized incorrectly. This is one error message I get after trying a few things: "Unhandled exception at 0x773F627C (ntdll.dll) in exercise7Chap9.exe: 0xC0000374: A heap has been corrupted (parameters: 0x77426480)."
finally, here's main just in case:
#include <iostream>
#include "hashT.h"
int main() {
//add one item and test for equality
//hashT<int> ht = hashT<int>(20);
//ht.insert(0, 1);
//bool itemInsertSuccess = ht.isItemAtEqual(0, 1);
//if (itemInsertSuccess)
// std::cout << "first test has succeeded" << endl;
//else
// std::cout << "first test has failed" << endl;
////remove item and make sure isItemEqual returns false
//ht.remove(0, 1);
//bool itemRemoved = ht.isItemAtEqual(0, 1);
//if (!itemRemoved)
// std::cout << "second test passed" << endl;
//else
// std::cout << "second test failed" << endl;
//add many items then make sure search() works
hashT<int> ht1 = hashT<int>(51);
for (int i = 0; i < 10; i++)
ht1.insert(i, i);
int indx = -1;
ht1.search(indx, 0);
if (indx == 25)
std::cout << "Test 3 has passed" << endl;
else
std::cout << "Test 3 has failed" << endl;
//print data then test retrieve() and print a single item
/*ht1.print();
int item = -1;
ht1.retrieve(10, item);
if (item != -1) {
std::cout << item << endl;
std::cout << "test 4 has passed" << endl;
}
else
std::cout << "test 4 has failed" << endl;
hashT<int> HtRetrieve = hashT<int>(10);
HtRetrieve.insert(0, 0);
int it = -1;
HtRetrieve.retrieve(0, it);
std::cout << it << endl;*/
char stop;
std::cin >> stop;
//return 0;
}
In your case here, scrap the variable isIntTable. Templates are a compile-time construct and run-time values won't influence how the template is compiled into a class in any way.
Then, in your constructor, just use the template type as the one you are allocating.
template <class elemType>
hashT<elemType>::hashT(int size)
{
HTable = new elemType[size];
length = 0;
indexStatusList = new int[0];
}
However, this could be much better. Consider using initialisation instead of assignation:
hashT<elemType>::hashT(int size) :
HTable{new elemType[size]},
length{size},
indexStatusList{int[size]} { /* empty constructor */ }
And it can be even better. Consider using smart pointer instead of raw owning pointers and vectors instead of dynamic allocated array:
template<typename T>
struct hashT {
// using the right type for size
hashT(std::size_t size) : pointerToOneT{std::make_unique<T>()}, HTable(size) {}
// make_unique is the `new` for unique pointers
// no need for destructors, the vector and unique_ptr are freeing themselves
private:
std::unique_ptr<T> pointerToOneT;
std::vector<T> HTable;
std::vector<int> indexStatusList;
};
If you don't want to use std::vector, you can always use std::unique_ptr<T[]>, which is a dynamically allocated array that free itself.
template<typename T>
struct hashT {
// using the right type for size
hashT(std::size_t size) :
HTable{std::make_unique<T[]>(size)},
indexStatusList(std::make_unique<int[]>(size)) {}
private:
std::unique_ptr<T[]> HTable;
std::unique_ptr<int[]> indexStatusList;
};
EDIT
In your actual destructor, the problem is that you initialized the int* with the new[] but you are using delete. To delete an array, you must use delete[]
template <class elemType>
hashT<elemType>::~hashT()
{
delete HTable;
delete[] indexStatusList;
}
Turns out this heap corruption was caused by my lack of understanding dynamic arrays in C++.
My incorrect initialization of the arrays HTable and indexStatusList were: Htable = new elemType();, HTable = new elemType[]; and indexStatusList = new int[0];
I simply needed to add the size as an argument (never seen a size argument passed in brackets before!)
Here is the working constructor:
//constructor
template <class elemType>
hashT<elemType>::hashT(int size)
{
HTable = new elemType[size]; // pass size so the compiler knows what to allocate and deallocate
HTSize = size;
length = 0;
indexStatusList = new int[size];
}
working destructor:
template <class elemType>
hashT<elemType>::~hashT()
{
delete[] HTable;
delete[] indexStatusList;
}
I am having a problem with my program throwing an exception when inserting data at specific index's of my array. I am using a hash table, and trying to use the STL list in an array of pointers to another class that contains the data.
Since this is a Hash Table, I'm avoiding using the vector class since the size of the array should be constant. (I know the initial size that I want)
MCVE (For you) :
#ifndef HASHTABLE_H
#define HASHTABLE_H
#include <list>
#include <cstring>
#include <stdlib.h>
template <typename T1>
class HashTable
{
public:
HashTable();
void Insert(T1 var)
int FindPrime(int);
int HashFunction(string);
private:
int prime;
list<T1> *List;
int LF;
}
#endif
template <typename T1>
HashTable<T1>::HashTable()
{
List[i] = list<T1>();
}
template <typename T1>
int HashTable<T1>::FindPrime(int num)
{
bool isNotPrime = false;
for (int i=num; i < num + 25; ++i)
{
for (int j=2; j<i; ++j)
{
if (i % j == 0)
{
isNotPrime = true;
}
}
if (isNotPrime == false)
{
prime = i;
return prime;
break;
}
isNotPrime = false;
}
prime = num;
return prime;
}
template <typename T1>
long HashTable<T1>::HashFunction(string key)
{
long numkey = 0;
char word[1000];
strcpy(word,key.c_str());
word[sizeof(word) - 1] = NULL; //Ensure null is at last index of word
for(int i = 0; word[i] != NULL; ++i)
{
numkey = numkey + (word[i] * 101 + word[i]);
}
numkey = numkey % prime;
return numkey;
}
template <typename T1>
void HashTable<T1>::Insert(T1 var)
{
int index = HashFunction(var -> getKey());
List[index].push_front(var);
++LF;
cout << "Load Factor: " << LF << endl << endl;
}
from a seperate class that is determining what to do with the data:
file >> num;
hash.FindPrime(num);
file >> letter; // Get letter from file so we know what to do
if(letter == 'D' || letter == 'd') //If the letter is D, then add a new DNA Node with corresponding data to the STL List
{
file >> Label >> ID >> Seq >> Length >> Index;
cout << "Note: Adding " << Label << " ..." << endl << endl;
Sequence* ptr = new DNA(Label, ID, Seq, Length, Index);
hash.Insert(ptr);
ptr = NULL;
delete ptr;
}
Sequence Class is the base class for Several inherited classes (DNA is one of them)
Type of List[i] is list<T1>. That's why the compiler doesn't allow you to use:
List[i] = new list<T1>;
// Trying to assign a list<T1>* to a list<T1>.
You can use
List[i] = list<T1>();
or
list<T1>** List;
I have an assignment to create a template container class with a dynamic array that can be increased and decreased. When the array is displayed, I get either garbage numbers or no numbers at all for the elements in the array where it was expanded(so if it expanded on the fifth element, the value would turn to junk after I enter the sixth). When I try to remove elements I get a Debug Assertion Failed error.
Here is my code:
template <class T> class container {
public:
container(); // constructor
// Post-condition: count is set to -1 and dynamic array size set to 5
~container(); //destructor
void insert(T &n);
// Pre-condition: a value is passed to the function
// Post-condition: if data array is not full, increment count by 1 and insert the value in the array
// Otherwise, display "Container full! No insertion is made."
void remove();
//Post-condition: if data array is not empty, remove the data[count] element in data array and decrement count by 1;
//otherwise, display a message "Container empty! Nothing is removed from it."
void display();
// Post-condition: if the array is not empty, displays all values in data array similar to the sample output;
//Otherwise, display the message “Container is now empty!"
void fillarray(container c);
//pre-condition: a container c is passed to the function
//post-condition: dynamic array of chosen type is created and filled continuously with user entered values
private:
bool empty;
//Post-condition: returns true is the array is empty, otherwise returns false
T *data; // dynamically allocated array used to store or contain the inserted values
int count; // indicates how many values have been inserted
int max;
};
template <class T> container<T>::container()
{
count = -1;
max = 5;
data = new T[max];
assert(data != NULL);
}
template <class T> container<T>::~container()
{
delete [] data;
}
template <class T> void container<T>::insert(T &n)
{
if (count >= (max - 1))
{
max = max * 2;
cout << "\nContainer full! Array size is increased by " << max/2 << ".";
T *temp = new T[max];
assert(temp != NULL);
for (int i = 0; i < count; i++)
temp[i] = data[i];
delete [] data;
data = temp;
count++;
data[count] = n;
}
else
count++;
data[count] = n;
}
template <class T> void container<T>::remove()
{
empty = count < 0;
if (empty == 1)
{
cout << "\nContainer empty! Nothing is removed from it.";}
else
{
count--;
T *temp1 = new T[max];
assert(temp1 != NULL);
for (int i = 0; i < count; i++)
temp1[i] = data[i];
delete [] data;
data = temp1;
}
}
template <class T> void container<T>::display()
{
empty = count < 0;
if (empty == 1)
{
cout << "\nContainer is now empty!";}
else
{
for (int i = 0; i <= count; ++i)
cout << " " << data[i];
}
}
template <class T> void container<T>::fillarray(container c)
{
char ans;
do
{
T value;
cout << "\nEnter a value:";
cin >> value;
c.insert(value);
cout << "\nAfter inserting, the \"container\" contains:" << endl;
c.display();
cout << "\nEnter more? (Y/y or N/n)";
cin >> ans;
} while (ans == 'Y' || ans == 'y');
for (int i = 0; i <= count; i++)
{
c.remove();
cout << "\nAfter removing a value from it, the \"container\" contains:" << endl;
c.display();
cout << endl;
}
}
// The main driver function to be used to test implementation
int main()
{
char choice;
cout << "\nEnter S for string container, D for double";
cin >> choice;
if (choice == 'S' || choice == 's')
{
container<string> c;
c.display();
c.fillarray(c);
}
else if(choice == 'D' || choice == 'd')
{
container<double> c;
c.display();
c.fillarray(c);
}
return 0;
}
template <class T> void container<T>::fillarray(container c)
{
//...
}
This function actually involves two container<T> objects: *this and c.
Since you "pass by value" (the function parameter is not a reference), c in fillarray is created as a copy of the original c in main. In fillarray you modify c, which deletes and changes c.data, but this->data still contains the dangling pointer to the original storage. Before long, you get undefined behavior; luckily enough bad things happened you could tell something was wrong.
Per the Rule of Three (Plus Two), if a class has a destructor, you probably should not allow the compiler to generate the default copy constructor and copy assignment, and you may want to consider implementing a move constructor and move assignment.
The easiest, and sometimes best, way of meeting that rule is to disable copying:
template <class T> class container
{
public:
container(container const&) = delete;
container& operator=(container const&) = delete;
//...
};
Then the compiler will make sure you don't accidentally make a copy and get yourself into this sort of trouble.
You make life confusing by count referring to maximum index rather than the number of entries. Using your scheme though, this line in insert is odd:
for (int i = 0; i < count; i++)
As that won't copy the final entry.
Basically a dynamic array, which has circular rotation when is full. You have access to every element and you can change it's value, however you can insert and remove only from both ends.(constant time). Most of the methods seem to be working fine, however at certain "push" numbers I get wrong output.
For example first input is 1,2,3 then I insert 4 at the end. Next output is: 2,3,4 However after I insert 5 at the end the output is 2, 3, 5
I have no idea what is causing this. I am posting the entire source code below (atleast the functions which have to do with the tests where the error must hide). There is some documentation in the file and an example of the error in case I haven't explained things clearly.
#include <iostream>
using namespace std;
template <typename Object>
class ArrayVector {
private:
int capacity; // capacity
int sz; // number of elements
Object* a;
int f; // start of the indexes
int b; // end of the indexes
public:
ArrayVector(int initCap);
~ArrayVector();
int size() const { return sz; }
bool isEmpty() const { return size() == 0; }
Object elemAtRank(int r);
void pushBack( const Object& e);
void pushFront(const Object& e);
void popBack();
void popFront();
};
template <typename Object> // constructor
ArrayVector<Object>::
ArrayVector(int initCap) {
capacity = initCap;
sz = 0;
a = new Object[capacity];
f = 0;
b = 0;
}
template <typename Object> // gets the element at a certain rank
Object ArrayVector<Object>:: elemAtRank(int r)
{
return a[(f + r) % sz]; // starting position in real array + r % number of elements
}
template <typename Object>
void ArrayVector<Object>:: pushBack( const Object& e)
{
if(sz == capacity && sz > 0) // if the array is full time to spin it
{
if(f == capacity){ // Handles the front.
f = 0; // if the front is equal to the capacity
// set it to zero, else increment
}else{
f++;
}
if(b == capacity){ //Handles the back
b = 0; //if the back is equal to the capacity
// cout<< "SC insert "<< e << " at "<< b <<endl;
a[b] = e;
}else{ // set it to zero, else increment
a[b] = e;
// cout<< "SC insert "<< e << " at "<< b <<endl;
b++;
}
}else{
a[b] = e;
// cout<< "insert "<< e << " at "<< b <<endl;
b++;
sz++;
}
}
template <typename Object>
void ArrayVector<Object>:: pushFront( const Object& e)
{
if(f == 0){
f = capacity-1;
}else{
f--;
}
a[f] = e;
if(sz< capacity)
sz++;
}
int main()
{
// Fill array and print it
cout << "Fill with numbers" << endl;
ArrayVector<int> asd(3);
asd.pushBack(1);
asd.pushBack(2);
asd.pushBack(3);
for(int i =0; i < asd.size(); i++)
cout << asd.elemAtRank(i) << endl;
//Test if it spins
cout << "BEGIN Spin TEST " << endl;
asd.pushBack(4);
cout << "First test is ok" << endl;
for(int i =0; i < asd.size(); i++)
cout << asd.elemAtRank(i) << endl;
// here the error comes
asd.pushBack(5);
cout << "On the second iteration things crash and burn" << endl;
for(int i =0; i < asd.size(); i++)
cout << asd.elemAtRank(i) << endl;
return 0;
}
In addition to your insertion times not matching your desired requirements, your issue is here:
template <typename Object>
void ArrayVector<Object>:: pushFront( const Object& e)
{
if(f == 0)
{
f = capacity-1;
}
else
{
f--;
}
a[f] = e; // problem lies here!
if(sz < capacity)
sz++;
}
You are figuring out where to do the insert, but you are not pushing the other elements around the vector; that is, you are just overwriting the element at the insertion point. If you wanted to push it onto the front, you would need to copy the other elements over 1 position each and then do your insert. A better solution (which would match your constant insertion time requirement) would be to implement it as a double-linked list. Pushing onto the ends would simply require the following pseudo-code:
void push_front(const Object& o)
{
if (size == capacity)
l.pop_back();
l.push_front(o);
}
If you really must use a contiguous memory block, you will not get constant-time insertion, but it would look like this:
// Assumptions: 0 is always the front, capacity-1 is always the maximum back
template <typename Object>
void ArrayVector<Object>:: pushFront( const Object& e)
{
// assume capacity > 0, move all the elements to the right one slot
for (int i = capacity - 1; i > 0; --i)
{
a[i] = a[i - 1];
}
a[0] = e;
if(sz < capacity)
sz++;
}
I am trying to simulate Conway's game of life using an implementation file I created, I have made good progress but unfortunately I am getting an error which confuses me. I think the problem is ignorance on my part of how to properly code templated functions, anyways this is my implementation file:
#include <list>
#ifndef HashTable_h
#define HashTable_h
using namespace std;
#define HASHTABLE_CAPACITY 1009
template <class DataType>
class HashTable
{
public:
HashTable(); // constructor
bool insert(DataType &a); // insert function for inserting value of dataType into table
bool retrieve(DataType &a); // retrieve function for retrieving value from table
bool replace(DataType &a); // function for replacing the value from the table with the parameter
bool remove(DataType& a);//removed function written and checked
//int getSizeOf() const;
void clear(); // for clearing the table
int size() const;
private:
list<DataType> table[HASHTABLE_CAPACITY]; // static array
int count;
int currentIndex;
typename list<DataType>::const_iterator it;
};
// constructor
template <class DataType>
HashTable<DataType>::HashTable()
{
list<DataType> table[HASHTABLE_CAPACITY];
count = 0;
currentIndex = -1;
}
// retrieve function
template <class DataType>
bool HashTable<DataType>::retrieve(DataType &a)
{
// get wrapped index
int wrappedIndex = a.hashCode() % HASHTABLE_CAPACITY;
if (wrappedIndex < 0) wrappedIndex = wrappedIndex + HASHTABLE_CAPACITY;
// if the array location isn't occupied, fail
if (wrappedIndex < 0 || wrappedIndex >= HASHTABLE_CAPACITY || table[wrappedIndex].empty()) return false;
// iterator for traversing table values
typename list<DataType>::const_iterator it;
// if the keys match then replace the data
// if a collision occurs then return false
it = find(table[wrappedIndex].begin(), table[wrappedIndex].end(), a);
if(it == table[wrappedIndex].end())
return false;
a = *it;
return true;
}
// overloaded operator function
// function for inserting values
template <class DataType>
bool HashTable<DataType>::insert(DataType &value)
{
// get wrapped index
int wrappedIndex = value.hashCode() % HASHTABLE_CAPACITY;
if (wrappedIndex < 0) wrappedIndex = wrappedIndex + HASHTABLE_CAPACITY;
// iterator for traversing values in table
typename list<DataType>::iterator it;
// if array location is not "occupied", copy into array
// else if keys match, replace the data
if (table[wrappedIndex].empty())
{
table[wrappedIndex].push_back(value);
count++;
return true;
}
else
{
it = find(table[wrappedIndex].begin(), table[wrappedIndex].end(), value);
if (it != table[wrappedIndex].end()) *it = value;
else {table[wrappedIndex].push_back(value); count++;}
}
return true;
}
// function for replacing values
template <class DataType>
bool HashTable<DataType>::replace(DataType &value)
{
// get wrapped index
int wrappedIndex = value.hashCode() % HASHTABLE_CAPACITY;
if (wrappedIndex < 0) wrappedIndex = wrappedIndex + HASHTABLE_CAPACITY;
if(table[wrappedIndex].empty()) return false;
// iterator for traversing the values in table
typename list<DataType>::const_iterator it;
it = find(table[wrappedIndex].begin(), table[wrappedIndex].end(), value);
if(it == table[wrappedIndex].end()) return false;
value = *it;
table[wrappedIndex].erase(it);
count--;
return true;
}
template <class DataType>
bool HashTable<DataType>::remove(DataType &value)
{
// get wrapped index
int wrappedIndex = value.hashCode() % HASHTABLE_CAPACITY;
if (wrappedIndex < 0) wrappedIndex = wrappedIndex + HASHTABLE_CAPACITY;
if(table[wrappedIndex].empty()) return false;
// iterator for traversing the values in table
typename list<DataType>::iterator it;
// if array location is not "occupied", copy into array
// else if keys match, remove the data
it = find(table[wrappedIndex].begin(), table[wrappedIndex].end(), value);
if(it == table[wrappedIndex].end()) return false;
value = *it;
table[wrappedIndex].erase(it);
count--;
return true;
}
// function for clearing the table of it's values
template <class DataType>
void HashTable<DataType>::clear()
{
count = 0;
currentIndex = -1;
for(int i = 0; i < HASHTABLE_CAPACITY; i++)
if( !table[i].empty()) table[i].clear();
}
template <class DataType>
int HashTable<DataType>::size() const
{
return count;
}
#endif
And this is the actual Game Of Life driver file:
// Lab 11b
#include <iostream>
using namespace std;
struct cell
{
int value; // equal to 1, so 0,0 is not a blank
int row; // any +/0/- value
int col; // any +/0/- value
bool operator==(const cell& c) const {return row == c.row && col == c.col;}
bool operator<(const cell& c) const {return (1000000 * row + col) < (1000000 * c.row + c.col);}
int hashCode() const {return 31 * row + col;}
};
#include "HashTable.h"
HashTable<cell> grid;
HashTable<cell> newGrid;
const int MINROW = -25;
const int MAXROW = 25;
const int MINCOL = -35;
const int MAXCOL = 35;
int neighborCount(int row, int col)
{
cell temp;
int count = 0;
for (temp.row = row - 1; temp.row <= row + 1; temp.row++)
for (temp.col = col - 1; temp.col <= col + 1; temp.col++)
if (temp.row != row || temp.col != col)
if (grid.retrieve(temp))
++count;
return count;
}
void initialize()
{
cout << "List the coordinates for living cells.\n";
cout << "Terminate the list with a special pair -1 -1\n";
cell temp;
while (true)
{
cin >> temp.row >> temp.col;
if (temp.row == -1 && temp.col == -1) break;
grid.insert(temp);
}
cin.ignore();
}
void print()
{
cell temp = {1};
cout << "\nThe current Life configuration is:\n";
for (temp.row = MINROW; temp.row <= MAXROW; temp.row++)
{
for (temp.col = MINCOL; temp.col <= MAXCOL; temp.col++)
if (grid.retrieve(temp))
cout << '*';
else
cout << ' ';
cout << endl;
}
cout << endl;
}
void update()
{
cell temp = {1};
newGrid.clear();
for (temp.row = MINROW; temp.row <= MAXROW; temp.row++)
for (temp.col = MINCOL; temp.col <= MAXCOL; temp.col++)
switch (neighborCount(temp.row, temp.col))
{
case 2:
if (grid.retrieve(temp)) newGrid.insert(temp);
break;
case 3:
newGrid.insert(temp);
break;
}
grid = newGrid;
};
int main()
{
cout << "Welcome to Conway's game of Life\n";
cout << "This game uses a grid in which\n";
cout << "each cell can either be occupied by an organism or not.\n";
cout << "The occupied cells change from generation to generation\n";
cout << "according to the number of neighboring cells which are alive.\n";
initialize();
print();
for (int i = 1; grid.size(); i++)
{
cout << "Generation " << i << ". Press ENTER to continue, X-ENTER to quit...\n";
if (cin.get() > 31) break;
update();
print();
}
return 0;
}
When I try to compile these files I get this error:
In file included from GameOfLife.cpp:16:
HashTable.h: In member function ‘bool HashTable<DataType>::retrieve(DataType&) [with DataType = cell]’:
GameOfLife.cpp:32: instantiated from here
HashTable.h:74: error: no matching function for call to ‘find(std::_List_iterator<cell>, std::_List_iterator<cell>, cell&)’
HashTable.h: In member function ‘bool HashTable<DataType>::insert(DataType&) [with DataType = cell]’:
GameOfLife.cpp:47: instantiated from here
HashTable.h:117: error: no matching function for call to ‘find(std::_List_iterator<cell>, std::_List_iterator<cell>, cell&)’
What could be the issue here?
You need to #include <algorithm> to get std::find. This is presumably what you want to use when you call find. You should avoid using namespace std, specially in headers.