Resizing string array in hash-table - c++

I'm learning hashing right now. I am trying to resize my hash-table when it is >=80% filled. But every time i try to resize it, i get undefined behaviour or it crashes.
I tried to make a new String array with more fields and then i deleted the old one but that wasn't working.
hashtable.h
class hashtable
{
public:
hashtable();
void insert(string);
void resize_array();
int hashfunction(string str);
string* getArray();
private:
int elemts_in_array;
int table_size;
string* T;
};
hashtable.cpp
hashtable::hashtable()
{
// your code (start with a capacity of 10)
table_size = 10;
elemts_in_array = 0;
string *array = new string[table_size];
T = array;
}
void hashtable::insert(string key)
{
string* array = getArray();
int hkey=hashfunction(key);
float filled = float(elemts_in_array)/float(table_size);
// When the array is more than 80% filled resize it and double the table_size
if(filled >= 0.8)
{
cout << "Resizing Array.." << endl;
resize_array();
}
for(int i=0; i<table_size;i++)
{
// if the field is empty insert it, else go +1
if(array[(hkey+i)%table_size] == "")
{
array[(hkey+i)%table_size] = key;
elemts_in_array++;
break;
}
if(array[(hkey+i)%table_size] == key)
{
// it is the same element
break;
}
}
}
void hashtable::resize_array()
{
int old_table_size =table_size;
table_size*=2; // double the size of the hashtable
string* old_array= new string[table_size]; // save the old array entries
old_array = T;
// Apply the old entries in old_array
for(int i=0; i<table_size;i++)
{
old_array[i]= T[i];
}
//create a new array with double size
string *new_array = new string[table_size];
//delete the old T
delete[] T;
T = new_array;
//re-hash the old entries into the new array with double size (HERE I GOT THE ISSUES)
for(int i=0; i<table_size/2; i++)
{
insert(old_array[i]);
}
}
sometimes my program went into a loop or it crashed. I really don't know why it is not working.

If you step through your program's execution with your debugger, you will probably spot an issue with your resize_array function.
When it copies the old table entries back to the newly allocated array, it uses the insert function. This has some problems:
you might not get back the same ordering of the original values, due to the collision resolution;
the insert function increases the table size, thus it will end up thinking it has twice as many entries as you originally inserted.
Now, the crash could happen because insert will hit the table-increase limit again. The cycle repeats until you either get a stack overflow or you run out of memory.
The proper way to copy back in your strings is this:
for(int i = 0; i < table_size / 2; i++)
{
T[i] = old_array[i];
}
But then there's another problem that can crash before any of this happens. You first saved your values like this:
for(int i=0; i<table_size;i++)
{
old_array[i]= T[i];
}
Note that table_size has already been doubled and so you are going to access past the end of T. You should have looped on old_table_size instead.
You also have some needless copying. If you are going to reallocate T, then just do this:
void hashtable::resize_array()
{
int old_table_size = table_size;
table_size *= 2;
string* old_T = T;
T = new string[table_size];
for (int i = 0; i < old_table_size; i++)
{
std::swap(T[i], old_T[i]); // or in C++11 assign with std::move
}
delete[] old_T;
}

Related

Appending a dynamic array and doubling its size upon completion

Create a dynamic array of int with a initial space of 4. Write a function ‘append’ that appends a given value to this array. At any stage, if this function finds the array full it automatically doubles the size of array to accommodate this new value. Also write a function to display all the elements of this array. Write a main to test all these functions.
I made an attempt to solve the above question as given below. However, I am unable to get the correct code. Please help me
#include<iostream>
using namespace std;
void append(int*& array, int val, int n, int r)
{
int i,p;
int *array1;
for (i=r;i<n;i++)
array[i] = val;
if(i==n)
{
p = 2*n;
array1 = new int [p];
}
for(int j =0; j<r/2; j++)
array1[j]= array[j];
append(array1, val, p, p/2);
}
int main()
{
int q,p=0,val, n = 4;
int n1 = p/2;
int *array = new int[n];
while(1)
{
cout<<"Enter 0 to end and 1 to continue";
cin>>q;
while(q!=0)
{
cin>>val;
append(array,val,n,n1);
}
}
return 0;
}
I need to solve this without using "Classes". How shall I do it?
Your function needs to do the following:
1) Be able to check if the current append call will result in an out-of-bounds write attempt. So you need something like (and give variables explanatory names like this) this as the first line in your function:
if (array_size < item_count) {
//double size of array
}
To double the size of the array, you have to make a new array with twice the size, copy all the items over from the old array, DELETE the old array, null the old array's pointer, and somehow update the array_size variable (return to main is one option, a static int counter in the function itself is another). You may have to return a pointer to the new array to main, as well. Or maybe you can just re-address the old pointer to the new array AFTER using that pointer to delete the old array. This is all about avoiding memory leaks. So, try to come up with a method declaration like:
int append(int* arrPtr, int value, int array_size, int item_count)
This particular approach means main is getting sent back the array size as an int after each append. So you need something in main like:
array_size = append(array, val, array_size, item_count);
The tricky part will be when you make the new array:
array_size = 2 * array_size;
int* temp = new int[array_size]
//copy everything over from old array to new using arrPtr and temp
for (int i = 0; i < array_size / 2; i++) {
temp[i] = arrPtr[i]
}
//delete the contents of the old array:
delete[] arrPtr;
//pointer gymnastics to redirect arrPtr to the new array:
arrPtr = temp;
temp = nullptr;
//okay, now you are back out of the conditional and can use item_count to insert the
//value into the array (doubled or not)
arrPtr[item_count] = value;
//don't forget to return array_size, and remember main must track item_count as well.
return array_size;
That's the general gist of it. This is not a complete answer, but should give you enough to work with. Basically, most of your code has to be rewritten, and the above is not a complete solution. Good luck.
After taking cue from Double size of dynamic array I have solved it.
#include<iostream>
using namespace std;
void add_element(int* &array, int &size)
{int count = 0;
while(1)
{
int number;
cout << "What number do you want to add? " << endl;
cin >> number;
if (count == size)
{
int newSize = size * 2;
int *newArr = new int[newSize];
for (int i = 0; i < count; ++i)
{
newArr[i] = array[i];
}
delete[] array;
array = newArr;
size = newSize;
}
array[count] = number;
++count;
int k;
cout<<"Do u want to end, then press 0";
cin>>k;
if(k==0) break;
}
for(int g = 0; g<count; g++)
cout<<array[g]<<'\t';
}
int main()
{
int i,j,k,size;
cin>>size;
int* array = new int [size];
add_element(array, size);
}

Writing a private function to expand an array if capacity has run out

I am trying to write a class where it starts with a small array to put values in. My objective is if the number of values exceed the capacity, the array expands to fit the remaining values. For example, if I had 5 numbers, the array would expand to accommodate the fifth value.
The object actually creates an array at run time. So when I wrote the function to expand, its actually creating a new array and pointing to it.
Heres the class
class DVec // self expanding array
{
private:
//[0...n-1] are the values, [0..cap-1] exist
int n;
double* a;
int cap;
void expand(double*);
public:
DVec(); // constructs an empty DVec
void add(double v); //adds v to the end of this DVec
int size();
double pop(); // removes and returns the last value (pre: not-empty)
double get(int idx); // returns value at idx in this DVec
void set(double v, int idx); // sets this DVec at idx to be v
};
Constructor
DVec::DVec()
{
cap = 4;
a = new double[cap];
n = 0;
}
Heres the code that I'm testing it with
void DVec::add(double v)
{
a[n++] = v;
if (n == cap) { expand(a); }
}
void DVec::expand(double*)
{
double* temp = new double[n*2];
for (int i = 0; i < n; i++)
{
temp[i] = a[i];
}
*a = *temp;
}
int main()
{
DVec a; DVec* p = new DVec();
for (int i = 0; i < 6; i++)
{
a.add(i * i);
}
cout << a.get(5);
}
My problem is that it keeps bombing out if I set the for loop to i<6. Anything lower its fine.
It comes back say it threw exception:
if (!has_cctor)
cexit();
My questions is what is this? Is it because I made a pointer equal a pointer(a* = *temp)? Or am I not even in the same galaxy as the correct way to write the function?
Edit:
Thanks for all the help guys. I made changes and it all works now.
{
double* temp = new double[cap++];
for (int i = 0; i < n; i++)
{
temp[i] = a[i];
}
a = temp;
}
DVec::DVec()
{
cap = 0;
a = new double[cap];
n = 0;
}
void DVec::add(double v)
{
if (n >= cap) { expand(a); }
a[n++] = v;
}
This line looks like the problem:
*a = *temp;
I assume you meant to replace a with temp, but what you've written is equivalent to this:
a[0] = temp[0]
You should write this instead:
a = temp;
You also need to remember to delete a before overwriting it, otherwise you will have a memory leak, as well as to update cap, or you will have further problems.
I assume you are doing this as an academic exercise rather than as a solution to a real-world problem. If not, you should use std::vector, as the standard library implementers have already done all the hard work for you.

Console Crash At the end(Loop) Not Out of Bound Error

Hi i was creating a simple Vector Class for my studies.
Just to store Variable LONG
i kept everything simple. When i build nothing was return error/ warning.
Then after run the program, it works but the program crash.
Code Vector.h
class Vector{
public:
Vector();
void add(long i);
void getData();
private:
long *anArray;
long maxSize;
long position;
void reSize(int i);
};
Vector.cpp
Vector::Vector(){
maxSize = 2;
anArray = new long[maxSize];
position = 0;
}
void Vector::add(long i){
if(position==maxSize-1)
reSize(maxSize * 2);
anArray[position] = i;
position++;
}
void Vector::reSize(int i){
long *temp = new long[maxSize];
for(int i =0; i<maxSize; i++)
{
temp[i] = anArray[i];
}
delete[] anArray;
anArray = temp;
}
void Vector::getData(){
for(int i = 0; i<position; i++)
{
cout << "Element" << i+1 << " : " << anArray[i] << endl;
}
}
Main
int main()
{
Vector vecStore;
for(int i = 0; i < 1000; i++)
{
long a;
vecStore.add(a = i + 1);
}
cout << "GET DATA _________ :: " << endl;
vecStore.getData();
return 0;
}
The program wouldn't crash if the input data small(e.g. 10-20)
but when i change it to 100 or even bigger. the program sometime crash and sometime its not.
Did i make a mistake?
In
void Vector::add(long i){
if(position==maxSize-1)
reSize(maxSize * 2);
anArray[position] = i;
position++;
}
when position==maxSize-1, reSize is called to double the size of the array. Unfortunately, reSize is broken.
void Vector::reSize(int i){
long *temp = new long[maxSize];
for(int i =0; i<maxSize; i++)
{
temp[i] = anArray[i];
}
delete[] anArray;
anArray = temp;
}
The parameter i is never used. This should be the new size of the Vector, but instead the new array is allocated with the same maxSize as the previous array. maxSize goes unchanged.
Back in add, data is stored at position and position is increased by one. This means on the next add position is equal to maxSize, so the check to prevent overrunning the array,
if(position==maxSize-1)
will not do what you want and the program writes outside the bounds of the array. From there, position keeps getting larger, will never be equal to maxSize minus one, and writes even further out of bounds, not that it matters at that point. Undefined Behaviour has already been invoked, and Crom only knows what will happen. Maybe the program crashes, maybe the program crashes later, maybe it does something bizarre, and maybe it even chugs on looking like everything is working just fine.
You want something more along the lines of
void Vector::reSize(int newSize){ // note more descriptive name
long *temp = new long[newSize]; // using new size, not old size
for(int i =0; i<maxSize; i++)
{
temp[i] = anArray[i];
}
delete[] anArray;
anArray = temp;
maxSize = newSize; // store new size.
}
Vector also needs a destructor to clean up anArray, but in order to properly fix this error, here's a bit of recommended reading: What is The Rule of Three?

Unitialized local variable and help correcting

I am learning about pointers and the new operator in class.
In my readArray function I am to read in a size. Use the size to dynamically create an integer array. Then assign the array to a pointer, fill it, and return the size and array.
I believe I've gotten that part corrected and fixed but when I try to sort the array, i get the error "uninitialized local variable temp used."
The problem is though I get that error when I am trying to intialize it.
Any help appreciated thank you. Seeing my errors is very helpful for me.
#include <iostream>
using namespace std;
int* readArray(int&);
void sortArray(int *, const int * );
int main ()
{
int size = 0;
int *arrPTR = readArray(size);
const int *sizePTR = &size;
sortArray(arrPTR, sizePTR);
cout<<arrPTR[1]<<arrPTR[2]<<arrPTR[3]<<arrPTR[4];
system("pause");
return 0;
}
int* readArray(int &size)
{
cout<<"Enter a number for size of array.\n";
cin>>size;
int *arrPTR = new int[size];
for(int count = 0; count < (size-1); count++)
{
cout<<"Enter positive numbers to completely fill the array.\n";
cin>>*(arrPTR+count);
}
return arrPTR;
}
void sortArray(int *arrPTR, const int *sizePTR)
{
int *temp;
bool *swap;
do
{
swap = false;
for(int count = 0; count < (*sizePTR - 1); count++)
{
if(arrPTR[count] > arrPTR[count+1])
{
*temp = arrPTR[count];
arrPTR[count] = arrPTR[count+1];
arrPTR[count+1] = *temp;
*swap = true;
}
}
}while (swap);
}
You make temp an int pointer (uninitiialized), and then set the thing it points at (anything/nothing) to arrPTR[ccount]. Since you are using temp only to swap, it should be the same type as those being swapped, in this case: an int.
If it absolutely must be a pointer (there is no good reason for this, it's slow, confusing, adds potential for errors, and adds potential for memory leaks):
int *temp = new int; //make an int for the pointer to point at
bool *swap = new bool; //make an bool for the pointer to point at
do
{
//your code
}while (swap);
delete temp;
delete swap;
You declared temp as a pointer. You need to allocate it on the heap before dereferencing and assigning to it later. However perhaps a variable on the stack would be preferable?
FYI: You should be aware of the memory leak in readArray as well which is leaving callers responsible for calling delete []
Edit: I hope this will help clear up some of the other problems.
#include <iostream>
int* readArray(int&);
void sortArray(int*, int);
int main ()
{
int size(0); // use stack when possible
int *arrPTR = readArray(size);
sortArray(arrPTR, size);
// arrays are zero based index so loop from 0 to size
for (int index(0); index < size; ++index)
std::cout << arrPTR[index];
delete [] arrPTR; // remember to delete array or we have a memory leak!
// note: because we did new[] for an array we match it with delete[]
// if we just did new we would match it with delete
system("pause");
return 0;
}
int* readArray(int& size)
{
std::cout << "Enter a number for size of array.\n";
std::cin >> size;
int *arrPTR = new int[size]; // all news must be deleted!
// prefer pre-increment to post-increment where you can
for(int count(0); count < size; ++count)
{
std::cout << "Enter positive numbers to completely fill the array.\n";
std::cin >> arrPTR[count];
}
return arrPTR;
}
// passing size by value is fine (it may be smaller than pointer on some architectures)
void sortArray(int *arrPTR, int size)
{
// you may want to check if size >= 2 for sanity
// we do the two loops to avoid going out of bounds of array on last iteration
for(int i(0); i < size-1; ++i) // the first to compare (all except last)
{
for(int j(i+1); j < size; ++j) // the second to compare (all except first)
{
// do comparison
if (arrPTR[i] > arrPTR[j]) // from smallest to biggest (use < to go from biggest to smallest)
{
// swap if needed
int temp(arrPTR[i]); // put this on stack
arrPTR[i] = arrPTR[j];
arrPTR[j] = temp;
}
}
}
}
temp is a "pointer to int, which you're not initializing. When you say *temp = ... you're actually assigning to whatever temp happens to be pointing, but since you haven't told it what to point to, it can write pretty much anywhere in the address space of your program.
Because of the way you're using them, it seems that temp and swap shouldn't be pointers at all, just a plain int and bool.
You didn't initialize the temp pointer do when you dereference it you are writing to a random part of memory. Temp doesn't need to be a pointer, it can just be an int. Just replace EVERY instance of *temp with temp.

Dynamically Growing an Array in C++

I have an array of pointers of CName objects. I have the following constructor which initializes my array to size one. Then when I add an object I grow the array by 1 and add the new object. It compiles fine, however when I try to print them I just get segmentation fault error. Can you look and see if I'm doing anything wrong?
//constructor
Names_Book::Names_Book()
{
grow_factor = 1;
size = 0;
cNames = (CName**)malloc(grow_factor * sizeof(CName*));
cNames[0] = NULL;
}
void Names_Book::addCName(CName* cn)
{
int oldSize = size;
int newSize = size + 1;
CName** newCNames = (CName**)malloc(newSize * sizeof(CName*));
for(int i=0; i<newSize; i++)
{
newCNames[i] = cNames[i];
}
for(int i=oldSize; i<newSize; i++)
{
newCNames[i] = NULL;
}
/* copy current array to old array */
cNames = newCNames;
delete(newCNames);
size++;
}
To have dynamically growable array in C++, you should use std::vector or at least look at its implementation.
There are a few things wrong with this function:
void Names_Book::addCName(CName* cn)
{
int oldSize = size;
int newSize = size + 1;
CName** newCNames = (CName**)malloc(newSize * sizeof(CName*));
for(int i=0; i<newSize; i++)
{
newCNames[i] = cNames[
}
for(int i=oldSize; i<newSize; i++)
{
newCNames[i] = NULL;
}
/* copy current array to old array */
cNames = newCNames; //right here you just leaked the memory cNames was pointing to.
delete(newCNames); // right here you delete the new array you just created using the wrong call.
size++;
}
Near the end you do two things quite wrong. (Commented above.)
Those last two lines should be:
free(cNames);
cNmaes = newCNames;
Also, you should do a realloc rather than slowly copying elements one by one....
With that said, you should use vector.
Don't try to (poorly) rewrite what already exists.
The first loop should be to oldSize:
for(int i=0; i<oldSize; i++)
cNames isn't big enough for newSize.