I have to implement a HashTable with linked lists. It is almost done (still missing templates, will do in time), but I'm stuck at something.
Basically, I want to resize my hashtable when the load factor reaches a given value, say 50%. But I don't know how I should do it.
I have a basic idea:
Create a temporary HT with the new size
Hash every data in every list from the old HT to the temporary one
Delete the old HT
Return the temporary HT
I can't figure out an implementation for it though...
Here is what I have so far:
//List:
struct Node
{
string data;
Node *next;
};
class List
{
private:
Node *head, *tail;
int length;
friend class HashTable;
public:
List();
List(const List &L);
//~List() {delete this;};
List& operator =(List L);
int find(string);
void insert(string value);
void remove_head();
void remove_poz(int);
void remove_tail();
void clear();
void display();
};
List::List()
{
head = NULL;
tail = NULL;
length = 0;
}
List::List(const List& L)
{
Node** temp = &head;
const Node* source = L.head;
while(source)
{
*temp = new Node(*source);
temp = &(*temp)->next;
source = source->next;
}
}
List& List::operator =(List L)
{
swap(head, L.head);
return *this;
}
void List::insert(string value)
{
Node* temp = new Node;
temp->data = value;
temp->next = NULL;
if (!head)
head = temp;
if (tail)
tail->next = temp;
tail = temp;
length++;
}
void List::display()
{
Node *temp = new Node;
temp = head;
while (temp != NULL)
{
cout<<temp->data<<" ";
temp = temp->next;
}
delete temp;
}
//HashTable:
class HashTable
{
private:
List *table;
float load, stored;
int slots;
friend class List;
public:
HashTable();
HashTable(int);
~HashTable();
int hashFunc(string key);
int findTable(string);
int findList(string);
HashTable& operator =(const HashTable&);
void resize(); //I need this one
void insert(string);
void remove(string);
void clear(int);
void clear();
void display();
};
HashTable::HashTable()
{
stored = 0;
load = 0.00;
slots = 15;
table = new List[slots];
}
HashTable::HashTable(int size)
{
stored = 0;
load = 0.00;
slots = size;
table = new List[slots];
}
int HashTable::hashFunc(string key)
{
unsigned int i, ind = 0;
for (i = 0; i<key.length(); ++i)
ind = ind + key[i];
ind %= slots;
return ind;
}
HashTable& HashTable::operator =(const HashTable& T) //I suppose it is incorrect
{
int i;
HashTable temp(T.slots);
for (i = 0; i < slots; ++i)
{
temp.table[i] = T.table[i];
}
return temp;
}
void HashTable::insert(string value)
{
int ind = hashFunc(value);
table[ind].insert(value);
if (!table[ind].head->next) stored++;
load = stored / slots;
if (load > 0.50) resize();
}
(Note: only the functions that might be needed are shown here)
Any help, correction or suggestion would be much appreciated :)
UPDATE:
Managed to pull this one off:
void HashTable::resize()
{
int i;
int newSize = slots * 2;
int newLoad = stored / newSize;
HashTable HT(newSize);
Node* temp;
for (i = 0; i < slots; ++i)
{
temp = table[i].head;
while (temp != NULL)
{
HT.insert(temp->data);
temp = temp->next;
}
}
}
Now I have a new HashTable named HT, with double the size of the original one, and all elements have been inserted correctly.
But I don`t know how to proceed.
The easiest way to proceed is to add a swapContents() method to your HashTable class:
void HashTable::swapContents(HashTable & rhs)
{
std::swap(table, rhs.table);
std::swap(load, rhs.load);
std::swap(stored, rhs.stored);
std::swap(slots, rhs.slots);
// any other member-variables of the HashTable class would get swapped here too
}
... then call swapContents(HT) at the end of your resize() method, so that HT becomes the older/smaller HashTable (and gets discarded) while this becomes the newer/larger table.
Related
I had to implement a Linked HashTable for a project. Now I have to come up with an excercise and a solution to it using my hashtable. Everything works just fine, except I get random Segfault errors.
By Random I mean: It is the same line of code that causes it, but always at different times, calls.
I tested my code in Atom, Codeblocks and in Visual Studio Code. Both Atom and CB threw SegFault error, but VS Code ran it just fine without a problem.
NOTE: THIS IS NOT THE FULL/REAL CODE. It's part of a header file that is included in the main.cpp file which is then compiled and ran.
The Code:
#include <iostream>
#include <typeinfo>
#include <string>
using namespace std;
//List:
template<class T>
struct Node
{
string data;
Node *next;
};
class List
{
private:
Node *head, *tail;
int length;
friend class HashTable;
public:
List();
List(const List &L);
//~List() {delete this;};
List& operator =(List L);
int find(string);
void insert(string value);
void remove_head();
void remove_poz(int);
void remove_tail();
void clear();
void display();
};
List::List()
{
head = NULL;
tail = NULL;
length = 0;
}
template<>
string List<string>::findByIndex(int ind)
{
int i = 0;
Node<string>* temp = new Node<string>;
temp = head;
while (temp != NULL)
{
i++;
if (i == ind) return temp->data;
temp = temp->next;
}
delete temp;
return "-1";
}
template<class T>
void List<T>::remove_head()
{
Node<T>* temp = new Node<T>;
temp = head;
head = head->next;
delete temp;
length--;
}
template<class T>
void List<T>::remove_pos(int pos)
{
int i;
Node<T>* curr = new Node<T>;
Node<T>* prev = new Node<T>;
curr = head;
for (i = 1; i < pos; ++i)
{
prev = curr;
curr = curr->next;
}
if (curr)
{
prev->next = curr->next;
length--;
}
else cout << "Error" << endl;
}
template<class T>
void List<T>::remove_tail()
{
Node<T>* curr = new Node<T>;
Node<T>* prev = new Node<T>;
curr = head;
while (curr->next != NULL)
{
prev = curr;
curr = curr->next;
}
tail = prev;
prev->next = NULL;
delete curr;
length--;
}
//HashTable:
class HashTable
{
private:
List *table;
float load, stored;
int slots;
friend class List;
public:
HashTable();
HashTable(int);
~HashTable();
int hashFunc(string key);
int findTable(string);
int findList(string);
HashTable& operator =(const HashTable&);
void resize(); //I need this one
void insert(string);
void remove(string);
void clear(int);
void clear();
void display();
};
HashTable::HashTable()
{
stored = 0;
load = 0.00;
slots = 15;
table = new List[slots];
}
int HashTable::hashFunc(string key)
{
int g, h = 0;
unsigned int i;
for (i = 0; i < key.size(); ++i)
{
h = (h << 4) + (int)(key[i]);
g = h & 0xF0000000L;
if (g != 0)
{
h = h ^ (g >> 24);
}
h = h & ~g;
}
return h % slots;
}
template<class T>
void HashTable<T>::remove(T value)
{
int ind = hashFunc(value);
int findInd = table[ind].findByValue(value);
if (findInd == 0)
table[ind].remove_head();
else if (findInd < table[ind].length)
table[ind].remove_pos(findInd);
else table[ind].remove_tail();
if (table[ind].isEmpty()) occupied--;
stored--;
load = stored / slots;
}
The function that would cause the segfault:
(This would be called over and over again in a loop till I don't have more elements in my table)
string reakcio(HashTable<string>& HT, int tarolok)
{
const int anyagszam = rand() % 4 + 2; //Min 2, Max 5 anyag hasznalodik
int i = 0, j;
string anyagok[5];
string eredmeny;
for(j = 0; j < tarolok && i < anyagszam; ++j) //elemek kivetele
{
while(!HT.table[j].isEmpty())
{
anyagok[i++] = HT.table[j].findByIndex(1); //This line right here is the culprit :(
HT.remove(anyagok[i-1]);
}
}
const int siker = rand() % 4 + 0; //75% esely a sikerre
if (siker)
{
eredmeny = anyagok[0];
for(i = 1; i < anyagszam; ++i)
eredmeny += " + " + anyagok[i];
}
else
eredmeny = "Sikertelen reakcio";
return eredmeny;
}
(Note: only the functions that might be needed are shown here)
Every element of my hashtable, or of my lists is a 10 character long random string value.
srand(time(NULL)) is used before the function call in main.cpp
Any help or advice would be much appreciated, as I'm stuck at this and I really need to move on to the next portion of my exercise, but I can't without this.
The main.cpp file:
#include <iostream>
//#include "LinkedHash.h"
#include "functions.cpp"
int main()
{
HashTable<string> Anyagok;
int tarolok;
tarol(Anyagok); //Stores the data from file, no problem here, functions.cpp
tarolok = Anyagok.getSlots();
srand(time(NULL));
int i = 1;
while (Anyagok.getStored() > 5 )
cout<<reakcio(Anyagok, tarolok)<<" "<<i++<<endl;
return 0;
}
The LinkedHash.h contains the hashtable and the list, the functions.cpp contains the problematic function.
EDIT:
By suggestion I changed out the
Node<string>* temp = new Node<string>;
temp = head;
part to
Node<string>* temp = head;
Also removed the delete line.
But my problem is still the same :/
Everything works just fine, except I get random Segfault errors
Then nothing works at all.
A first review show little care to the cornercases in the list class. You need to define a correct behavior for
operation on empty lists
operation on first and last element
key not found during search
Notable errors found:
remove_head, remove_tail will segfault on empty list. head is NULL. head->next is invalid memory access. Similar errors are all over the implementation.
HashTable<T>::remove(T value) will always remove something. Even if the value argument is not in the hashtable. This is deeply flawed
findByIndex returning "-1" make no sense. "-1" is a valid input.
Node<T>* temp = new Node<T>;temp = head;. You just leaked memory. You need a pointer to manipulate node addresses. You should not instantiate Nodes to get a pointer. This is not an issue (ie not noticeable) for a small projet, but unacceptable for a real implementation.
I want to remove one node from a Circular Single Linked List and when i reach to the Cmd, it shows me an address (maybe) which is repeating infintely. Please help me or show me what is wrong. Here is the code in C++
using namespace std;
class Node{
private:
int data;
Node * next;
public:
void setdata(int s);
int getdata()
{
return data;
}
void setnext(Node * next_pointer);
Node * getnext();
};
void Node::setdata(int s){
data = s;
}
Node * Node::getnext(){
return this->next;
}
void Node::setnext(Node *next_node)
{
this->next = next_node;
}
class Circular{
private:
Node * first;
int sizen;
public:
Circular();
void append(int value);
void display();
Node * walk(int start, int die=3);
int remove_node(Node * prev_node);
int getsize();
Node * get_first();
void removing(int val);
};
Circular::Circular(){
first = 0;
sizen = 0;
}
void Circular::append(int val)
{
Node * new_node = new Node;
new_node -> setdata(val);
if(this->sizen == 0 )
{
//There is empty
this->first = new_node;
this->first -> setnext(this->first);
this->sizen = 1;
}
else
{
//It's not empty
Node *node = this->first;
while(node->getnext()!= this->first)
{
node = node->getnext();
}
node->setnext(new_node);
node->getnext() -> setnext(this->first);
this->sizen +=1;
}
}
void Circular::display()
{
Node *temp = this->first;
std::cout<<temp->getdata()<<" ";
temp = temp->getnext();
while(temp!=this->first)
{
std::cout<<temp->getdata()<<" ";
temp = temp->getnext();
}
}
void Circular::removing(int val)
{
Node *new_node = this->first, *d;
/*if(new_node->sizen==0)
return 0;
if(new_node->sizen==1 && new_node->getdata() == val)
{free(new_node);
return 0;
}*/
std::cout<<"Removing "<<val<<std::endl;
while((new_node->getnext())->getdata()!=val)
{
new_node = new_node->getnext();
}
if((new_node->getnext())->getdata()==val)
{
d = new_node->getnext();
new_node->getnext() ->setnext(d->getnext());
free(d);
}
}
int Circular::getsize(){
Node *temp = this->first;
int length = 0;
length++;
while(temp->getnext()!=this->first)
{
temp = temp->getnext();
length++;
}
return length;
}
int main(){
Circular l;
int i, n,k;
std::cout<<"How many participants do you want? ";
std::cin>>n;
std::cout<<"Which participant should be killed? (Kth)";
std:cin>>k;
for(i=1;i<=n;i++)
l.append(i);
l.display();
l.removing(2);
l.display();
}
And Here is where the problem gets me anxious:
void Circular::removing(int val)
{
Node *new_node = this->first, *d;
/*if(new_node->sizen==0)
return 0;
if(new_node->sizen==1 && new_node->getdata() == val)
{free(new_node);
return 0;
}*/
std::cout<<"Removing "<<val<<std::endl;
while((new_node->getnext())->getdata()!=val)
{
new_node = new_node->getnext();
}
if((new_node->getnext())->getdata()==val)
{
d = new_node->getnext();
new_node->getnext() ->setnext(d->getnext());
free(d);
}
}
I have no error and when I run the program, I want to remove the Second (2) node, but it shows me:
1 1114304 1114304 1114304 1114304 1114304 and so on.
I am trying to implement a skiplist in cpp . There are many versions of skiplist available but I particularly want to implement a version where each node has a right and down pointer to form a connected list at various levels . Also at each higher level there is a replica of node rather than just a pointer.
I am giving my code that I have implemented uptill now. There is only one function that I have implemented till now i.e insertion. But I am getting segmentation fault. I know I am messing somewhere with pointers somewhere either in constructor, update or insert functions. Can somebody please help.
class SkipList
{
private:
struct node {
int key;
int data;
int level;
struct node* rgt = nullptr;
struct node* dwn = nullptr ;
node(int k, int value, int l):
key(k), data(value), level(l)
{}
};
//generates the ndde level in tha range [1,maxLevel).
int randomLevel() const;
//returns a set of pointers to the location at each node where new links are to be created
std::vector<node*> update(int searchKey) const ;
//creates a new node and returns a pointer to it
static node* makeNode(int key, int val, int level);
const float probability;
const int maxLevel;
// head and tail vectors
vector<node*> head;
vector<node*> nil;
public:
SkipList();
~SkipList();
void insert(int searchKey, int val);
void print() const;
};
SkipList::SkipList() :
probability(0.5), maxLevel(16)
{
int headkey = std::numeric_limits<int>::min();
int nilkey = std::numeric_limits<int>::max();
for(int i = 0; i < maxLevel;i++)
{
head[i] = new node(headkey,0,maxLevel-1);
nil[i] = new node(nilkey,0,maxLevel-1);
if(i > 0)
{
head[i]-> dwn = nil[i-1];
nil[i] -> dwn = nil[i-1];
}
head[i]->rgt = nil[i];
}
}
void SkipList::insert(int searchKey, int val)
{
vector <node*> preds = update(searchKey);
node* temp;
const int newLevel = randomLevel();
for(int i = 0; i< newLevel; i++)
{
node* ptr = makeNode(searchKey,val, newLevel-1);
temp = preds[i]->rgt;
preds[i]->rgt = ptr;
ptr->rgt = temp;
}
}
void SkipList::print() const{
node* list = head[0]->rgt;
int lineLength = 0;
std::cout<<"{";
while (list->rgt != nil[list->level])
{
std::cout<<"value: "<<list->data
<<", key: "<<list->key
<<", level: "<<list->level;
list = list->rgt;
if(list->rgt != nil[list->level]) std::cout<<" : ";
if (++lineLength % 2 == 0) std::cout << "\n";
}
std::cout << "}\n";
}
int SkipList::randomLevel() const{
int v = 1;
while (((double)std::rand() / RAND_MAX) < probability
&& v < maxLevel)
{
v++;
}
return v;
}
SkipList::node* SkipList::makeNode(int key, int value, int level){
return new node(key, value, level);
}
std::vector<SkipList::node*>SkipList::update(int searchKey) const{
int level = head[0]->level;
std::vector<node*> result(level,nullptr);
node* x ;
for(unsigned int i = level;i-- >0;)
{
x = head[i];
while(x->rgt->key < searchKey)
{
x = x->rgt;
}
result[i]= x;
}
return result;
}
int main()
{
SkipList s;
s.insert(5,22);
s.insert(2,33);
s.print();
return 0;
}
You should use push_back method in ctor of SkipList. Now you are creating objects
head[i] = new node(headkey,0,maxLevel-1);
and you are trying to assign the created node object to object returned by vector::operator[] which doesn't exist.
Or you can invoke vector::resize(maxlevel) method before entering into for loop.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
Let me first start by saying I do not have access to debuggers and I'm using Nano as my editor
Currently, with my calculator, I am beating my head against a brick wall(segmentation fault). I've tried going through my pointers to discover what my problem is, but my lack of experience/knowledge has only gotten me so far. Let me explain what works so far in my program. Currently, I am able to store hexadecimal numbers in a linked list and add them together. The problem comes from my multiplication method.Somehow leftNode is becoming NULL midway through the multiplication method throwing a segmentation fault. I'm wondering at what point does leftNode become NULL?
Multiplication Method:
LList Calculator::multiply(LList& left, LList& right) {
LList prodSum;
listnode *leftNode = (left.next());
int zeros = 0;
for(;;) {
if(leftNode == NULL) break;
int lval = leftNode->data;
LList curList;
for(int i = 0; i < zeros; i++) {
curList.insertTail(0);
}
right.reset();
listnode *rightNode = (right.next());
int carry = 0;
while(rightNode != NULL) {
int rval = rightNode->data;
int product = lval * rval + carry;
carry = product / 16;
product %= 16;
curList.insertTail(product);
rightNode = (right.next());
}
while(carry) {
curList.insertTail(carry % 16);
carry /= 16;
}
prodSum = *add(prodSum, curList);
leftNode = (left.next()); // eventually causes a segmentation fault
leftNode->data << endl;
++zeros;
}
return prodSum;
}
Classes related to multiplication:
class listnode {
public:
element data;
listnode * next;
};
class LList {
private:
listnode * head;
listnode * tail;
listnode * view;
public:
LList();
~LList();
void read();
listnode* next();
void reset();
void print();
void insertTail(element val);
void clean();
element deleteHead();
};
class Calculator {
public:
Calculator();
//inline LList* add(LList& left, LList& right); works
inline LList multiply(LList& left, LList& right);
};
Calculator::Calculator() {
};
Other methods related to traversing nodes:
listnode* LList::next() {
listnode* temp = view;
if(temp != NULL)
view = view->next;
if(view == NULL) {
}
return temp;
};
void LList::reset() {
view = head;
}
LList::LList(){
head = NULL;
view = NULL;
};
void LList::insertTail(element val) {
listnode * temp;
temp = new listnode;
temp -> data = val;
temp -> next = NULL;
if(head == NULL) {
head = temp;
view = head;
}
else
tail -> next = temp;
tail = temp;
};
void LList::clean() {
while(head != NULL)
deleteHead();
};
element LList::deleteHead() {
listnode * temp;
temp = head;
head = head -> next;
delete temp;
return temp -> data;
};
LList::~LList(){
delete head;
};
it's me again.
One exception occurs after the line you marked: // eventually causes a segmentation fault, there seems to be a partially-formed line for sending leftNode->data to cout, but on the final iteration through left's nodes, leftNode = (left.next()); will set leftNode to NULL, so a dereference here might be causing the fault.
One other problem is that no copy constructor or assignment operator is defined for LList, so this line: prodSum = *add(prodSum, curList); will give prodSum a set of list nodes that will be deleted right after.
However, LList's destructor only seems to delete the head node, not the whole list, so there's a grab-bag of invalid and valid going on.
Also, multiply returns prodSum, so the lack of a copy constructor will make something similar happen.
I'm including a version of your code that seems to work. I had to make my own add function, just because I don't see it here.
I made the destructor delete all of the LList's nodes.
I marked the default copy constructor and assignment operator =delete because the default implementations do the wrong thing.
In order to pass LList objects around by value, I added a move constructor and a move assignment operator. These pass allocated nodes from one object to another, and only one object is allowed to keep one set of nodes, so you don't have to worry about double-destruction.
#include <iostream>
#include <string>
typedef int element;
class listnode {
public:
element data;
listnode * next;
};
class LList {
listnode *head, *tail, *view;
public:
LList() { head = view = tail = NULL; }
LList(LList&& src) : head(src.head), tail(src.tail), view(src.view) { src.head = src.tail = src.view = nullptr; }
LList(const LList&) = delete;
~LList() { clean(); }
LList& operator = (LList&& src) {
clean();
/* OK here */
head = src.head;
tail = src.tail;
view = src.view;
src.head = src.tail = src.view = nullptr;
return *this;
}
LList& operator = (const LList&) = delete;
listnode* next() {
listnode* temp = view;
if(temp) view = view->next;
return temp;
}
void reset() { view = head; }
void print();
void insertTail(element val) {
listnode* temp = new listnode;
temp->data = val;
temp->next = NULL;
if(!head) { view = head = temp; }
else { tail->next = temp; }
tail = temp;
}
void clean() { while(head) deleteHead(); }
element deleteHead() {
listnode* temp = head;
head = head->next;
const element data = temp->data;
delete temp;
return data;
}
};
LList add(LList& left, LList& right) {
LList sum;
int carry = 0;
left.reset();
right.reset();
for(;;) {
const listnode* leftNode = left.next();
const listnode* rightNode = right.next();
if(!leftNode && !rightNode) break;
if(leftNode) carry += leftNode->data;
if(rightNode) carry += rightNode->data;
sum.insertTail(carry % 16);
carry /= 16;
}
if(carry) sum.insertTail(carry);
return sum;
}
LList multiply(LList& left, LList& right) {
LList prodSum;
listnode *leftNode = left.next();
int zeros = 0;
for(;;) {
if(!leftNode) break;
int lval = leftNode->data;
LList curList;
for(int i = 0; i < zeros; i++) {
curList.insertTail(0);
}
right.reset();
listnode *rightNode = right.next();
int carry = 0;
while(rightNode) {
int rval = rightNode->data;
int product = lval * rval + carry;
carry = product / 16;
product %= 16;
curList.insertTail(product);
rightNode = right.next();
}
while(carry) {
curList.insertTail(carry % 16);
carry /= 16;
}
prodSum = add(prodSum, curList);
leftNode = left.next(); // eventually causes a segmentation fault
//std::cout << leftNode->data << std::endl;
++zeros;
}
return prodSum;
}
LList string_to_list(std::string hex_string) {
LList list;
for(size_t i=hex_string.length()-1; i+1; --i) {
char c = hex_string[i] | 0x20;
if (c >= '0' && c <= '9') list.insertTail(c - '0');
else if(c >= 'a' && c <= 'f') list.insertTail(c - 'a' + 10);
}
return list;
}
std::string list_to_string(LList& list) {
std::string hex_string;
list.reset();
for(;;) {
listnode* node = list.next();
if(!node) return hex_string;
static const char digits[] = "0123456789abcdef";
hex_string = digits[node->data] + hex_string;
}
}
int main() {
//LList list = string_to_list("1234aBcd");
//std::string s = list_to_string(list);
//std::cout << s << '\n';
LList left = string_to_list("111");
LList right = string_to_list("333");
LList prod = multiply(left, right);
std::cout << list_to_string(prod) << '\n';
}
I have a function that removes from a linked list. Each node in the linked list is a dynamically created struct. To remove, I pass a data value in that I would like to search for in the list of nodes. If one of those nodes contains that data, I want to remove the entire node containing that data. It appears like they get added fine, but whenever I remove, the size variable decrements but the nodes are still in the list.
In Mag.h:
struct ListNode
{
int* data;
ListNode* nextNode;
};
class Mag
{
private:
ListNode* head;
public:
Mag();
Mag(Mag& mag);
Mag &operator= (const Mag &);
~Mag();
int size;
void add(const int&);
void remove(const int&);
void printList();
};
}
I add nodes to the list like this:
// adds to front of list
void Mag::add(int const &num)
{
Node* new_data = new ListNode();
new_data->data = num;
new_data->next = head;
head = newNode;
size++;
}
Now here's how I remove them (probably the issue):
void Mag::remove(int const &num)
{
if (head == NULL)
return;
int look_for = num;
ListNode* searchFor = head;
int count = 0;
count = size;
if (count != 0)
{
do
{
if (searchFor->data == look_for)
{
ListNode* delete_node = new ListNode;
delete_node = searchFor;
searchFor = searchFor->next;
size--;
delete delete_node;
return;
}
searchFor = searchFor->next;
count--;
} while (count != 0);
}
}