Inserting a node in a hashtable - c

The code below is correct but I do not understand why 2 lines of the code work. I am referring to the last else block. Specifically, I am referring to these 2 lines:
newWord->next = hashtable[index];
hashtable[index] = newWord;
If the goal is to append the node to the linked list at an index of the hash table, why is newWord->next pointing to the index of the hashtable when there are presumably nodes already at that index. I would think it should be newWord->next = NULL since that node would be the last link in the linked list and, therefore, should point to NULL. From the code, it looks like the "next" field of the struct is referencing the index. I hope I'm making sense.
/**
* Loads dictionary into memory. Returns true if successful else false.
*/
bool load(const char* dictionary)
{
// TODO
// opens dictionary
FILE* file = fopen(dictionary, "r");
if (file == NULL)
return false;
// create an array for word to be stored in
char word[LENGTH+1];
// scan through the file, loading each word into the hash table
while (fscanf(file, "%s\n", word)!= EOF)
{
// increment dictionary size
dictionarySize++;
// allocate memory for new word
node* newWord = malloc(sizeof(node));
// put word in the new node
strcpy(newWord->word, word);
// find what index of the array the word should go in
int index = hash(word);
// if hashtable is empty at index, insert
if (hashtable[index] == NULL)
{
hashtable[index] = newWord;
newWord->next = NULL;
}
// if hashtable is not empty at index, append
else
{
newWord->next = hashtable[index];
hashtable[index] = newWord;
}
}

Your assumption that the new node is appended to the end is wrong. The code inserts the new node at the front of the linked list, effectively making it the new head. The "tail" is the old list and its head is now the node after the new head.
This kind of insertion is faster, because you don't have to walk the list to find the end. The order of the nodes doesn't matter here.
You don't even need the distinction in if (hashtable[index] == NULL); you can collapse the two cases into one, namely the code in the else clause.

Related

C how to iterate and add a new node after a desired node in single Linked List

typedef struct ITEM {
int value;
struct ITEM *next;
}ITEM;
int add_after(ITEM *list, ITEM *c_item, int value)
{
ITEM *elem;
//if head is NULL, it is an empty list
if(c_item == NULL || list == NULL)
{
return -1;
}
//create a new node
elem = malloc(sizeof(ITEM));
if(elem == NULL)
{
return -1;
}
elem->value = value;
// iteration
while(list != NULL)
{
if(list == c_item)
{
elem->next = list->next;
c_item->next = elem;
return 1;
}
list = list->value;
}
return 0;
}
What it should do:
c_item should be equal to one of the exiting nodes in the list. After finding an equal, it should make a new node elem and put that node infront of c_item.
What my code does:
list is HEAD value and compared with the c_item value and if its not the same then list goes to the next value in the list. After finding the equal, then it adds elem node to the list infront.
Problem:
This only works with 1 existing node in the list. If there is 2 nodes then it gets stuck at list = list->value;.
// Linked List 10->20->30->NULL
//Input:
c_item = 20
elem = 100
//Output:
10->20->100->30->NULL
You seem to have the basics of most of what you want to do in place. Let's try to to reorganize your pieces, and perhaps you can get close to your desired solution.
I would first use comments to describe what I want to accomplish in the function.
int add_after (ITEM *list, ITEM *c_item, int value) {
// Check c_item and list are not NULL
// Check if c_item is in list
// Create new elem with value
// Add new elem after c_item
}
Then, I would write the code that accomplishes each of these things.
Check c_item and list are not NULL
if (! (c_item && list)) return -1;
Check if c_item is in list
if (! list_has_item(list, c_item)) return -1;
Create new elem with value
ITEM *elem = create_item(value);
if (elem == NULL) return -1;
Add new elem after c_item
ITEM *old_after = c_item->next;
c_item->next = elem;
elem->next = old_after;
This last bit actually the specific question you ask about. The implementation remembers what used to be after the the c_item, makes the new element to be after it instead, then puts what used to be after c_item after the new element.
It is possible to make the same thing happen without the temporary. It is debatable what is more clear, but if you are curious, feel free to try to rewrite the three lines of above into two lines instead.
I think you already understand how to check if an item is in the list and how to create a new item, so I will leave those up to you to implement.

Delete a string out of a linked list of strings

I am working on a word processor where it is requested to be able to delete a word out of a list of words.
Basically, the user enters words (thus, strings of characters) which are then stored in a linked list (here dico, thanks to the structure dictionary which represents all the words that the user has entered).
I am unfortunately stuck : it seems like the code I wrote only ever deletes the second character, whereas I would like it to be able to delete the word requested by the user (here : str).
For instance, if the user had previously entered : "hello world" and they would now like to delete the world "world", the dico should now be "hello".
typedef struct dll {
char data;
int count; //not needed here
struct dll* next;
} dll; //linked list of each character : dll represents one word
typedef struct dictionary {
dll * data;
struct dictionary* next;
struct dictionary* prev;
} dictionary; //linked list of all the words
dll* entry(){
char data = getc(stdin);
if (data != '\n'){
dll* curr = create_dico(data);
curr->next=entry();
return curr;
}
return NULL;
}
void suppression(dictionary** dico) {
printf("Please enter what you wish to remove out of the list: \n");
dictionary *str = malloc(sizeof(dictionary));
str->data = entry();
str->next = NULL;
dictionary* temp = *dico;
if (str->data == NULL){
*dico = temp->next;
free(temp);
return;
}
while (temp != NULL && temp->data->data == str->data->data) {
temp = temp->next;
}
dictionary *next = temp->next->next;
free(temp->next);
temp->next = next;
}
Your deletion function doesn't reflect the data structures you are using: linked lists of linked lists!
The very first thing you need to do is detect where the word is located, you need to compare the two linked lists for that purpose:
// notice: pointer to dll, not dictionary!
dll* str = entry();
dictionary* temp = *dico;
while(temp)
{
dll* s = str; // you yet need original str for deletion!
dll* word = temp->data;
while(word && s && word->data == s->data)
{
word = word->next;
s = s->next;
}
// OK, now we need to know if we reached the ends of BOTH word and s
// -> in that case, both are equal!
if(!word && !s)
break;
}
So we iterated over the words list now. If we found the string inside, we stopped prematurely, otherwise we reached the null-element at the very end. So:
if(temp)
{
// we didn't reach end of the words' list -> we found an equal element
// at first, we'd remove the current word from the linked simply by
// re-linking predecessor and successor nodes
// the nice thing about is that you created a doubly linked list
// so we have both of them available from current node, so:
if(temp->prev)
temp->prev->next = temp->next;
else
// special case: we are deleting the head node!
*dico = temp->next;
if(temp->next)
temp->next->prev = temp->prev;
// no else needed, as we haven't a dedicated tail node
// now we need to delete the word's characters!
dll* word = temp->data;
while(word)
{
dll* next = word->next;
free(word);
word = next;
}
// now we yet need to delete the word node itself!
free(temp);
}
Fine so far, the list is adjusted. We created a temporary reference string, though, which itself needs to be freed again as well:
while(str)
// well, just the same as when deleting the word...
As you do the same thing twice, you might create a common function for...
Be aware that above is untested code, no guarantee that it is bug-free. But it should suffice to show where you have to keep an eye on... Be aware, too, that this answer is based on quite a few assumptions, mainly the lists having been created correctly before, as you didn't provide a minimal reproducible example.

Linked lists are not created, why? [CS50 pset4]

I am doing the pset4 of CS50 which basically needs you to create 26 nodes (each node for every letter of the alphabet) and created a linked list within these nodes to connect words from a dictionary.
So, for example, node 0 will store every word of the dictionary that starts with A, node 1 wills store every word of the dictionary that starts with B, etc...
So, here is the main piece of code:
// Insert words into hash table
while (fscanf(file, "%s", word) != EOF)
{
// for every word, we allocate enough memory for a node, that will carry the word
node *new_node = malloc(sizeof(node));
if(new_node == NULL) { printf("could not allocate memory.\n"); return false; }
strcpy(new_node->word, word);
new_node->next = NULL;
if(!hashtable[alphabetPosition(word[0])]){
hashtable[alphabetPosition(word[0])] = new_node;
}
else
{
for(node *ptr = hashtable[alphabetPosition(word[0])]; ptr != NULL; ptr = ptr->next){
hashtable[alphabetPosition(word[0])]->next = new_node;
}
}
}
alphabetPosition() is basically a function that will return the first character of the word.
the main problem is this:
else
{
for(node *ptr = hashtable[alphabetPosition(word[0])]; ptr != NULL; ptr = ptr->next){
hashtable[alphabetPosition(word[0])]->next = new_node;
}
}
Because every thing else is working. The nodes are been created, but the linked lists are not.
I'm pretty sure there is something wrong with this piece of code but I can't seem to understand. If someone could help me (explaining how to solve it), it would help me so much.
Thanks!
The main flaw is: hashtable[index] is only and always pointing to the last node created. I.E. hashtable[alphabetPosition(word[0])]->next is always set to new_node.
The for loop is basically wrong. The program simply needs to point the new_node to the current head of the list (ie hashtable[alphabetPosition(word[0]) and then make new_node the new head.

Deleting List With One Node In C

I have a function here that will remove a node from a sorted list of any type.
I am having difficulties with one specific case: when there is 1 node in the list and you want to delete it.
In this case, I want to make the list empty, so when the list is printed out, no data is printed to the screen, but I can't seem to get that result. Just say, for example, the list is of type double, and the list consists of just one node 2.0. If this node is the target for deletion, the proceeding output should be an empty list. Instead my code prints out 0.0.
I am not sure how to handle this error. I have found the specific part of the function where this is to be taken care of, but its implementation escapes me. I first check if the previous node is null, and then check if the list length is equal to 1.
The function returns 1 if it was successful and 0 if it failed.
int SLRemove(SortedListPtr list, void *newObj) {
Node ptr, iterptr, prev = NULL;
if(list==NULL || newObj ==NULL) {
return 0;
}
int size= listlength(list);
for(ptr=list->start; ptr!=NULL; ptr=ptr->next) {
if(list->cf(newObj, ptr->info)==0){//found matching entry in list
//deleting first node;
if(prev==NULL) {
if(size == 1) {
printf("attempting to delete list with 1 node\n");
/*code to delete node where it's the only element in the ist, should make the list empty.*/
return 1;
}
list->start = ptr->next;
destroyNode(ptr);
return 1;
} else {
prev->next = ptr->next;
destroyNode(ptr);
return 1;
}
}
prev = ptr;
}
return 0;
}
Any help you can provide would be much appreciated. Thank you.
The first check should be:
if(list==NULL || list->start == NULL || newObj ==NULL) {
return 0;
}
Once past this check, there's at least one node in the list. If prev == NULL, then you need to set list->start = list->start->next to delete the first node. It doesn't matter if there is one node or more than one node.
The other functions you have need to check for list->start == NULL (or size == 0) to avoid printing garbage.
Using a double pointer can eliminate checking for prev == NULL, but I can't explain for your code since I don't know how node is defined, and it's not really needed, since checking for prev == NULL is just as good. As an example:
typedef struct Node_{
struct Node_ *next;
...
}Node;
/* in the delete function */
Node **ppNode = &list->start; /* ptr to list->start or ...->next */
/* to advance ppNode */
ppNode = &(*ppNode->next);
/* to remove a node from the list */
*ppNode = (*ppNode)->next;

fill empty linked list from another linked list in c language

i have a full linked list with data in it, what i want is to fill another linked list with the same data but with a condition, so let say that this is the linked list :
char cl;
int time;
int lng;
C 0 1
D 0 2
B 2 1
A 2 2
i wan to copy from this list to a new empty one but only if (time>0), so the new one will be like this :
B 2 1
A 2 2
i have tried this code but it doesn't work :
void insert(const node * n )// this where i put the pointer of the full node
{
node *head=n;
node *sec; // new node
sec=(node*)malloc(sizeof(node));
do{
if(head->time>0){
strcpy(sec->cl,head->cl);
sec->time= head->time;
sec->lng= head->lng;
head=head->next;
sec=sec->next;
}
else
head=head->next;
}while(head!=NULL);
print(sec) // this will print the new node
}
Help me please. Thank you
I combined all the suggestions from the comments as well as some additional fixes.
Here's the resulting code:
const node* insert(const node * head)
{
node *sec = NULL; // start of the new, second list
node *last = NULL; // points to the last inserted node
while(head!=NULL){
if(head->time > 0){
node* newsec=(node*)malloc(sizeof(node));
newsec->cl = head->cl;
newsec->time = head->time;
newsec->lng = head->lng;
newsec->next = NULL;
//Add the new node to the list:
if(last == NULL){ //This is the first element in the new list
sec = newsec;
}else{
last-> next = newsec;
}
last = newsec;
}
head=head->next;
}
print(sec); // this will print the new node
return sec;
}
Your mistakes:
Wrong memory allocation (You only allocated memory once)
strcpy is not needed (char's don't require a string-copy)
while must be at the beginning of the loop (your code would fail if the given list is empty)
missing semicolon
wrong concatination of the new list
wrong const-correctness (Missing const in node *head=n;)
the internal head-variable is not neccessary (And the parameter-naming n is also not ideal. If you name it "start"/"head"/"begin", the comment wouldn't be neccessary)
Another suggestion: Use an upper case name for your struct, since it makes it easier to distinguish types from variables (should be Node, not node).
Note that you might want to remove the const from the return value type.
// Note: a better function name might be: printNodesWithTime
void insert(const node * pOld )
{
// note: since nothing is done with the new linked list,
// there is no need to actually create it
while( NULL != pOld )
{
if(pPld->time > 0)
{
print(pOld) // this will print the node
}
// step to next old node
pOld = pOld->next;
} // end while
} // end function: insert

Resources