Implementing a Hash Table (rehash scope error) - c++

I am getting a very strange error in my code. This assignment is for a class I'm taking and essentially we are learning how to implement a hash table. The error i'm getting is when I try and rehash to a larger size. Here's the portion of the code giving me the problem, and I'll explain more fully what the problem is.
if(htable->size>=htable->cap)
{
cout<<htable->cap<<endl;
HashTable tempht=*htable;
delete htable;
htable=new HashTable((tempht.cap * 2) + 1);
for (size_t i=0; i<tempht.cap; i++)
{
Node* n=tempht.table[i];
while (n!=NULL)
{
htable->add(n->item);
n=n->next;
}
}
if (htable->table[0]==NULL)
{
cout<<"HOORAY!"<<endl;
}
}
if (htable->table[0]==NULL)
{
cout<<"HOORAY!"<<endl;
}
else
{
cout<<htable->table[0]->item<<endl;
}
htable is a HashTable variable. In the HashTable class it contains an array Node* (Nodes are just objects I created that contain a string and a pointer to the next item in the chain). This part of the code is simply trying to rehash to a larger table. The issue I'm getting is once I exit the first if statement, my table's first value no longer equals NULL (the test I'm running rehashes a table with nothing in it to a table that still has nothing in it, but has a larger capacity). When I run the code, the first htable->table[0]==NULL passes while the second does not, despite there being no changes other than exiting the if statement (my expected result is that the table[0] should be NULL). My best guess is it's some kind of scoping error, but I honestly can't see where the problem is. Any help would be greatly appreciated.
Edit: Just to clarify, the initial hash table has a capacity of 0 (this is one of the project requirements). So when i try to add an item to the table, this if statement is executed (since the size is 0 and the cap is 0, we have to maintain a load factor of 1). I can confirm that once the table reaches the first and second "Hooray" checks, that htable->cap (which is the total capacity of the array) is 1, which is what it should be. The only thing that is getting messed is bucket 0 (which in this case is the only bucket). For whatever reason, it's null before exiting the if statement but not after.
I'm posting my whole HashTable class, let me know if you find anything.
#pragma once
#include <iostream>
#include <string>
#include <fstream>
#include "Node.h"
using namespace std;
class HashTable
{
public:
Node** table;
int size;
int cap;
HashTable (int c)
{
size=0;
cap=c;
table = new Node*[cap];
if (cap>0)
{
for (size_t i=0; i<cap; ++i)
{
table[i]=NULL;
}
}
}
~HashTable()
{
delete table;
}
size_t hash(string thing)
{
size_t total=0;
int asci;
char c;
size_t index;
for (size_t i=0; i<thing.length(); i++)
{
total=total*31;
c=thing[i];
asci=int(c);
total=asci+total;
}
index=total%cap;
cout<<"index"<<index<<endl;
system("pause");
return index;
}
void add(string thing)
{
size_t index;
index=hash(thing);
cout<<"index "<<index<<endl;
system("pause");
Node* temp=table[index];
if (temp==NULL)
{
cout<<"Here"<<endl;
system("pause");
}
else
{
cout<<"Here2"<<endl;
system("pause");
cout<<"temp"<<temp->item<<endl;
system("pause");
}
Node* n = new Node(thing);
cout<<"n"<<n->item<<endl;
system("pause");
if (temp==NULL)
{
table[index]=n;
}
else
{
while (temp->next!=NULL)
{
temp=temp->next;
}
temp->next=n;
}
size++;
}
Node* find(string search)
{
Node* n= NULL;
size_t index;
if(cap!=0)
{
index=hash(search);
Node* temp=table[index];
while (temp!=NULL)
{
if (temp->item==search)
{
n=temp;
return n;
}
}
}
return n;
}
void remove (string thing)
{
if (find(thing)==NULL)
{
return;
}
else
{
size_t index;
index=hash(thing);
Node* temp=table[index];
if (temp->item==thing)
{
table[index]=temp->next;
delete temp;
}
while (temp->next!=NULL)
{
if (temp->next->item==thing)
{
Node* temp2=temp->next;
temp->next=temp->next->next;
delete temp2;
break;
}
}
}
size--;
}
void print(ofstream &ofile)
{
for (size_t i=0; i<cap; i++)
{
Node* n=table[i];
ofile<<"hash "<<i<<":";
while (n!=NULL)
{
ofile<<" "<<n->item;
n=n->next;
}
}
}
};

Well, this is C++, and I'm more a Java guy, but I'll take a stab at it.
Turns out the problem IS with the
HashTable tempht=*htable;
delete htable;
block after all.
See, the first line there says "copy all of the members from *htable into tempht". So now tempht and htable SHARE their table memory, since table is just a pointer to memory that was allocated at construction, and you just copied the pointer. You wanted it to copy the nodes inside table, but it didn't do that.
So now you have two different HashTable objects with the same pointer value in table. Now, when tempht is freed, the destructor calls free on the table pointer, which effectively frees the table data in both objects htable and tempht.
What you really want to do is write a copy constructor, or do something like:
HashTable *tempht=htable;
htable=new HashTable((tempht->cap * 2) + 1);
for (size_t i=0; i<tempht->cap; i++)
{
Node* n=tempht->table[i];
while (n!=NULL)
{
htable->add(n->item);
n=n->next;
}
}
if (htable->table[0]==NULL)
{
cout<<"HOORAY!"<<endl;
}
delete tempht;
See how all I've really done is change tempht to a pointer, using it to point to the old hashtable while you copy all the nodes from it to the new htable object, then deleting the old Hashtable.

Related

How to correctly delete an allocated array (queue data structure)

I created a queue data structure using a struct and a dynamically allocated array, I don't understand what is the right way to free or delete it without any memory leaks.
I have tried using the following:
delete[] q->data;
delete[] &(q->data);
delete &(q->data);
#include "queue.h"
void initQueue(queue* q, unsigned int size)
{
q->maxSize = size;
q->size = 0;
q->data = new unsigned int[size];
q->front = 0;
q->rear = 0;
}
void enqueue(queue* q, unsigned int newValue)
{
if (q->size != q->maxSize)
{
q->data[q->rear] = newValue;
q->size++;
q->rear++;
}
else
{
std::cout << "Queue is full! you can clean it and initialize a new one" << std::endl;
}
}
int dequeue(queue* q)
{
int i = 0;
if (q->size == 0)
{
std::cout << "Queue is empty!" << std::endl;
return EMPTY;
}
else
{
q->front++;
q->size--;
return q->data[q->front];
}
}
void cleanQueue(queue* q)
{
//the delete function
}
The technical right answer here is to delete q->data, as others have suggested. But...
right way to free or delete it without any memory leaks
The right way in C++, unless you're doing some exotic with allocation, is not to do your own memory management. Write a class that allocates in the constructor, and deletes in the destructor, as Chris suggested, is a great way to learn about RAII and how it saves you from the mental burden of manually writing "delete" everywhere.
But the right right way, if someone was paying me? I'd skip all that and use a vector.
#include <vector>
class MyQueue {
public:
MyQueue(unsigned int size) : data(size) { }
void enqueue(unsigned int value) { /* whatever... */ }
int dequeue() { /* whatever... */ }
private:
std::vector<unsigned int> data;
};
When this class goes out of scope or gets deleted, the vector will automatically be cleaned up. You don't even need to free or delete anything.

Trie Tree. Unable to access memory

I am a beginner at C++ and am have some issues with 2 separate errors. Unable to access memory and stack overflow.
This is my implementation for a Trie Tree, using pointers, of words containing characters a-z. When running tests, I can successfully add several hundred, or even thousands of nodes without issue, until it eventually crashes. Error: Unable to access memory. I more often get this error when I am trying to run a query and use the "isAWord" function. I also get a stack overflow when I try to run the deconstructor. Any help is appreciate, as I've spent 2 days trying to debug with little success.
#include "Trie.h"
#include <iostream>
#include <iterator>
#include <sstream>
using namespace std;
//sets up tree
Trie::Trie()
{
for (int i = 0; i < ALPH; i++)
this->childs[i] = nullptr;
endNode = false;
}
//add 'userInput' string to trie
void Trie::addAWord(std::string userInput)
{
Trie* start = this;
for (int i = 0; i < userInput.length(); i++)
{
int index = userInput[i] - 'a';
if (start->childs[index] == nullptr)
start->childs[index] = new Trie();
start = start->childs[index];
}
start->endNode = true;
}
//returns true if 'wordFind' is in tree
bool Trie::isAWord(std::string wordFind)
{
if (this == nullptr)
return false;
Trie* start = this;
for (int i = 0; i < wordFind.length(); i++)
{
int index = wordFind[i] - 'a';
start = start->childs[index];
if (start == nullptr)
return false;
}
return start->endNode;
}
//returns a vector containing the words in tree with prefix 'prefFind'
vector<std::string> Trie::allWordsStartingWithPrefix(std::string prefFind)
{
string pres = PrefixRec(prefFind,*this);
stringstream preStream(pres);
istream_iterator<std::string> begin(preStream), end;
vector<std::string> stringSet(begin, end);
copy(stringSet.begin(), stringSet.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
return stringSet;
}
//helper method for AllWordsStartingWithPrefix
std::string Trie::PrefixRec(std::string& key, Trie const temp)
{
if (temp.endNode)
return(key + " ");
for (char index = 0; index < ALPH; ++index)
{
index = key[index] - 'a';
Trie const* curChild = temp.childs[index];
if (curChild)
{
key.push_back(index);
PrefixRec(key, *curChild);
key.pop_back();
}
}
}
//copy cons and assignment op
Trie& Trie::operator=(const Trie& other)
{
Trie* newPtr = new Trie(other);
other.~Trie();
return *newPtr;
}
//deconstructor
Trie::~Trie()
{
if (this == nullptr)
return;
for (int i = 0; i < ALPH; i++)
{
if (childs[i] != nullptr)
childs[i]->~Trie();
}
delete this;
return;
}
#include <iostream>
#include <vector>
#include <string>
#define ALPH 26
class Trie
{
public:
bool endNode;
Trie* childs[ALPH];
Trie();
void addAWord(std::string key);
bool isAWord(std::string key);
std::vector<std::string> allWordsStartingWithPrefix(std::string key);
Trie& operator=(const Trie& other);
std::vector<std::string> wordsWithWildcardPrefix(std::string);
std::string PrefixRec(std::string& key, Trie const temp);
~Trie();
};
I also get a stack overflow when I try to run the deconstructor.
This is because of this line:
delete this;
This is what a delete does
The delete expression invokes the destructor (if any) for the object
that's being destroyed,
You can imagine why calling delete from within the destructor would be problematic. (Hint: Infinite recursion)
You don't want any delete this in your code.
Once you get rid of this, there are other issues.(Although you may live just by fixing this). For instance calling the destructor explicitly as you are doing in this line(and several other lines)
other.~Trie();
From iso cpp:
Should I explicitly call a destructor on a local variable?
No!
The destructor will get called again at the close } of the block in which the local was created. This is a guarantee of the language; it happens automagically; there’s no way to stop it from happening. But you can get really bad results from calling a destructor on the same object a second time! Bang! You’re dead!
Replace the explicit destructor calls with delete and let it call the destructor correctly.
I would recommend replace any raw pointers and new and delete with smart pointer. Start with shared_ptr to begin with. (raw_pointers are so 2010 ;))
Footnote: Get rid of these checks. They are non-idiomatic. It's ok and desirable for the caller to burn when calling a member function on a nullptr
if (this == nullptr)
return false;

Stack (Data structure) implementation

So I'm just starting to learn about data structures through a course on Coursera and I learned that it's possible to create a stack data structure by using an array. I was just wondering if what I have written is what a stack is supposed to do.
#include <iostream>
using namespace std;
const int MAX_SIZE = 10000;
class Stack {
public:
Stack();
~Stack();
void push(int n);
void pop();
int top();
bool isEmpty() const;
void print() const;
private:
int* array [MAX_SIZE];
int curNum;
};
Stack::Stack() {
curNum = 0;
}
Stack::~Stack() {
for (int i = 0; i < curNum; ++i)
delete array[i];
}
void Stack::push(int n) {
if (curNum >= MAX_SIZE) {
cout << "reached maximum capacity...can't add an element\n";
return;
}
array[curNum] = new int(n);
curNum++;
}
void Stack::pop() {
delete array[curNum];
curNum--;
}
int Stack::top() {
return *array[curNum];
}
void Stack::print() const{
for (int i = 0; i < curNum; ++i)
cout << *array[i] << endl;
}
bool Stack::isEmpty() const{
return curNum == 0;
}
int main () {
Stack stack;
stack.push(5);
stack.print();
stack.pop();
}
Also, I see that a lot of people don't use dynamic memory allocation for this kind of task. Is there a reason why? It seems like specifying a size for the array at compile time might lead to insufficient memory or over-allocating memory to me
Yes, this is one way to implement a stack. The important thing that defines a stack is LIFO (last in, first out). So as long as you are only adding to and removing from the top, then that is a stack. Think of it as a stack of dishes; if 10 dishes are put one by one into a stack, and then one by one removed from said stack, the first dish put on will also be the last dish removed. You can't remove a dish that's not at the top, as it is covered by all the dishes above it. The same is true with a stack data structure.
So your implementation is indeed a stack.
The stack we use when we want something in reverse order and stack also takes constant time means O(1) time to push and pop means to remove or to add it will work much faster

Segmentation Fault 11 whenever I run this. Would like assistance/feedback

So I'm making a really rudimentary implementation of a circular list. I haven't made the remove function yet. Whenever I run the cpp, I get a seg fault 11. Any feedback would be much appreciated. Thank you.
#include <iostream>
using namespace std;
struct node{
node* next=NULL;
bool tail= false;
int contents;
};
node* start;//start is a pointer that exists at the start of the list before the first element
class CircList{
node *seek;
public:
CircList (){ //creates a list of one node that points to itself
node *b= new node;
b->contents=0;
b->next = b;
start->next=b;
b->tail=true;
}
bool empty(){
if(start->next==NULL){
return true;
}
return false;
}
int size(CircList a){
if(start->next==NULL){
cout<<"size is 0 \n";
return true;
}
seek=start->next;
for(int i=0; i++;){
if(seek->tail==true){
cout<<"size is "<<i;
}
seek=seek->next;
}
return 0;
}
void insert(int pos, int val){
if(start->next ==NULL){//if inseting when the list is empty
node *b= new node;
b->next = b;
b->tail=true;
return;
}
node *b= new node;
b->contents= val;
seek=start->next;
for(int i=0;i<=pos; i++){
if(seek->tail==true){//if inserting at the end
seek->tail=false;
b->tail=true;
seek->next=b;
b->next=start->next;
}
if(pos==i){//if inserting between two nodes
b->next = seek->next;
seek->next = b;
}
seek=seek->next;
}
}
void remove(int a){
seek=start->next;
for(int i=0;i<=a-1; i++){
if(i<a){
seek=seek->next;
}
if(i==a-1){
}
}
}
void display(){
cout<<start->next->contents; //will also be completed in the near future
seek=start->next;
for(int i=0; ;i++){
if(seek->tail==false){
cout<<seek->contents<<"\n";
}
if(seek->tail==true){
cout<<seek->contents<<"\n";
return;
}
}
}
};
That was the .h file. The following is the cpp. I just plugged in numbers to test. I want to get the program running so that I can test how it behaves.
#include <iostream>
#include "CircList.h"
using namespace std;
int main(){
CircList a;
a.insert (5,5);
a.insert (5,5);
a.insert (1,4);
a.insert (20,65);
a.insert (3,7);
a.size(a);
a.display();
}
I kept treating start as a node instead of a pointer. By making start = Null and replacing all the "start->next"'s with "start", I got it to compile and run. But now it's only infinitely inserting nodes with a value of 0 in the contents.
Edit: I fixed it. By changing that weird for loop in the display function to a while loop, it doesn't do infinite inserts of the node in the constructor, anymore. It seems to work decently enough now.
This here causes a seg fault
start->next=b;
because start is NULL at the start of the program so you are de-referencing a null pointer.
instead set start to the first node in your constructor
start = b;
Your global variable start is an uninitialized pointer, yet you dereference it all over the place.

Class creating multiple objects of another class at the same memory location (C++)

So, I've got this class that contains a vector of another class. Whenever I try to push a new object into this vector, it's creating that object at the same memory location each time.
The (hopefully) relevant code:
class FSM{
private:
std::vector<Node> nodeList;
int cap;
int obs;
int topNode;
public:
FSM(int nodeCap, int numObs){
cap = nodeCap;
obs = numObs;
topNode = -1;
}
bool addNode(){
if (isFull()) return false;
nodeList.push_back(Node(obs));
topNode++;
return true;
}
Now, if I create a stand-alone Node object in my main function and cout the &node, I get different memory locations. But the ones created in the FSM class are always the same. Also, if I change anything in one of the Nodes stored by the FSM class, it changes it for all of them. I have no idea what's going on.
EDIT: As requested, here is the Node class. Just gonna post the whole thing, not sure what is relevant.
class Node{
private:
std::vector<int> connects;
int action;
public:
Node(int numObs){
for(int i = 0; i < numObs; i++){
connects.push_back(-1);
}
srand(time(NULL));
}
void setConnections(std::vector<int> newVec){
for (int i = 0; i < connects.size(); i++){
connects[i] = newVec[i];
}
}
int getConnection(int index){
return connects[index];
}
std::vector<int> getConnectionList(){
return connects;
}
void setAction(int act){
action = act;
}
int getAction(){
return action;
}
void setRandomConnections(int numNodes){
for (int i = 0; i < connects.size(); i++){
connects[i] = rand() % numNodes;
}
}
};
EDIT the Second: Here's what my main is doing.
int main(){
FSM myFSM(5, 3);
while (!myFSM.isFull()){
myFSM.addNode();
std::cout << &myFSM.getTopNode(); // getTopNode() returns the most recent
// node.
}
}
If getTopNode does what I think it does, you're printing the address of a temporary object (aka a copy of the top node, not the top node itself). So that code is meaningless.
Here I've implemented a print function for the locations of the nodes in FSM:
void printNodeLocations()
{
for(Node& n : nodeList) { std::cout << &n << std::endl; }
}
And I get different ones as expected:
0x8ad3018
0x8ad301c
EDIT: I cannot reproduce your claim that changing one node changes all of them. See updated code
This line:
std::cout << &myFSM.getTopNode();
probably prints the address of a temporary object, not the actual object in the vector. This will be true if you're not returning by reference but rather by value.
So it's not weird if the temporary happens to be created at the same location each time, since after the temporary dies, its location in memory is free to be used again later.
In order to get the actual object rather than a copy of it, getTopNode() needs to do:
Node& FSM::getTopNode()
{
if (nodeList.empty()) {
// Up to you how to handle this error.
}
return nodeList.back();
}
Of course, if your current getTopNode() implementation actually already returns a pointer:
Node* FSM::getTopNode()
then your problem is that you're printing out the address of the pointer rather than the pointer itself. In that case you should print with:
std::cout << myFSM.getTopNode();
Nothing happens similar to yours.
#include <iostream>
#include <vector>
class Node{
private:
std::vector<int> connects;
int action;
public:
Node(int num){
for(int i = 0; i < num; i++){
connects.push_back(i);
}
}
std::vector<int> getConn()
{
return connects;
}
};
class FSM{
private:
std::vector<Node> nodeList;
public:
FSM(){}
void addNode(int size){
Node l(size);
std::cout<<"temp_address "<<&l<<"\n";
nodeList.push_back(l);//use of default copy constructor
}
void printList(){
std::vector<int> p;
for (int i=0; i<nodeList.size(); i++)
{
std::cout<<"Node_arr_num "<<i<<" mem_address "<<&nodeList[i]<<"\nConnections:";
p=nodeList[i].getConn();
for (int j=0; j<p.size(); j++)
std::cout<<" "<<p[j];
std::cout<<"\n";
}
}
};
int main()
{
FSM f;
f.addNode(5);
f.addNode(10);
f.addNode(3);
f.printList();
return 0;
}
Result:
temp_address 0xbfea7660
temp_address 0xbfea7660
temp_address 0xbfea7660
Node_arr_num 0 mem_address 0x8dab098
Connections: 0 1 2 3 4
Node_arr_num 1 mem_address 0x8dab0a8
Connections: 0 1 2 3 4 5 6 7 8 9
Node_arr_num 2 mem_address 0x8dab0b8
Connections: 0 1 2
Be careful with adding nodes later, when your app will grow. Temporary l object (ore your Node(obs)) must be copied with explicit copy constructor of class Node if Node will be more complex (contains fields with dynamic allocated memory).