Stack overflow exception for large data sets - c++

I tried solving this problem: http://projecteuler.net/problem=201 But the program below breaks for larger SET size(>20) throwing a stack overflow exception. Is there a memory leak happening? Could you please point out where? Also if the following implementation involves bad coding practice, please let me know. I'm trying to improve my amateur programming skills. Thanks in advance.
EDIT: I edited the code below. I don't get any stack overflow exceptions now. But could anyone suggest me a better algorithm? Thanks!
#include "stdafx.h"
#include <iostream>
#include <cmath>
#include <winsock2.h>
#define SET 100
#define SUBSET 50
class node
{
public:
int data;
node* next;
};
using namespace std;
int sum(int A[SUBSET]);
void combCalc(int S[SET]);
bool search(node* head, int sum);
void appendIfUnique(int total, bool nullHeader);
int _tmain(int argc, _TCHAR* argv[])
{
cout << "Hello World" << endl;
int S [SET];
for(int i=1; i<=SET; i++)
{
S[i-1]= (int)pow((float)i,2);
}
combCalc(S);
cin.get();
return 0;
}
static node *head;
static node *current = head;
void combCalc(int S[])
{
int row=0, col=0;
int elePos[SUBSET], B[SUBSET];
bool nullHeader = true;
for(int z=0; z<SUBSET; z++) // initializing elePos
{
elePos[z]=z;
}
bool notDone = true;
while (notDone || col <(SUBSET-1))
{B[col] = S[elePos[col]];
if(col==(SUBSET-1)) //finished forming a subset
{
notDone = false;
for(int q=(SUBSET-1); q>=0; q--) //incrementing from the last element
{
if(elePos[q]<(SET-SUBSET+q)) //checking if the element has reached its maximum
{
notDone = true;
elePos[q]++;
for(int w=q+1; w<SUBSET; w++) //setting the trailing elements to its minimum
{
elePos[w]=elePos[q]+w-q;
}
break;
}
}
if(notDone){col=0;row++;}
int total = sum(B);
appendIfUnique(total,nullHeader);
nullHeader = false;
}
else
{
col++;
}
}
int result = 0;
for(node *pnode = head; pnode != current->next; pnode=pnode->next)
result = result + pnode->data;
cout << result << endl;
}
int sum(int A[])
{
int total = 0;
for(int i=0; i<SUBSET; i++)
{
total = total + A[i];
}
return total;
}
bool search(node* head, int sum)
{
bool exists = false;
node* pNode = head;
while(pNode != NULL)
{
if(pNode->data == sum)
{
exists = true;
break;
}
pNode = pNode->next;
}
return exists;
}
void appendIfUnique(int total, bool nullHeader)
{
if(nullHeader){head = NULL;}
if(!search(head,total))
{
node *temp;
/*temp=(node *) malloc(sizeof(node));*/
temp = new node();
temp->data = total;
temp->next = NULL;
if(head == NULL)
{
head = current = temp;
}
else
{
current->next = temp;
current = temp;
}
}
}

Some notes:
Break point (in my system: cygwin g++) is SET=18 (17 works)
Problem is due to too much recursion [run it within a debugger] You have too many calls to combCalc(S) (in my case, it dies after 32K calls).
As it has been indicated in the comments, you should probably reconsider your algorithm. In the meantime, a simple modification is to remove the recursion (since it is not even a proper recursion):
int _tmain(int argc, _TCHAR* argv[])
{
cout << "Hello World" << endl;
int S [SET];
for(int i=1; i<=SET; i++)
{
S[i-1]= (int)pow((float)i,2);
}
while(combCalc(S)) { } // <--- keep calling while combCalc is true
cin.get();
return 0;
}
by making combCal() return a bool:
bool combCalc(int S[]) // <--- MODIFY (check also the forward declaration)
{
...
if(notDone || col <(SUBSET-1))
{
...
return true; // <--- MODIFY return true... I need to keep calculating.
}
else
{
int result = 0;
for(node *pnode = head; pnode != current->next; pnode=pnode->next)
result = result + pnode->data;
cout << result << endl;
return false; // <--- MODIFY return false.... we're done
}
}
The above is just a minimum modification. I'm not even sure it solves the problem correctly since I haven't really looked at the algorithm.
You should consider:
Use a less brute force algorithm
Move the while loop within the combCalc()... so you will be having a single call... and in this case you probably can remove the static variables and make them simple local variables.
Consider not using #define for constants.
Consider using STL structures... instead of your own home grown ones. This will remove some of the concerns below.
Consider not using malloc but using new (more C++)
You're not using free this means that the memory you allocated will not be released. (use delete if you're using new or delete [] if you did new [])

Related

Checking if a word is a palindrome by reversing a stack without using any prewritten functions

#include <iostream>
#include <string>
using namespace std;
#define Max 100000
class Stack {
private:
int top =-1;
char letters[Max];
public:
void setTop(int t) {
top = t;
}
int getTop() {
return top;
}
bool isEmptyStack() {
if (top == -1) {
return true;
}
else{ return false;
}
}
char push(char x,int s) {
if (top != s - 1){
top++;
x = letters[top];
return x;
}
}
char pop() {
if ((isEmptyStack())==false){
cout << "the deleted value is: " << l[top]<<endl;
top--;
return l[top];
}
}
};
void reverse(char letters[], char temp[], int size, Stack stack) {
int i=0;
for (i = 0; i < size; i++) {
stack.push(letters[i],size);
}
i = 0;
cout << temp<<endl;
while (stack.isEmptyStack() == false)
{
letters[-1] = stack.getTop();
stack.pop();
stack.push(letters[i],size);
i++;
}
/* for (int i = 0; i < size; i++) {
cout << temp[i];
}*/
}
int myStringLength(const char* letter)
{
for (int i = 0, c = 0; letter[i] != '\0'; i++, c++) {
if (letter[i] != '\0')
for (; letter[i] != '\0'; i++, c++)
if (letter[i] == '\0') break;
return c;
}
}
int main()
//initializes the main function
{
Stack stack;
string w;
std::cout << "Enter a Word: ";
getline(cin,w);
char* letters = &w[0];
// sets the character text array to set the number of characters equal to the size of the string
//calls the processData function
std::cout << letters<<endl;
int size = myStringLength(letters);
reverse(letters, letters, size, stack);
return 0;//returns the function at 0.
}
I set out to create a program that will check if a word is a palindrome(meaning it is spelled the same normally and if the word is reversed.) I am not yet at that point that is just the final objective. In my code, I have created a stack class because I wanted to feel the satisfaction of getting the same result using my own code. My problem is the stack is not reversing it is returning some weird characters that I don't have the keys on my keyboard to replicate.
The desired outcome should be word's reversed characters.
if the word is food the function should be returning doof. I have already compared the reversed stack to the original and printed the final statement. I fixed the char letters[];
If you're open to using a simple function instead of a Stack then you could use the following program since it is much more simple than your Stack version and hence less-error prone.
#include <iostream>
#include <string>
bool checkIfPalindroom(const std::string &str)
{
for(int i=0;i<(str.size()/2);i++)
{
if (str[i] != str[str.size() - i - 1])
{
return false;//if this if is satisfied even once, return false
}
}
return true;//if the control flow reaches here this will mean that no if was satisfied and hence return true
}
int main()
{
std::string myString = "Somearbitrarystring";
if(checkIfPalindroom(myString))//call the function
{
std::cout<<"The given string: "<<myString <<" is a palindrome"<<std::endl;
}
else
{
std::cout<<"The given string: "<<myString<<" is not a palindrome"<<std::endl;
}
return 0;
}

Stack using array

I'm new to coding and was just learning stack and the code below is not giving any output. Can you guys please help me out with what I'm doing wrong in this?
#include<iostream>
using namespace std;
struct stack
{
int size;
int top;
int *arr;
};
int isEmpty(struct stack *ptr)
{
if(ptr->top==-1)
{
return 1;
}
return 0;
}
int isFull(struct stack *ptr)
{
if(ptr->top==ptr->size-1)
{
return 1;
}
return 0;
}
int main()
{
struct stack *s;
s->size = 80;
s->top=-1;
s->arr= new int(80);
if(isEmpty(s))
{
cout<<"the stack is empty"<<endl;
}
else
{
cout<<"the stack is not empty"<<endl;
}
}
Your main bug is that you haven't initialized s to point to anything. You set s->arr using new, but haven't done so for s itself. You dereference s when setting the size, top, etc, and also when checking top. This is undefined behavior.
You need:
struct stack *s = new struct stack;
to fix this.
Also, always remember to check the allocated pointer(s) are not nullptr before proceeding.
You also don't de-allocate the memory (using delete), so you have a memory leak.
Also, you can use instead the defined true and false, instead of 1 and 0, and change the return types to be bool.
Here is a corrected version of your code with the mentioned changes:
#include <iostream>
struct stack {
int size;
int top;
int *arr;
};
bool isEmpty(struct stack *ptr) {
if (ptr->top == -1) {
return true;
}
return false;
}
bool isFull(struct stack *ptr) {
if (ptr->top == ptr->size - 1) {
return true;
}
return false;
}
int main(void) {
/* you have to initialize s to point to something */
struct stack *s = new struct stack;
/* check for nullptr */
if (s == nullptr) {
return EXIT_FAILURE;
}
s->size = 80;
s->top = -1;
/* array of 80 integers */
s->arr = new int[s->size];
/* check for nullptr */
if (s->arr == nullptr) {
return 1;
}
if (isEmpty(s)) {
std::cout << "the stack is empty\n";
} else {
std::cout << "the stack is not empty\n";
}
/* remember to de-allocate the memory -- in the reverse order */
delete[] s->arr;
delete s;
return 0;
}
Tip: always compile your code with some basic compilation flags to check for this sort of thing. For example, you might compile using at least:
g++ -Wall -Werror -Wextra program.cpp
Also, consider using valgrind, which is a powerful tool for detecting memory leaks and uninitialized memory accesses.
A few extra notes:
The code reads like a C program more or less, as pointed out in the comments, so consider whether you want to instead write it as a C program, or adapt it more to C++
You could also instead declare s to be on the stack instead (struct stack s;), access its fields in main like s.size, s.top, etc and then pass s in by its address (&s).
I have commented out where some changes were to be made...
#include<iostream>
using namespace std;
struct stack
{
int size;
int top;
int* arr;
};
int isEmpty(struct stack* ptr)
{
if (ptr->top == -1)
{
return 1;
}
return 0;
}
int isFull(struct stack* ptr)
{
if (ptr->top == ptr->size - 1)
{
return 1;
}
return 0;
}
int main()
{
// s should point to something for initializing top to -1;
struct stack* s = new struct stack;
s->size = 80;
s->arr = new int(80);
// Top is initialized to -1 otherwise the output will be shown as The stack is not empty.
s->top = -1;
if (isEmpty(s))
{
cout << "the stack is empty" << endl;
}
else
{
cout << "the stack is not empty" << endl;
}
}

Getting a segmentation fault while implementing Trie in c++

Another day another seg fault I don't understand, I'm trying to implement tries for the first time and it is proving to be quite some challenge, I think it would be very helpful if someone could tell me what I'm doing wrong, would probably help me understand OOP better too I suppose, because I think the error is related to that.
The fault happens while searching and I wasn't able to understand it using the debugger myself.
Here is the code:
#include <vector>
#include <iostream>
using std::string, std::cout, std::vector;
class TrieNode {
private:
bool isLeaf;
vector<TrieNode*> ar = vector<TrieNode*>(26);
public:
TrieNode() {
isLeaf = false;
for(int i = 0; i < 26; i++) ar[i] = nullptr;
}
void insert(TrieNode *root, string key) {
TrieNode *crawl = root;
for(int i = 0; i < key.size(); i++) {
if(!crawl->ar[key[i]]) {
crawl->ar[key[i]] = new TrieNode();
}
crawl = crawl->ar[key[i]];
}
crawl->isLeaf = true;
}
bool search(TrieNode *root, string key) {
TrieNode *crawl = root;
for(int i = 0; i < key.size(); i++) {
if(!crawl->ar[key[i]]) {
return false;
}
crawl = crawl->ar[key[i]];
}
return crawl->isLeaf;
}
};
int main() {
TrieNode* head = new TrieNode();
head->insert(head, "hello");
cout << head->search(head, "hello");
}
Make your ar[key[i]] to something like ar[key[i]-'a'] if your string is say always lower-case.
Basically, key[i] is a char in the range of ['a'-'z']. When it's implicitly converted to an int, it's not in the range of [0,25], but rather equal to their ascii values.

Why is the 98th element in my Stack implementation such a bizarre value?

I have the following stack data structure implementation in C++:
// file: Stack.h
#pragma once
#include <iostream>
#include <exception>
class CStack
{
private:
int counter;
int *data;
int currentmaxsize;
void adjust();
public:
void push(int value);
int pop();
int peek();
int getsize();
CStack();
~CStack();
};
// file: Stack.cpp
#include "Stack.h"
void CStack::adjust()
{
int *temp = new int[currentmaxsize];
for (int i = 0; i < counter; i++)
{
temp[i] = data[i];
}
delete data;
data = new int[currentmaxsize * 2];
for (int i = 0; i < counter; i++)
{
data[i] = temp[i];
}
delete temp;
currentmaxsize *= 2;
}
int CStack::getsize()
{
return counter;
}
void CStack::push(int value)
{
if (counter+1 == currentmaxsize)
{
adjust();
}
counter++;
data[counter] = value;
}
int CStack::peek()
{
return data[counter];
}
int CStack::pop()
{
if (counter > 0)
{
int ret = data[counter];
counter--;
return ret;
}
else if (counter == 0)
{
throw std::exception("cannot pop empty stack");
}
return 0xFFFFFFFF;
}
CStack::CStack()
{
data = new int[100];
currentmaxsize = 100;
counter = 0;
}
CStack::~CStack()
{
delete data;
}
This is a fairly standard stack implementation. The only thing that is different from the kind of stack you would see in most textbooks is the adjust() function, which reallocates the stack with a bigger size if the original boundary is reached.
I wrote the following driver for the data structure as well:
// file: driver.cpp
#include <iostream>
#include "Stack.h"
int main(int argc, char *argv[])
{
CStack stack;
for (int i = 0; i < 200; i++)
{
stack.push(i);
std::cout << "Pushed: " << i << std::endl;
//std::cout << "New stack size: " << stack.getsize() << std::endl;
}
int len = stack.getsize();
std::cout << "len = " << len << std::endl;
for (int i = 0; i < len; i++)
{
std::cout << "Popped: " << stack.pop() << std::endl;
//std::cout << "New stack size: " << stack.getsize() << std::endl;
}
return 0;
}
This works almost as I would expect it to, except this one value in the program output:
Popped: 100
Popped: 99
Popped: 7798895
Popped: 97
Popped: 96
It is always the value of the 98th element in the stack that has a bizarre value like this, and I don't know why it is - the adjust() function is being called when the stack hits 100 values, not 99, so I don't imagine it's a problem with the adjust function.
Your push and peek and probably other functions use counter as the index of the last element. But other parts of your code use counter as the number of elements so counter-1 would be the index of last. So data is lost during adjust
Select one design: The valid indexes are 0 through counter-1 inclusive or 0 though counter or 1 through counter (wasting position 0).
I only like the first of those choices but any one of them can work (your existing code is closest to being the third). Having different parts play by different rules doesn't work.
I see at least one bug. You are using delete after doing a new[].
new should be matched with delete and new[] should be matched with delete[]. Otherwise it's undefined behavior.
Also, you are doing unnecessary copying in your adjust function.

AVL Tree Memory issues with destructor

I'm trying to implement AVL Tree in C++, but I'm stuck with the insertion, I have changed some things but nothing seemed to effectively solve the problem. I used Xcode's Address Sanitizer and I'm getting that error after inserting a second element into the tree:
Thread 1: Use of deallocated memory detected.
==3822==ERROR: AddressSanitizer: heap-use-after-free on address.....
This is the implementation of the tree so far:
RoadTree.hpp
#ifndef RoadTree_hpp
#define RoadTree_hpp
#include "Road.hpp"
class RoadTree {
private:
struct TreeNode {
Road *key;
TreeNode *rightChild;
TreeNode *leftChild;
int height;
TreeNode() : key(NULL), rightChild(NULL), leftChild(NULL), height(0) { }
TreeNode(Road *r) : key(r), rightChild(NULL), leftChild(NULL), height(0) { }
};
TreeNode *root;
int numberOfRoads;
int GetHeight(TreeNode *n) const;
void SimpleRightRotation(TreeNode *&n);
void DoubleRightRotation(TreeNode *&n);
void SimpleLeftRotation(TreeNode *&n);
void DoubleLeftRotation(TreeNode *&n);
void Insert(TreeNode *&n, Road *r);
void ClearTree(TreeNode *&n);
void PreOrder(TreeNode *n) const;
public:
RoadTree();
~RoadTree();
void Insert(Road *r);
Road *FindRoad(string destination);
void ListRoads();
void ClearTree();
void PreOrder();
inline int RoadCount() {
return numberOfRoads;
}
};
#endif /* RoadTree_hpp */
RoadTree.cpp
#include "RoadTree.hpp"
RoadTree::RoadTree() : root(NULL), numberOfRoads(0) { }
RoadTree::~RoadTree() {
ClearTree(root);
}
void RoadTree::Insert(Road *r) {
Insert(root, r);
}
int RoadTree::GetHeight(TreeNode *n) const {
if (n == NULL)
return -1;
else
return n->height;
}
void RoadTree::SimpleRightRotation(TreeNode *&n) {
TreeNode *tempNode = n->rightChild;
n->rightChild = tempNode->leftChild;
tempNode->leftChild = n;
n->height = 1 + max(GetHeight(n->leftChild), GetHeight(n->rightChild));
n = tempNode;
tempNode->height = 1 + max(n->height, GetHeight(tempNode->rightChild));
}
void RoadTree::DoubleRightRotation(TreeNode *&n) {
SimpleLeftRotation(n->rightChild);
SimpleRightRotation(n);
}
void RoadTree::SimpleLeftRotation(TreeNode *&n) {
TreeNode *tempNode = n->leftChild;
n->leftChild = tempNode->rightChild;
tempNode->rightChild = n;
n->height = 1 + max(GetHeight(n->leftChild), GetHeight(n->rightChild));
n = tempNode;
tempNode->height = 1 + max(n->height, GetHeight(n->leftChild));
}
void RoadTree::DoubleLeftRotation(TreeNode *&n) {
SimpleRightRotation(n->leftChild);
SimpleLeftRotation(n);
}
void RoadTree::ClearTree(TreeNode *&n) {
if (n != NULL) {
ClearTree(n->rightChild);
ClearTree(n->leftChild);
delete n;
}
n = NULL;
}
void RoadTree::Insert(TreeNode *&n, Road *r) {
if (n == NULL) {
n = new TreeNode(r);
numberOfRoads++;
} else {
if (r->GetDestination() < n->key->GetDestination()) {
Insert(n->leftChild, r);
if ((GetHeight(n->leftChild) - GetHeight(n->rightChild)) == 2) {
if (r->GetDestination() < n->leftChild->key->GetDestination())
SimpleLeftRotation(n);
else
DoubleLeftRotation(n);
}
} else if (r->GetDestination() > n->key->GetDestination()) {
Insert(n->rightChild, r);
if ((GetHeight(n->rightChild) - GetHeight(n->leftChild)) == 2) {
if (r->GetDestination() > n->rightChild->key->GetDestination())
SimpleRightRotation(n);
else
DoubleRightRotation(n);
}
} else if (r->GetDestination() == n->key->GetDestination())
n->key->SetRoad(r->GetDestination(), r->GetCost(), r->GetInfo());
}
n->height = 1 + max(GetHeight(n->leftChild), GetHeight(n->rightChild));
}
Road *RoadTree::FindRoad(string destination) {
TreeNode *n = root;
while (n != NULL) {
string current = n->key->GetDestination();
if (destination < current)
n = n->leftChild;
else if (destination > current)
n = n->rightChild;
else if (destination == current)
return n->key;
}
return NULL;
}
void RoadTree::PreOrder(TreeNode *n) const {
if (n != NULL) {
cout << " " << n->key->GetDestination() << " ";
PreOrder(n->leftChild);
PreOrder(n->rightChild);
}
}
void RoadTree::PreOrder() {
PreOrder(root);
}
void RoadTree::ListRoads() {
}
void RoadTree::ClearTree() {
ClearTree(root);
}
And this is the implementation of Road:
Road.hpp
#ifndef Road_hpp
#define Road_hpp
#include <iostream>
using namespace std;
class Road {
private:
string destination;
int cost;
string info;
public:
Road();
Road(string destination, int cost, string info);
inline string GetDestination() {
return destination;
}
inline int GetCost() {
return cost;
}
inline string GetInfo() {
return info;
}
};
#endif /* Road_hpp */
Road.cpp
#include "Road.hpp"
Road::Road() {
destination = "";
cost = 0;
info = "";
}
Road::Road(string destination, int cost, string info) {
this->destination = destination;
this->cost = cost;
this->info = info;
}
The only way I can insert more than 1 element is leaving the destructor blank, then no error shows, so I don't know what's causing it to fail. The error is showing up at the Insertion method, in the line that compares the elements in order to advance in the tree.
Update: Since this is part of a bigger project, I'm almost 100% sure that the problem isn't from the tree's implementation (I put the tree and Road class in a separate project and everything worked as intended). The full project has a class called Place, it has a name and info, as well as an AVL Tree for each place (where I store the place's roads). Those places are stored in a Hash Table (that I have implemented myself).
This is the implementation of the Place class:
Place.hpp
#ifndef Place_hpp
#define Place_hpp
#include <iostream>
#include "Road.hpp"
#include "RoadTree.hpp"
using namespace std;
class Place {
private:
string name;
string info;
RoadTree adjacentRoads;
public:
Place();
Place(string name, string info);
void InsertRoad(Road *r);
Road *FindRoad(string destination);
void ListRoads();
inline string GetName() {
return name;
}
inline string GetInfo() {
return info;
}
inline void SetPlace(string newName, string newInfo) {
name = newName;
info = newInfo;
}
inline void Write() {
cout << name << endl;
cout << "Info: " << info << endl;
}
};
Place.cpp
#include "Place.hpp"
Place::Place() {
name = "";
info = "";
}
Place::Place(string name, string info) {
this->name = name;
this->info = info;
}
void Place::InsertRoad(Road *r) {
adjacentRoads.Insert(r);
}
Road *Place::FindRoad(string destination) {
return adjacentRoads.FindRoad(destination);
}
void Place::ListRoads() {
adjacentRoads.ListRoads();
}
This is how I get a pointer from the Hash Table (if the full code is needed tell me):
Place *HashTable::Find(string key) {
unsigned long hashedKey = HashFunction(key);
list<Place>::iterator current;
for (current = table[hashedKey].begin(); current != table[hashedKey].end(); current++) {
Place currentPlace = *current;
if (currentPlace.GetName() == key)
return &*current;
}
return NULL;
}
And this is an example of a main that gives me the Thread 1: Use of deallocated memory detected. error
int main(int argc, const char * argv[]) {
//Declare a HashTable to store Places
HashTable map;
//Declare some places
Place p1("Murcia", "10");
Place p2("Lorca", "11");
Place p3("Cartagena", "12");
Place p4("Zaragoza", "13");
Place p5("Madrid", "14");
Place p6("Galicia", "15");
//Insert those places into the HashTable
map.Insert(p1);
map.Insert(p2);
map.Insert(p3);
map.Insert(p4);
map.Insert(p5);
map.Insert(p6);
//Declare some roads
Road *r1 = new Road(p2.GetName(), 20, "asdgasdg");
Road *r2 = new Road(p3.GetName(), 61, "asdgsw2");
//Get a pointer of a place from the HashTable to insert roads in it
Place *p1f = map.Find(p1.GetName());
//Check if it's not null, if it's not then insert the first road,
//get a pointer of it and print the name
if (p1f != NULL) {
p1f->InsertRoad(r1);
Road *r1f = p1f->FindRoad(p2.GetName());
cout << r1f->GetDestination() << endl;
}
//Get pointer of a place again (each time you want to insert a road
//in a place you must get it's pointer from the HashTable
Place *p2f = map.Find(p1.GetName());
//Checks again and insert second road, then throws error after that
if (p2f != NULL) {
p2f->InsertRoad(r2);
Road *r2f = p1f->FindRoad(p3.GetName());
cout << r2f->GetDestination() << endl;
}
return 0;
Update 2: Added HashTable implementation
HashTable.hpp
#ifndef HashTable_hpp
#define HashTable_hpp
#include "Place.hpp"
#include <list>
class HashTable {
private:
list<Place> *table;
int numberOfEntries;
int currentTableSize;
float maxLoadFactor;
unsigned int HashFunction(string key);
bool LoadFactorExceeded();
void ResizeTable();
bool IsPrime(int number);
int NextPrime(int number);
public:
HashTable();
~HashTable();
void Insert(Place p);
Place *Find(string key);
void EmptyTable();
void ListPlaces();
inline int Count() {
return numberOfEntries;
}
};
#endif /* HashTable_hpp */
HashTable.cpp
#include "HashTable.hpp"
#include <algorithm>
const int START_SIZE = 101;
HashTable::HashTable() {
table = new list<Place>[START_SIZE];
numberOfEntries = 0;
maxLoadFactor = 2.0f;
currentTableSize = START_SIZE;
for (int i = 0; i < START_SIZE; i++) {
table[i].clear();
}
}
HashTable::~HashTable() {
delete [] table;
}
unsigned int HashTable::HashFunction(string key) {
unsigned long hashValue = 0;
for (int i = 0; i < key.length(); i++)
hashValue = 47 * hashValue + key[i];
return (hashValue % currentTableSize);
}
bool HashTable::LoadFactorExceeded() {
float currentLoadFactor = numberOfEntries / currentTableSize;
if (currentLoadFactor > maxLoadFactor)
return true;
else
return false;
}
void HashTable::ResizeTable() {
list<Place> *oldTable = table;
int oldTableSize = currentTableSize;
currentTableSize *= 2;
currentTableSize = NextPrime(currentTableSize);
table = new list<Place>[currentTableSize];
for (int i = 0; i < currentTableSize; i++)
table[i].clear();
numberOfEntries = 0;
for (int i = 0; i < oldTableSize; i++) {
list<Place>::iterator current;
for (current = oldTable[i].begin(); current != oldTable[i].end(); current++)
Insert(*current);
}
delete [] oldTable;
}
bool HashTable::IsPrime(int number) {
if (number % 2 == 0 || number % 3 == 0)
return false;
int divisor = 6;
while (divisor * divisor - 2 * divisor + 1 <= number) {
if (number % (divisor - 1) == 0)
return false;
if (number % (divisor + 1) == 0)
return false;
divisor += 6;
}
return true;
}
int HashTable::NextPrime(int number) {
while (!IsPrime(++number)) {}
return number;
}
void HashTable::Insert(Place p) {
unsigned long hashedKey = HashFunction(p.GetName());
list<Place>::iterator current = table[hashedKey].begin();
if (!table[hashedKey].empty()) {
for (current = table[hashedKey].begin(); current != table[hashedKey].end(); current++) {
Place &currentPlace = *current;
if (currentPlace.GetName() == p.GetName()) {
currentPlace.SetPlace(p.GetName(), p.GetInfo());
break;
} else if (current == --table[hashedKey].end()) {
table[hashedKey].push_back(p);
numberOfEntries++;
}
}
} else {
table[hashedKey].push_back(p);
numberOfEntries++;
}
if (LoadFactorExceeded())
ResizeTable();
}
Place *HashTable::Find(string key) {
unsigned long hashedKey = HashFunction(key);
list<Place>::iterator current;
for (current = table[hashedKey].begin(); current != table[hashedKey].end(); current++) {
Place currentPlace = *current;
if (currentPlace.GetName() == key)
return &*current;
}
return NULL;
}
void HashTable::EmptyTable() {
for (int i = 0; i < currentTableSize; i++) {
table[i].clear();
}
table = new list<Place>[START_SIZE];
numberOfEntries = 0;
currentTableSize = START_SIZE;
}
void HashTable::ListPlaces() {
list<string> places;
for (int i = 0; i < currentTableSize; i++) {
list<Place>::iterator current;
for (current = table[i].begin(); current != table[i].end(); current++)
places.push_back(current->GetName());
}
places.sort();
for (list<string>::iterator current = places.begin(); current != places.end(); current++)
cout << *current << endl;
cout << "Total: " << numberOfEntries << " lugares" << endl;
}
What could be causing the problem?
I'm not sure if this is it, but I noticed something: it looks like a linked list, and your recursive ClearTree function will attempt to free items repeatedly:
void RoadTree::ClearTree(TreeNode *&n) {
if (n != NULL) {
ClearTree(n->rightChild);
ClearTree(n->leftChild);
delete n;
}
n = NULL;
}
Assuming there are 2 elements in the list, and we call it with the first element:
ClearTree( firstElement );
It will then first
ClearTree(n->rightChild); // 2nd element
which in turn will first call
ClearTree(n->rightChild); // non-existing 3rd element: NOP
and proceed with
ClearTree(n->leftChild); // first element again
Maybe if you didn't get the error, this would recurse until you get a stack overflow?
You could simply remove the call to ClearTree(n->leftChild) to fix it; the function will recurse across the rightChild until it reaches the end, then delete the nodes from last to first when it backtracks.
Perhaps it's better to just iterate over the list: (untested, hope this works)
TreeNode * cur = n;
while ( cur != NULL )
TreeNode * next = n->rightChild;
delete cur;
cur = next;
}
n = NULL;
UPDATE
I've found the problem. Here's my debug output:
> g++ -O0 -g *cpp && gdb ./a.out
(gdb) r
Starting program: /home/kenney/roadtree/a.out
= INITIALIZING PLACES =
--> RoadTree[0x7fffffffe1a0] CONSTRUCTOR root: 0
--> RoadTree[0x7fffffffe1c0] CONSTRUCTOR root: 0
--> RoadTree[0x7fffffffe1e0] CONSTRUCTOR root: 0
--> RoadTree[0x7fffffffe200] CONSTRUCTOR root: 0
--> RoadTree[0x7fffffffe220] CONSTRUCTOR root: 0
--> RoadTree[0x7fffffffe240] CONSTRUCTOR root: 0
= INSERTING PLACES =
<-- RoadTree[0x7fffffffe340] DESTRUCTOR! root: 0
<-- RoadTree[0x7fffffffe360] DESTRUCTOR! root: 0
<-- RoadTree[0x7fffffffe380] DESTRUCTOR! root: 0
<-- RoadTree[0x7fffffffe3a0] DESTRUCTOR! root: 0
<-- RoadTree[0x7fffffffe3c0] DESTRUCTOR! root: 0
<-- RoadTree[0x7fffffffe3e0] DESTRUCTOR! root: 0
= CREATING ROADS =
These are the p1..p6 and the map.Insert(p1..p6). There's already a hint that something is wrong. Next this code is run:
cout << "= p1 =\n";
Place *p1f = map.Find(p1.GetName());
cout << "found " << p1f << " for " << p1.GetName() << "\n";
Producing this debug output:
= p1 =
<-- RoadTree[0x7fffffffe110] DESTRUCTOR! root: 0
found 0x6098f0 for Murcia
Then,
if (p1f != NULL) {
p1f->InsertRoad(r1);
Road *r1f = p1f->FindRoad(p2.GetName());
cout << r1f->GetDestination() << endl;
}
outputting this debug from RoadTree::Insert, indicating that the first if statement's 'then' is executed, assigning a new TreeNode to n:
n null, allocating.
--> TreeNode[0x609ad0] CONSTRUCTOR
allocated TreeNode 0x609ad0 key: 0x609a60 dest: Lorca
Lorca
So far so good, now the same again for p2. First the output of map.Find:
= p2 =
FINDING Murcia
<-- RoadTree[0x7fffffffe110] DESTRUCTOR! root: 0x609ad0
!!! RoadTree::ClearTree:: delete 0x609a60
<-- TreeNode[0x609ad0] DESTRUCTOR
found 0x6098f0 for Murcia
Next we continue to p2f->InsertRoad(r2); which is basically Place.adjacentroads.Insert aka RoadTree.insert:
n not null: 0x609ad0 key: 0x609af0
Note the address of n: this is the deleted TreeNode.
Here, the 'else' of the 'if' in RoadTree::Insert is taken since n != NULL:
if (r->GetDestination() < n->key->GetDestination()) {
is executed, causing:
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7b9126b in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::string const&) ()
from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
(gdb) bt
#0 0x00007ffff7b9126b in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::string const&) ()
from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#1 0x00000000004046b3 in Road::GetDestination (this=0x609af0) at Road.hpp:20
#2 0x0000000000405121 in RoadTree::Insert (this=0x609900, n=#0x609900: 0x609ad0, r=0x609ab0) at RoadTree.cpp:75
#3 0x0000000000404c0d in RoadTree::Insert (this=0x609900, r=0x609ab0) at RoadTree.cpp:15
#4 0x0000000000404845 in Place::InsertRoad (this=0x6098f0, r=0x609ab0) at Place.cpp:14
#5 0x000000000040401d in main (argc=1, argv=0x7fffffffe5f8) at main.cpp:63
(gdb)
The fault is apparent in the n->key->GetDestination() which attempts to return a copy of a string that is already deleted, causing a segfault because some pointers are already overwritten.
The problem is in HashTable::Find, which does this:
Place currentPlace = *current;
if (currentPlace.GetName() == key)
return &*current;
which constructs a Place copy on the stack that gets destroyed when the method returns. The private fields of Place also get destroyed, including the string name, which was attempted to be returned by Road::GetDestination().
Replacing it with this with this solves it:
if (current->GetName() == key)
return &*current;
I'm not sure this is the only fix needed, but it's a step.