HashTable Implementation Get and Set Operator Overloading - c++

I'm trying to implement a basic hashtable. I'm using a linked list to resolve collisions.
My get and set methods are giving me a bunch of trouble, and I'm not really sure what the issue is. I believe I'm overloading the operators correctly. I think the problem happens when I append to my linked list.
class HashTable {
struct Node{
int key;
int value;
Node *next;
};
Node **table;
int hash_func(int key) const {
return key % TABLE_SIZE;
}
public:
HashTable() {
table = new Node*[TABLE_SIZE]();
for (int i = 0; i < TABLE_SIZE; ++i)
table[i] = nullptr;
}
int& operator[](int const key) {
int h_key = hash_func(key);
while(table[h_key]) {
table[h_key] = table[h_key]->next;
}
table[h_key] = new Node;
table[h_key]->key = key;
table[h_key]->next = table[h_key];
return table[h_key]->value;
}
int operator[](int const key) const {
int h_key = hash_func(key);
while (table[h_key]) {
if (table[h_key]->key == key) {
return table[h_key]->value;
}
table[h_key] = table[h_key]->next;
}
return 0;
}
};

Your problem is that you are overwriting data in your while loops in your get and set methods.
When you do table[h_key] = table[h_key]->next;, you permanently lose whatever was originally stored at location h_key. Instead use a placeholder, like this:
Node * curr = table[h_key];
while (curr->next)
{
curr = curr->next;
}
Node * new_node = new Node;
new_node->key = key;
curr->next = new_node;
You have a similar problem with your get method.

In your setter, you want to insert a new element at the end. So first you find the end like this:
while(table[h_key]) {
table[h_key] = table[h_key]->next;
}
But then, you set:
table[h_key]->next = table[h_key];
Instead, you would have to set:
table[h_key]->next = nullptr;
Otherwise, your condition in the loop does not work.
Consider this answer to an addendum to #bpachev's answer. My code only illustrates the problem using your wrong code, and is not a drop-in solution.

I agree with bpachev. Since this is C++, you should consider using templates for something like this (to removing clutter and reuse). Overhead for using template classes are minimal, and this process is done during compile time.

Related

I am having troubles linking multiple classes together via their pointers

I am having a few simple troubles regarding my code. What I am trying to do is build my own "list" code for 2 reasons. First of all, I want to get a deeper understanding of how linked lists work in practice. And secondly, because I am working on a project that requires a list of references, not just copied objects like the standard "list" library offers. I am also attempting to keep my code similiar to how C# works, while keeping the simplicity of python's naming scheme.
The following code is my issue:
// & - Get the address of object
// * - Get the contents of pointer
// int x = 25;
// int* p = &x;
// "p" now contains the pointer address
// "*p" contains the same info as x
// The following are all equal
// x = x + 5;
// x = *p + 5;
// *p = *p + 5;
template <class T> class Link;
template <class T> class Link {
public:
T result;
bool containsData = false;
Link<T> *x;
};
template <class T> class list {
public:
Link<T> start;
int count = 0;
Link<T> *blank(T object) {
Link<T> temp;
return &temp;
}
void append(T object) {
print("Starting");
Link<T> *temp = &start;
while (true) {
print("1");
if (temp->containsData == false) {
break;
}
print("2");
Link<T> *next = temp->x;
temp = next;
}
print("Doing");
temp->containsData = true;
temp->x = blank(object);
print("Done");
count++;
}
void pop(int index);
void clear();
void reverse();
T get(int index);
};
What is happening is that the first iteration works well. However, the second appended item in the list seems to halt at "1", seeming to be completely incapable of moving past "temp->containsData". I've tried putting it in a try-catch block, but it would seem that not even try-catch picks it up for some reason (Which surprised me quite a bit!). It just exits in the middle of trying to find out if the next item in the list is just another link to search through, or whether it is the last one in the list (Hence, containsData set to false).
I have checked, and yes it is the "temp->containsData" part that just crashes out. It seems trying to access "containsData" randomly crashes it. I am aware the code isn't too pretty, that's mainly from my debugging.
If anyone could throw me some pointers (Pun not-intended) as to how to continue, I would be incredibly grateful. Again, I do not want to just use the inbuilt "list" library and call it a day. I want to legit work through this coding trouble.
Thanking you in advance
Andrey :)
Link<T> *blank(T object) {
Link<T> temp;
return &temp;
}
is bad because it is returning a pointer to non-static local object, which will vanish on returning from this function.
Try this instead:
Link<T> *blank(T object) {
return new Link<T>();
}
Just like magic, the moment I post this, THEN stackoverflow decides to point me to a useful post. I've been looking online for hours!
Thanks for the (Surprisingly) quick responses! I managed to find a better way of doing it. Turns out that I was goind about it a bit of an unnecessarily complicated way. The following code seems to work:
template <class T> class Link {
public:
Link<T> *next;
T *item;
};
template <class T> class list {
public:
Link<T> *start = new Link<T>();
int count = 0;
void append(T *object) {
Link<T> *currentValue = start;
while (currentValue->next != nullptr) {
currentValue = currentValue->next;
}
currentValue->next = new Link<T>();
currentValue->item = object;
print(object);
count++;
}
void pop(int index) {
if (index >= count) { throw "index larger than size!"; }
if (index == 0) {
//Deleting the first item in the list
if (count == 1) {
start = new Link<T>();
} else {
start = start->next;
}
} else if (index == count - 1){
//Deleting the last item in the list
Link<T> *currentValue = start;
while (currentValue->next->next != nullptr) {
currentValue = currentValue->next;
}
currentValue->next = new Link<T>();
} else if (index == count - 1) {
//Deleting somewhere in the middle of the list
}
count--;
}
void clear() { start = new Link<T>(); count = 0; }
T get(int index) {
if (index >= count) { throw "index larger than size!"; }
int looper = 0;
Link<T> *currentValue = start;
while (looper != index) {
currentValue = currentValue->next;
looper++;
}
T *temp = currentValue->item;
return *temp;
}
};
Much smaller, works fine, links everything properly.
HOWEVER, thankyou to all the people pointing out that a pointer will disappear once the reference to it disappears. I completely forgot about that.
Thankyou so much for all your help, and I shall (hopefully) be able to finish this code pretty quickly :)

List implementation does not sum lists correctly

I am trying to dig into data structures in C++. Therefore I am learning how to write a list. Everything seemed to be working just fine until it came to overloading sum operator +. For two given lists it sums two of the highest values of the lists.
Here is .h file:
typedef struct rob{
int value;
struct rob* next;
}element;
class list{
public:
friend list& operator+(list&,list&);
friend void merge(list& x,list& y);
void show();
bool search(int x);
void append(int x);
bool sortAppend(int x);
list& operator--(int);
bool empty() { return (inf.head==nullptr);}
void clear() { inf.head = nullptr; }
list() { inf.head = inf.tail = nullptr; }
~list() { while(!empty()) { (*this)--;}}
private:
typedef struct{
element* head;
element* tail;
}info;
info inf;
};
I know that in an .h file the typedef may seem a bit C-like, but the header design is copied from the book that I am learning from. I am trying to crack the methods by my own though using authors ideas.
And relevant function definitions:
#include "list.h"
bool list::sortAppend(int x){
element* newElem = new element;
newElem->value = x;
if (empty()){
inf.head=inf.tail=newElem;
newElem->next=nullptr;
return true;
}
else if ( (newElem->value) < (inf.head->value) ){
newElem->next=inf.head;
inf.head=newElem;
return true;
}
else if ( (newElem->value) > (inf.tail->value) ) {
newElem->next=nullptr;
inf.tail->next=newElem;
return true;
}
element* tempHead = inf.head;
while(tempHead!=inf.tail){
if ( (newElem->value) < (tempHead->next)->value) {
newElem->next = (tempHead->next);
tempHead->next = newElem;
return true;
}
else{
tempHead = tempHead->next;
}
}
return false;
}
list& operator+(list& X, list& Y){
list* tempListArr[2] = {&X, &Y};
list* tempList = new list;
for(const list* i: tempListArr)
{
element* tempHead = (i->inf).head;
while(tempHead!= nullptr){
tempList->sortAppend(tempHead->value);
tempHead = tempHead->next;
}
tempList->show();
std::cout << "--\n";
}
return *tempList;
}
For given list containing values:
#include <iostream>
#include "list.cpp"
int main(){
list myList;
myList.sortAppend(5);
myList.sortAppend(2);
myList.sortAppend(4);
list myList2;
myList2.sortAppend(21);
list myList3;
myList3 = myList + myList2;
return 0;
}
Could anyone point me where I made a mistake? I am stuck for a few hours now and I don't know what goes wrong.
Many thanks in advance!
FOLLOW UP:
The sortAppend method surely works. It does create a sorted list as desired.
There must have been something wrong with the + operator definition itself though I have tried, instead of range loop, using for loop for one iteration and still I got a list of two values only.
You are simply not setting inf.tail to the new tail in
else if ((newElem->value) > (inf.tail->value)) {
newElem->next = nullptr;
inf.tail->next = newElem;
inf.tail = newElem; // <-- missing!
return true;
}
You should - at least - change the signature of operator+ to return a list instead of a list reference and return a local object instead of an unowned heap object (which is a memory leak). If you do so you will have to write a copy constructor and copy assignment operator too.
Given your code,
list myListWierd;
myListWierd.sortAppend(2);
myListWierd.sortAppend(4);
myListWierd.sortAppend(5);
myListWierd.show();
shows
2
5
so the sortAppend does not work.
The trouble is around updating either the tail, since operator + relies on using the tail.
I could sort the code out for you and make it work; indeed Andreas' answer does this. But for now, notice you have assumed a function works, but I found a case it doesn't work for by looking at the moving parts - a list we created, that we then try to re-create in a different order. As a general rule, try all the parts in a function that goes wrong, one at a time, maybe as a unit test.
Rather than fixing this, for now, let's make a couple of suggestions.
First, the destructor does nothing, other than walk pointers (using empty which uses head and not tail - so head needs setting as said before)
~list() { while (!empty()) { (*this)--; } }
If you don't want leaks you need to give this more thought.
Next,
list& operator+(list&, list&)
creates a pointer and returns its contents. This is a BAD IDEA. NEVER DO THIS.
For now, change the signature to
list operator+(list&, list&);
and just return a list:
list operator+(list& X, list& Y) {
list* tempListArr[2] = { &X, &Y };
list tempList;//copy it over to the calling vode otherwise DANGER
for (const list* i : tempListArr)
{
element* tempHead = (i->inf).head;
while (tempHead != nullptr) {
tempList.sortAppend(tempHead->value);
std::cout << "Adding " << tempHead->value << '\n';
tempHead = tempHead->next;
}
tempList.show();
std::cout << "--\n";
}
return tempList;
}

Linked list gets corrupted when I instantiate a Node immediately after calling append

I would like to know what may I be misunderstanding in the code below (the commented out lines). If I try to create the Node before going through the linked list, the values from root->next get messed up, from the second iteration onward.
The way the code is right now works, but I don't see why the other way doesn't.
template <class T>
class Node
{
public:
T data;
Node* next;
Node(T nodeData) : data(nodeData) { next = nullptr; }
void append(T nodeData)
{
// If I uncomment this, I get the problem
//Node newNode (nodeData);
Node* insertionNode = this;
while(insertionNode->next != nullptr)
{
insertionNode = insertionNode->next;
}
// Instead of using newNode, I must create the node here
// insertionNode->next = &newNode;
insertionNode->next = new Node(nodeData);
}
};
int main()
{
int testList[] = {1,2,3,4,5};
Node<int> rootNode(0);
for(int i = 0; i < 5; i++)
{
rootNode.append(testList[i]);
}
return 0;
}
You are creating newNode on the stack. After append has finished, it will no longer exist. By saying new Node(nodeData); you are essentially placing it on the heap, which gives you control over the objects life cycle.

Figuring out Insert function parameter for linked list?

My teacher gave the class for a driver to complete a program, and I'm unsure how to code the insert function because of it.
The line giving me trouble:
you.Insert(me,0);
you is for the default constructor and me is for an explicit value constructor, so this line is supposed create a node in you with the contents of me.
I'm lost understanding how to write the parameter to access me for my insert function
void WRD::Insert( ?, int new_data)
I'll include the explicit constructor I have, any insight understanding this mentally will help. (included example of what insert should look like or do based on an example I was given.)
WRD::WRD(const string & s)
{
cout<<"one called\n";
front = 0;
for(unsigned i=0; i<s.length(); i++)
{
AddChar(s[i]);
}
}
class node
{
public:
char symbol;
node * next;
};
v
oid Insert(node * &ptr, int new_data)
{
node *new_ptr = new node;
new_ptr -> data = new_data;
new_ptr -> next = 0; //always initialize a pointer
if (Empty(ptr))
{
ptr = new_ptr;
}
else if (new_ptr->data <= ptr->data)
{
new_ptr->next = ptr;
ptr = new_ptr;
}
else
{
node *fwd_ptr=ptr, *pre_ptr=ptr;
while(fwd_ptr!=0 && (fwd_ptr->data < new_ptr->data))
{
pre_ptr = fwd_ptr;
fwd_ptr = fwd_ptr->next;
}
if (fwd_ptr == 0)
{
pre_ptr->next = new_ptr;
}
else
{
new_ptr->next = fwd_ptr;
pre_ptr->next = new_ptr;
}
}
}
Like this I think (assuming I've understood you right)
void WRD::Insert(const WRD& w, int new_data)
It might help to show more of your driver program, in particular how you and me are declared.

push_front() as a linked list member function

How to implement push_front() method for a singly linked list as its member function? The code below does not compile (error: lvalue required as left operand of assignment), because you cannot assign to this pointer. What is a way round this?
#include<algorithm>
using namespace std;
class ListElem{
public:
ListElem(int val): _val(val){}
ListElem *next() const { return _next; }
void next(ListElem *elem) { _next = elem; }
void val(int val){ _val = val; }
int val() const { return _val;}
void print();
void push_front(int);
private:
ListElem *_next;
int _val;
};
void ListElem::push_front(int val)
{
ListElem *new_elem = new ListElem(val); //new node
new_elem->next( this ); // new node points to old head
this = new_elem; // make new node the new head, error!
return;
}
void ListElem::print()
{
ListElem *pelem = this;
while(ListElem *pnext_elem = pelem->next())
{
cout << pelem->val() << ' ';
pelem = pnext_elem;
}
cout << pelem->val() << endl;
}
int main()
{
//initialization
ListElem *head = new ListElem(1);
ListElem *elem = head;
for (int ix = 2; ix < 10; ++ix)
{
ListElem *elem_new = new ListElem(ix);
elem -> next(elem_new);
elem = elem_new;
}
head->print();
//insert at the beginning
head->push_front(7);
head->print();
}
Logically, push_front() must be a method of List class and not of a ListElement class
You're using this incorrectly. You want to have a static member called, say, ListElem *head and use that where you were using this. You'll also have to initialise it.
If you really want to do it that way, you can do it like this:
void ListElem::push_front(int val)
{
ListElem *new_elem = new ListElem(_val);
_val = val;
new_elem->next(_next);
_next = new_elem;
}
This will replace the data in the "current" node with the new data, and move the "current" data to the new node, which will yield the same list content.
But it's not really correct to conflate a list with its nodes.
The book you linked takes a very non-OO approach to the whole thing (both the Java and the C++ examples look like transliterated C), and conflating the type of a list with the type of its nodes is pretty certain to lead to bugs later.
For instance, if you do this
ListElem* x = head;
head->push_front(99);
then the contents of *x will have changed, which isn't really what you would expect.