I'm making a C++ program "Hash Table"
I've spent 2 days debugging this piece of code but I still don't understand what's wrong.
I'm using xCode and it says "Build Failed" and gives this message
Thread 1:EXC_BAD_ACCESS (code=1, address=0x110)
Here is the pieces of my code, I'd really appreciate your any help!
hash.cpp
#include "hash.h"
size_t HashTable::hash_s(const Key &k){ /* simple hash function just to see how collisions are handled */
return (k.length());
}
HashTable::HashTable(): size_(0), numberOfLoaded(0){} /* constructor */
HashTable::~HashTable() { /* destructor */
delete [] hashTable;
}
void HashTable::allocate(){ /* function for memory allocation */
if (empty()){ /* if hashTable is empty, allocate memory for one element */
size_ = 1;
hashTable = new hashTableElement[size_];
hashTable->k = nullptr;
hashTable->value = nullptr;
return;
} else { /* if hashTable is not empty (at least has 1 element */
hashTableElement *newHashTable = new hashTableElement[size_*2]; /* allocate memory for new double-sized array */
for (int i = 0; i < size_; i++ ){
if (hashTable[i].k != nullptr){ /* copy all not empty array elements into the new double-sized array */
size_t newIndex = hash_s(*(hashTable[i].k)) % (size_*2); /* count new index for double-sized array */
newHashTable[newIndex] = hashTable[i];
}
}
delete [] hashTable; /* delete memory from small table */
size_ *= 2;
hashTable = newHashTable;
}
}
bool HashTable::insert(const Key &k, const Value &v){
allocate(); /* call allocation function */
size_t index = hash_s(k) % size_; /* index is a reminder of hash_s divided by size of the table */
hashTableElement *currentElement = &hashTable[index]; /* set a pointer to the currentElement */
/*problem is here EXC_BAD ACCESS (code=1, address=0x110)*/ while (currentElement->next) currentElement = currentElement->next; /* move to the last element in collision chain */
if ((currentElement->value != nullptr) || (currentElement != &hashTable[index])){ /* allocate memory for next element */
currentElement->next = new hashTableElement;
currentElement = currentElement->next;
}
currentElement->value = new Value; /* allocate memory for structure */
currentElement->k = new Key; /* allocate memory for key */
*(currentElement->k) = k; /* set to new element key value the value of inserted element */
*(currentElement->value) = v; /* set to new element value */
numberOfLoaded++; /* increment counter of inserted elements */
return true;
}
bool HashTable::empty() const{
for (int i = 0; i < size_; i++){
if( hashTable[i].value != nullptr ){ /* if in the table at least one element inserted, return false */
return false;
}
}
return true;
}
main.cpp
#include "hash.h"
#include <fstream>
#include <iostream>
using namespace std;
void HashTable::print(){ /* function that prints entire table */
for (int i = 0; i < size_; i++){
hashTableElement *newelement = &hashTable[i]; /* set a pointer to current element */
while ((newelement) && (newelement->value != nullptr)){ /* printing all collisions chain */
cout << *(newelement->k) << " " << newelement->value->age << " " << newelement->value->weight << endl;
cout << "I = " << i << " Number Of loaded = " << numberOfLoaded << " Size of Table = "<< size_ <<endl;
cout << "_____________________"<<endl;
newelement = newelement->next; /* go to the next element in the chain */
}
}
}
int main(int argc, const char * argv[]) {
HashTable hashObj;
string name;
Value val;
Key k;
ifstream ins("/Users/Aeon/Documents/Study/NSU/Programming/Lab Works/2_Level/2_hash_table/Realization/input2.txt");
if (!ins.is_open()){
cout << "File can't be open!\n"<<endl;
} else {
while (ins >> k){ /* read a key (which is person's name) */
ins >> val.age; /* read an age */
ins >> val.weight; /* read a weight */
hashObj.insert(k, val); /* call insert function */
}
}
hashObj.print(); /* print an entire table */
return 0;
}
hash.h
#include <algorithm>
#include <stdlib.h>
#include <string>
using namespace std;
typedef std::string Key;
struct Value {
unsigned int age;
unsigned int weight;
};
struct hashTableElement {
Value *value;
Key *k;
hashTableElement *next;
};
class HashTable {
private:
unsigned int numberOfLoaded;
size_t size_;
hashTableElement *hashTable;
void allocate(); /* function for memory allocation */
public:
HashTable(); /* constructor */
~HashTable(); /* destructor */
size_t hash_s(const Key &k); /* simple hash function */
void print(); /* function prints entire table */
bool insert(const Key& k, const Value& v); /* function inserts an element into the table */
bool empty() const; /* function returns true if HashTable is empty, false if not */
};
#endif /* defined(__Lab_Hash__hash__) */
Also, I just uploaded my test files:
input2
input
Thank you very much!
Related
I have a project making a stem and leaf plot with linked lists and I keep getting errors that I don't even understand. How do you make a linked list of linked lists? I have to do it this way for the assignment so I can't do it in an alternate way and I have to work from the files below. My top stem linked list works, but it doesn't seem to be accessing the leaf portion and I'm getting the following error:
"The instruction at 0x0000000000404050 referenced at 0x000000000000001E. The memory could not be read."
Here is my code:
* Driver.cpp
*/
#include <iostream>
#include <string>
#include <iomanip>
#include <fstream>
using namespace std;
#include "StemAndLeaf2.h"
int main()
{
ifstream fin("input.txt");
int x;
unsigned short leafunit, stemunit;
fin >> leafunit >> stemunit;
StemAndLeaf one;
one.setLeafUnit(leafunit);
one.setStemUnit(stemunit);
while (fin >> x)
{
one.addTerm(x);
cout << "Insert: " << x << endl;
cout << "StemAndLeafPlot: " << endl;
cout << one << endl;
}
cout << "The smallest term: " << one.getSmallestTerm() << endl;
cout << "The largest term: " << one.getLargestTerm() << endl;
return 0;
}
/*
* SteamAndLeaf2.h
*/
#ifndef STEAMANDLEAF2_H_
#define STEAMANDLEAF2_H_
#include <math.h>
#include <list>
#include <ostream>
// DO NOT REMOVE THE LINE BELOW
/* CMSC341_TEST_INCLUDES */
#include "Stem2.h"
class StemAndLeaf
{
// DO NOT REMOVE THE LINE BELOW
/* CMSC341_FRIEND_CLASSES */
public:
StemAndLeaf(){FRONT_ptr = NULL; REAR_ptr = NULL;};
~StemAndLeaf()
{
while(FRONT_ptr != NULL)
{
Cursor = FRONT_ptr;
FRONT_ptr = Cursor->getNext();
delete Cursor;
}
}
unsigned short getLeafUnit() const { return leafUnit; }
void setLeafUnit(unsigned short leafUnit) { this->leafUnit = leafUnit; }
unsigned short getStemUnit() const { return stemUnit; }
void setStemUnit(unsigned short stemUnit) { this->stemUnit = stemUnit; }
/**
* #brief Get the smallest term in the datastructure. The term MUST be reconstructed
*
* #return unsigned short
*/
unsigned short getSmallestTerm() const
{
return ((FRONT_ptr->getValue()*10))+(FRONT_ptr->findSmallestValue()));
}
/**
* #brief Get the largest term in the datastructure. The term MUST be reconstructed
*
* #return unsigned short
*/
unsigned short getLargestTerm() const
{
return ((REAR_ptr->getValue()*10))+(REAR_ptr->findLargestValue()));
}
/**
* #brief Adds the term to the data structure. Insertion MUST be ordered, both for Stem and the Leaf
*
* #param term the value to add
*/
void addTerm(unsigned short term)
{
Insert = new Stem();
Insert->setValue(term/10);
Insert->setLeafList(Insert->getLeafLink());
if(isEmpty())
{
Insert->setLeafCount(1);
Insert->insert(term%10);
FRONT_ptr = Insert;
REAR_ptr = Insert;
}
else
{
for(Cursor = FRONT_ptr; Cursor != NULL; Prev = Cursor, Cursor = Cursor->getNext())
{
Insert->setLeafCount(1);
Insert->insert(term%10);
if((Cursor == FRONT_ptr) && (Cursor->getValue() > Insert->getValue()))
{
Insert->setNext(Cursor);
FRONT_ptr = Insert;
}
else if((Cursor == REAR_ptr) && (Cursor->getValue() < Insert->getValue()))
{
Cursor->setNext(Insert);
REAR_ptr = Insert;
}
else if((Prev->getValue() < Insert->getValue()) && (Cursor->getValue() > Insert->getValue()))
{
Prev->setNext(Insert);
Insert->setNext(Cursor);
}
else if(Insert->getValue() == Cursor->getValue())
{
if(!Cursor->insert(term%10))
{
Cursor->incrementLeafCount();
}
}
break;
}
}
}
/*
* Used to display/debug the StemAndLeaf diagram
* will look exactly like this (using example in project):
*
* 0 | 0:1
* ...
* 2 |
* 3 | 6:2 9:1
*
* You will not be given too much data to overrun 80 characters
*/
friend std::ostream &operator<<(std::ostream &out, const StemAndLeaf &x)//;
{
out << x.FRONT_ptr->getValue() << " | ";
return out;
}
private:
unsigned short leafUnit; // increment of each leaf in this particular structure
unsigned short stemUnit; // increment of each stem in this particular structure
Stem *FRONT_ptr, *REAR_ptr, *Insert, *Prev, *Cursor;
inline bool isEmpty() const { return FRONT_ptr == nullptr; }
};
#endif /* STEAMANDLEAF2_H_ */
/*
* Steam2.h
*/
#ifndef STEM2_H_
#define STEM2_H_
#include <ostream>
#include <list>
// DO NOT REMOVE THE LINE BELOW
/* CMSC341_TEST_INCLUDES */
#include "Leaf2.h"
class Stem
{
// DO NOT REMOVE THE LINE BELOW
/* CMSC341_FRIEND_CLASSES */
public:
Stem(){FRONT_ptr = NULL; REAR_ptr = NULL;}
virtual ~Stem(){};
unsigned short getLeafCount() const { return m_LeafCount; }
void setLeafCount(unsigned short LeafCount) { m_LeafCount = LeafCount; }
void incrementLeafCount() { m_LeafCount++; }
void decrementLeafCount() { m_LeafCount--; }
Leaf *getLeafLink() const {return FRONT_ptr;}
void setLeafList(Leaf *LeafList) { FRONT_ptr = LeafList; }
unsigned short getValue() const { return m_Value; }
void setValue(unsigned short value) { m_Value = value; }
Stem *getNext() const { return m_Next; }
void setNext(Stem *next) { m_Next = next; }
/**
* #brief Add the value to the stem. Returns trues if a new lef was created
*
* #param value The number to insert
* #return true if a new leaf was created
* #return false if we've seen the value before
*/
bool insert(unsigned short value)
{
Insert = new Leaf();
Insert->setValue(value);
if(FRONT_ptr = NULL)
{
FRONT_ptr = Insert;
REAR_ptr = Insert;
return true;
}
else
{
for(Cursor = FRONT_ptr; Cursor != NULL; Prev = Cursor, Cursor = Cursor->getNext())
{
if(Cursor->getValue() == Insert->getValue())
{
Cursor->incrementCount();
return false;
}
else
{
if((Cursor == FRONT_ptr) && (Cursor->getValue() > Insert->getValue()))
{
Insert->setNext(Cursor);
FRONT_ptr = Insert;
}
else if((Cursor == REAR_ptr) && (Cursor->getValue() < Insert->getValue()))
{
Cursor->setNext(Insert);
REAR_ptr = Insert;
}
else if((Prev->getValue() < Insert->getValue()) && (Cursor->getValue() > Insert->getValue()))
{
Prev->setNext(Insert);
Insert->setNext(Cursor);
}
return true;
}
}
}
}
unsigned short findSmallestValue()
{
if(FRONT_ptr != NULL){
return FRONT_ptr->getValue();}
else {return 0;}
}
unsigned short findLargestValue()
{
if(REAR_ptr != NULL){
return REAR_ptr->getValue();}
else {return 0;}
}
private:
// they are given the code for a L.L.
Leaf *FRONT_ptr, *REAR_ptr, *Insert, *Prev, *Cursor;
/**
* Stem Value
*/
unsigned short m_Value;
/**
* How many leaves in this stem
*/
unsigned short m_LeafCount;
/**
* Pointer to the next stem
*/
Stem *m_Next;
};
#endif /* STEM2_H_ */
/*
*Leaf2.h
*/
#ifndef LEAF2_H_
#define LEAF2_H_
#include <ostream>
// DO NOT REMOVE THE LINE BELOW
/* CMSC341_TEST_INCLUDES */
struct Leaf
{
// DO NOT REMOVE THE LINE BELOW
/* CMSC341_FRIEND_CLASSES */
Leaf()
: m_Count(1)
{
}
unsigned short getValue() const { return m_Value; }
void setValue(unsigned short value) { m_Value = value; }
unsigned short getCount() const { return m_Count; }
void setCount(unsigned short count) { m_Count = count; }
void incrementCount() { m_Count++; }
void decrementCount() { m_Count--; }
Leaf *getNext() const { return m_Next; }
void setNext(Leaf *next) { m_Next = next; }
/**
* #brief Operator overload to print the Leaf in a nice format According to
* the project description the format should be "value:count"
*
* #param out
* #param leaf
* #return std::ostream&
*/
friend std::ostream &operator<<(std::ostream &out, const Leaf &leaf)
{
out << leaf.m_Value << ":" << leaf.m_Count;
return out;
}
private:
unsigned short m_Value;
unsigned short m_Count;
Leaf *m_Next;
};
#endif /* LEAF2_H_ */
The input file is a .txt file with the following values:
1
10
22
13
8
23
I get the error even if I turn everything after the "add term" call into a comment, and I know the leaf portion isn't working because when I run the "findSmallestTerm" and "findLargestTerm" it only gives me the stem value. There's a lot wrong with my code beyond just the error with the leaf values but I'm not asking for the answer to the whole project, I just need help on getting the stem and leaf lists to connect, if that makes sense. Sorry to just dump all of the code here, but I don't know which part of it is the problem so I didn't know what would be relevant or not.
UPDATE: I deleted some of the unnecessary code and added the info for the input file.
The crash is caused by uninitialized data members and wrong check statements:
if(FRONT_ptr = NULL)
This should be:
if(FRONT_ptr == NULL)
Stem::m_Next should be inited:
Stem *m_Next=NULL;
Stem::m_Next should be inited:
Leaf *m_Next=NULL;
The variable Prev should be inited after entering the for loop:
void addTerm(unsigned short term) {
// Prev should be inited or the crash happens
for (Prev = Cursor, Cursor = FRONT_ptr; Cursor != NULL;
Prev = Cursor, Cursor = Cursor->getNext()) {
}
}
The Stem class should have destructors to avoid memory leak:
virtual ~Stem() {
while (FRONT_ptr != NULL) {
Cursor = FRONT_ptr;
FRONT_ptr = Cursor->getNext();
delete Cursor;
}
};
The code also have other some bad smell and bugs:
Temporary variables should not be data members: INsert, Prev, Cursor
Stem::Insert, StemAndLeaf::Insert have memory leaks when the data have existed in the list, the new elements haven't been inserted into the list, the pointer should be deleted
Forget to check if pointer is NULL before accessing it: getSmallestTerm, getLargestTerm
so the code, basically an AList with multiple functions, I have below currently runs fine and my passes all assertions and test cases that are assigned to my assignment, however, I lose .5 points because of a memory error. I believe the error lies somewhere in my copy constructor, but I am currently struggling to know where is the issue.
This is how my code looks along with my copy constructor.
#ifndef __ALIST_H__
#define __ALIST_H__
// size should not be negative
typedef unsigned long size_t;
#define RFACTOR 2 // use factor 2
namespace ds {
template <typename ItemType> class TestDriver; // for autograding; please ignore
/** Array-based list. */
template <typename ItemType> class AList {
friend class TestDriver<ItemType>; // for autograding; please ignore
private:
/** The underlying array. */
ItemType *items;
/** Stores the current size of the list. */
size_t count;
/** Max number of items allowed. */
size_t maxCnt;
/** Resize the underlying array to the target capacity. */
void resize(size_t capacity) {
maxCnt = capacity;
ItemType *a = new ItemType[maxCnt];
for (size_t i = 0; i < count; i++) {
a[i] = items[i];
}
delete[] items;
items = a;
}
public:
/**
* Construct a new AList object.
*
* #param initSize initial size of the underlying array; default 100
*/
AList(size_t initSize = 100) {
count = 0;
maxCnt = initSize;
items = new ItemType[maxCnt];
}
/** Destroy the AList object. */
~AList() { delete[] items; }
/** Return the number of elements in list. */
size_t size() const { return count; }
/** Return the i-th item in list .*/
ItemType &get(int i) const { return items[i]; }
/** Append `x` to the end of list. */
void addLast(ItemType x) {
if (count == maxCnt) {
resize(count * RFACTOR);
}
items[count] = x;
count += 1;
}
/** Return the last item in list. */
ItemType &getLast() const { return items[count - 1]; }
/** Delete and return the last item. */
ItemType removeLast() {
ItemType returnItem = getLast();
count -= 1;
return returnItem;
}
AList(const AList<ItemType> &other);
void addFirst(ItemType x);
ItemType &getFirst() const;
ItemType removeFirst();
};
/** Copy constructor. */
template <typename ItemType>
AList<ItemType>::AList(const AList<ItemType> &other) {
// TODO: create a list that is identical to `other`
count = other.count;
maxCnt = other.maxCnt;
ItemType arr[maxCnt];
items = new ItemType[maxCnt];
for(size_t i = 0; i < count; i++)
{
items[i] = arr[i];
}
delete [] items;
items = other.items;
}
/** Insert x at the front of list. */
template <typename ItemType> void AList<ItemType>::addFirst(ItemType x) {
// TODO:
if(count == maxCnt)
{
resize(count * RFACTOR);
}
for(size_t i = count; i > 0; i--)
{
items[i] = items[i - 1];
}
items[0] = x;
count = count + 1;
}
/** Return the first element in list. */
template <typename ItemType> ItemType &AList<ItemType>::getFirst() const {
// TODO:
return items[0];
}
/** Delete and return the first element in list. */
template <typename ItemType> ItemType AList<ItemType>::removeFirst() {
// TODO:
ItemType temp = items[0];
for(size_t i = 0; i < count - 1; i++)
{
items[i] = items[i + 1];
}
count = count - 1;
return temp;
}
} // namespace ds
#endif // __ALIST_H__
When I run my code, this is what the memory error states:
Output: 'original list: [1]; copy & getFirst: 1; resulting list: [1]'
217Expected: 'original list: [1]; copy & getFirst: 1; resulting list: [1]'
218Error(s):
219==190== Invalid free() / delete / delete[] / realloc()
220==190== at 0x483D74F: operator delete[](void*) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
221==190== by 0x10979A: ds::AList<int>::~AList() (AList.h:51)
222==190== by 0x10962E: main (test_driver.cpp:55)
223==190== Address 0x4da3cc0 is 0 bytes inside a block of size 4 free'd
224==190== at 0x483D74F: operator delete[](void*) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
225==190== by 0x10979A: ds::AList<int>::~AList() (AList.h:51)
226==190== by 0x109608: main (test_driver.cpp:96)
227==190== Block was alloc'd at
228==190== at 0x483C583: operator new[](unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
229==190== by 0x109761: ds::AList<int>::AList(unsigned long) (AList.h:47)
230==190== by 0x10939F: main (test_driver.cpp:55)
231==190==
main program:
#define CATCH_CONFIG_MAIN
#include "AList.h"
#include "catch.hpp"
#include <cstdlib>
#define SIZE 5
TEST_CASE("All") {
ds::AList<int> L;
// randomly add SIZE ints to the array
int nums[SIZE];
srand(time(0)); // setting the seed for rand()
for (int i = 0; i < SIZE; i++) {
nums[i] = rand() % 20 + 1; // generating random numbers by rand()
L.addLast(nums[i]);
}
SECTION("copy constructor") {
ds::AList<int> *K = new ds::AList<int>(L);
CHECK(L.size() == K->size());
CHECK(K->getLast() == nums[SIZE - 1]);
delete K; // this should not also delete L
}
SECTION("addFirst") {
L.addFirst(123);
L.addFirst(234);
CHECK(L.getFirst() == 234);
CHECK(L.get(2) == nums[0]);
}
SECTION("removeFirst") {
int x = L.removeFirst();
CHECK(x == nums[0]);
CHECK(L.getLast() == nums[SIZE - 1]);
}
}
If anyone could give any tips or suggestions on how I can fix this, it would be greatly appreciated!
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...
I am working on a project which I have to include a header file to my main.cpp. The header file is a heap which is using a template file. For reasons that escape me the insert and remove functions cannot be "seen" in the main file. I am getting an error message: C:/Users/Tito/Documents/C++proj/cs3304/Homework2_2/Homework10/main.cpp:58:17: error: request for member 'remove' in 'enter1', which is of non-class type 'priority_queue_heap()'. Can someone please tell me where I am going wrong? I will really appreciate it.
Thanks
Here are the lines of code:
Main.cpp:
/**
* Insert a few elements into a heap and the remove them
* one by one and see if we get them in the right.
*/
#include "priority_queue_heap.h"
#include "heap.h"
#include <iostream>
#include <ctime>
using namespace std;
int test1() {
heap<int> hp;
hp.insert(1);
hp.insert(2);
hp.insert(3);
hp.insert(4);
hp.insert(5);
hp.check_heap();
int x = hp.remove();
cout << "removed " << x << endl;
x = hp.remove();
cout << "removed " << x << endl;
x = hp.remove();
cout << "removed " << x << endl;
x = hp.remove();
cout << "removed " << x << endl;
x = hp.remove();
cout << "removed " << x << endl;
cout << "empty? " << hp.is_empty() << endl;
}
void test2() {
srand(time(NULL));
heap<int> hp;
for(int i = 0; i < 30; i++ ) {
hp.insert(rand());
}
while(!hp.is_empty()) {
int x = hp.remove();
cout << x << endl;
}
}
int main() {
/*test1();
test2();*/
priority_queue_heap<int> enter1();
enter1.insert(135);
enter1.insert(909);
enter1.insert(203);
cout<<endl;
cout<< "values to be removed" << endl;
cout << enter1.remove() << endl;
}
heap.h:
#ifndef HEAP_H
#define HEAP_H
/**
* This class implements a heap as described in the text.
* We will treat it as a priority queue.
*/
template <class T>
class heap {
public:
static const int CAPACITY = 10;
heap() {
size = 0;
}
bool is_empty() const { return size == 0;}
bool is_full() const { return size == CAPACITY; }
/**
* Remove the largest value from this heap and return it.
*
* Precondition: heap is not empty.
*/
T remove();
/**
* Inserts the 'value' into the heap.
*
* Precondition: heap is not full
*/
void insert(const T& value);
/**
* Check if the heap is valid.
* Prints out each parent and its children (for all nodes with children)
* Stops when a parent is less than one or both of its children
* Prints 'check' for each parent that is greater than or equal to its
children
*/
bool check_heap();
private:
T data[CAPACITY];
int size;
};
#include "heap.template"
#endif // HEAP_H
heap.template:
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <iomanip>
/*
* parent index is p, children are at indices 2*p+1 and 2*p+2
* You must check that those are in range
*
* child index is c, parent index is (c-1)/2 (integer division)
*/
/**
* Inserts the 'value' into the heap.
*
* Precondition: heap is not full
*/
template <class T>
void heap<T>::insert(const T& value) {
assert(!is_full());
//std::cout << size << std::endl;
// add the value to a new node in proper position
data[size] = value;
size++;
// move the value up the tree as needed
int child = size-1; // index of the new 'node'
int parent = (child-1)/2; // index of the parent
while((child > 0) && (data[parent] < data[child])) {
// swap parent and child values
T tmp = data[parent];
data[parent] = data[child];
data[child] = tmp;
// update parent and child
child = parent; // this is where new value is!
parent = (child-1)/2;
}
// it's a heap!
}
/**
* Remove the largest value from this heap and return it.
*
* Precondition: heap is not empty.
*/
template <class T>
T heap<T>::remove() {
assert(!is_empty());
// grab first element, save it for return later
T save = data[0];
// copy last value in list to the beginning
// decrement size
data[0] = data[size-1];
size--;
// size--;
// data[0] = data[size];
// sift the new first element down until it finds its place
int parent = 0;
int left_child = 2*parent+1;
int right_child = 2*parent+2;
bool still_working = true;
while(still_working && left_child < size) { // while the parent has at
least one child
if(right_child >= size) {
// only the left child to worry about
if(data[parent] < data[left_child]) {
// out of order, so swap them
T t = data[parent];
data[parent] = data[left_child];
data[left_child] = t;
parent = left_child;
still_working = false; // we must be done!
} else {
still_working = false;
}
} else {
// two children
if(data[left_child] > data[right_child]) {
//left child larger
if(data[parent] < data[left_child]) {
// out of order, so swap them
T t = data[parent];
data[parent] = data[left_child];
data[left_child] = t;
parent = left_child;
} else {
still_working = false;
}
} else {
// right child larger
if(data[parent] < data[right_child]) {
// out of order, so swap them
T t = data[parent];
data[parent] = data[right_child];
data[right_child] = t;
parent = right_child;
} else {
still_working = false;
}
}
left_child = 2*parent + 1;
right_child = 2*parent + 2;
}
}
return save;
}
/**
* Check if the heap is valid.
* Prints out each parent and its children (for all nodes with children)
* Stops when a parent is less than one or both of its children
* Prints 'check' for each parent that is greater than or equal to its
children
*/
template <class T>
bool heap<T>::check_heap() {
for(int p = 0; p < size; p++ ) {
int cl = 2*p+1;
int cr = 2*p+2;
std::cout << std::setw(5) << p << std::setw(10) << data[p];
if(cl < size) { // p has a left child?
std::cout << std::setw(10) << data[cl];
if(data[p] < data[cl]) {
std:exit(1);
}
}
if(cr < size) { // p has a right child?
std::cout << std::setw(10) << data[cr];
if(data[p] < data[cr])
std::exit(1);
}
std::cout << std::endl;
}
return true;
}
priority_queue_simple.template:
#include <cassert>
/**
* Remove the largest value from this priority queue and return it.
*
* Precondition: priority queue is not empty.
*/
template <class T>
T priority_queue_simple<T>::remove() {
assert(size > 0);
int imax = 0;
for(int i = 1; i < size; i++ ) {
if(data[i] > data[imax])
imax = i;
}
T tmp = data[imax];
data[imax] = data[size-1];
size--;
return tmp;
}
/**
* Inserts the 'value' into the priority queue.
*
* Precondition: priority queue is not full
*/
template <class T>
void priority_queue_simple<T>::insert(const T& value) {
assert(size < CAPACITY);
size++;
data[size-1] = value;
}
priority_queue_heap.h:
#ifndef PRIORITY_QUEUE_HEAP_H
#define PRIORITY_QUEUE_HEAP_H
//#include "heap.h"
template <class T>
class priority_queue_heap {
priority_queue_heap();
bool is_empty() const;
bool is_full() const;
/**
* Remove the largest value from this priority queue and return it.
*
* Precondition: priority queue is not empty.
*/
T remove();
/**
* Inserts the 'value' into the priority queue.
*
* Precondition: priority queue is not full
*/
void insert(const T& value);
private:
heap<T> pri_que;
};
#include "priority_queue_heap.template"
#endif // PRIORITY_QUEUE_HEAP_H
template <class T>
T priority_queue_heap<T>::remove()
{
return pri_que.remove();
}
priority_queue_heap.template:
template <class T>
T priority_queue_heap<T>::remove()
{
return pri_que.remove();
}
template <class T>
void priority_queue_heap<T>::insert(const T& value)
{
pri_que.insert(value);
}
priority_queue_simple.h:
#ifndef PRIORITY_QUEUE_SIMPLE_H
#define PRIORITY_QUEUE_SIMPLE_H
/**
* This class implements a priority queue using a very simple strategy:
* Store the values in an array.
* Add new values at the end.
* When asked to remove a value, search for the largest (linear search)
*
*/
template <class T>
class priority_queue_simple {
public:
static const int CAPACITY = 30;
priority_queue_simple() {
size = 0;
}
bool is_empty() const {
return size == 0;
}
bool is_full() const {
return size == CAPACITY;
}
/**
* Remove the largest value from this priority queue and return it.
*
* Precondition: priority queue is not empty.
*/
T remove();
/**
* Inserts the 'value' into the priority queue.
*
* Precondition: priority queue is not full
*/
void insert(const T& value);
private:
T data[CAPACITY];
int size;
};
#include "priority_queue_simple.template"
#endif // PRIORITY_QUEUE_SIMPLE_H
You should remove the "()" characters after enter1 at line 51 of main.cpp ...
Otherwise c++ sees that as a function, it does not call the constructor.
You have a subtle error in your heap declaration (main.cpp:57):
priority_queue_heap<int> enter1();
Here you are actually declaring a prototype for the enter1 function that takes no argument and returns a priority_queue_heap<int>. Just remove the parentheses to actually declare a variable:
priority_queue_heap<int> enter1;
priority_queue_heap<int> enter1();
Is interpreted by the compiler as a function named enter1 that returns a priority_queue_heap<int> and takes no parameters. When you use
cout << enter1.remove() << endl;
You are trying to call a member function on a name that the compiler interpreted as a function so that is why it tells you it is of non class type. Remove the () from enter1 so you have
priority_queue_heap<int> enter1;
and now enter1 will be an object of type priority_queue_heap<int>
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;
}