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

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.

Related

Why is this function not deleting nodes?

I'm a beginner programmer just starting to get my hands dirty with linked lists.
I'm currently trying to figure out a function that deletes a song (node) from a "playlist" (linked list.) Each node has 3 data points, 2 strings (artist and title) and 1 integer (release year.) Can anybody help me figure out what I'm doing wrong and how I can fix it?
Function:
struct Node *borrow_song(struct Node *pointer) {
struct Node *temp = pointer;
char response[40];
struct Node *remove;
printf("Which song do you want to borrow? (title): ");
scanf(" %s", response);
while(temp != NULL) {
remove = temp;
if (strcmp(response, temp->title) == 0) {
printf("\nSuccess! %s is in the list. Borrowing..\n", response);
free(remove); // I have a feeling this isn't how you properly free a node.
remove = NULL;
return 0;
}
else
temp = temp->next;
}
printf("%s was not in the list... Try again.", response);
return 0;
}
Driver:
switch....
case 4:
borrow_song(head);
printf("\nNew list:\n\n");
print_list(head);
Node creation function from a generous person on here (creates node from .txt file)
struct Node *read_node(FILE *inputp) {
struct Node *temp = malloc(sizeof(*temp));
if (temp != NULL && fscanf(inputp, "%39s%39s%d", &temp->name, &temp->title, &temp->year) == 3) {
temp->next = NULL;
temp->prev = NULL;
return temp;
}
else {
free(temp);
return NULL;
}
}
And lastly, the driver for that:
while ((node = read_node(inputp)) != NULL) {
if (!head) {
head = tail = node;
}
else {
node->prev = tail;
tail = tail->next = node;
}
}
This is the input file:
Rachmaninov Concerto_No_2 1999
Mozart Symphony_No_41 2000
Vivaldi The_Seasons 2003
Beethoven Symphony_No_5 1994
Bach Toccatas 2005
This is the console output:
Which song do you want to borrow? (title): Toccatas
Success! Toccatas is in the list. Borrowing..
New list:
Rachmaninov, Concerto_No_2, 1999
Mozart, Symphony_No_41, 2000
Vivaldi, The_Seasons, 2003
Beethoven, Symphony_No_5, 1994
`ĘŁt, Toccatas, 2005
Still working on pointers, I guess we all start somewhere :P
Thank you for any help!
You need to unlink the node by setting the previous node's next pointer to point to the node after the one being removed. If the node to remove is the first node in the list, there is no previous node, so you need to allow for the head of the list to change, probably by always returning the pointer to the head element (which appears to be what was intended if you look at the return type!).
The most uniform way to do this is to keep a Node **next_ptr initially set to &pointer. In the loop, set Node *temp = *next_ptr and see if you want to remove the temp node. If so, set *next_ptr = temp->next, free temp and return pointer. If not, set next_ptr = &temp->next and go around the loop again. If temp == NULL, then you didn't find the node, and should return pointer. In this way, if the node to remove is the first one, you will have updated pointer, otherwise you will have updated the previous node's next. Either way, you always return pointer, which will always be the head element.

Why does my C Program crash when I try to delete a node out of a singe linked list

I am currently creating a Program in C which is basically a linked list inside of a linked list. The inner list being character and the outer list being words. Unfortunately I'm having Problems with deleting some of the outer Nodes (words) and freeing their memory. My program keeps crashing and I have no idea why. The compiler doesnt give me any warnings or errors and I've been looking for a fix for hours. Any help is apreciated for anyone who could look over the code! Thanks!
*void deleteWord (Node* Node, int index){
int counter = 0;
if (Node == NULL)
return;
while (Node->next != NULL && counter != (index - 1)){
Node = Node->next;
counter++;
}
struct node* wordTemp = Node->next;
//Node->next = Node->next->next;
while (wordTemp->word != NULL){
InnerNode* letterTemp = wordTemp->word->next;
free(wordTemp->word);
wordTemp->word = letterTemp;
}
free(wordTemp);
return;
}
Seems like you are freeing Node->next (stored in wordTemp), without re-assigning it, essentially breaking the link in the linked list, so now Node->next points to a deleted memory.

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.

Link list program crash on malloc

I'm fairly new to C and coding in general so please bear with me. I've been trying to implement a linked list recently and this is the code i came up with
typedef struct something{
int data;
struct something *next;
} thing ;
int main ()
{
thing *head, *current;
head=malloc(sizeof(thing));
puts("head=malloc(sizeof(thing));");
if (head != NULL)
puts("malloc success");
head=NULL;
current=head;
puts("current=head;");
if (current == NULL)
puts("current is NULL");
puts("while");
while (current!=NULL)
{
current = current->next;
}
puts("end while");
current->next=malloc(sizeof(thing));
puts("current->next=malloc(sizeof(thing));");
//free at end of program
}
While the compiler shows 0 errors, when i run the program it only runs until the final malloc part before crashing. It doesnt run the final puts so i will assume it's something to do with the way i'm trying to use malloc.
I'll gladly appreaciate for someone to tell me what im doing wrong.
The problem is that your while loop goes to far. You want to stop when current points to the last element of the list, so you can add to it. But you're going one step further, and stopping when current == NULL. It's then too late to assign to current->next.
First, you need to initialize head->next to NULL.
head = malloc(sizeof(thing));
head->next = NULL;
Get rid of the line:
head = NULL;
as this is overwriting the result of malloc().
Then your while loop needs to test current->next, not current itself:
while (current->next != NULL) {
current = current->next;
}
And when you add the new node, you have to set its next pointer to NULL as well:
current->next = malloc(sizeof(thing));
current->next->next = NULL;
These should fix your problem.
You allocate head and then immediately after few checks point its pointer to NULL
// Allocation here
head=malloc(sizeof(thing));
puts("head=malloc(sizeof(thing));");
// Not a null
if (head != NULL)
puts("malloc success");
// Point to NULL again ???
head=NULL;
Then your current points to head viz NULL again that makes current NULL
current=head;
puts("current=head;");
if (current == NULL)
puts("current is NULL");
and then you dereference current and try to malloc
puts("while");
while (current!=NULL)
{
current = current->next;
}
puts("end while");
current->next=malloc(sizeof(thing)); //current is NULL here NULL->next is invalid
puts("current->next=malloc(sizeof(thing));");

doubly linked list insert at middle

Can anyone identify what is happening in my code that is causing the segmentation fault? Please modify/correct the wrong part.
void InsertAtMid (Node *head){
int num,count=0,i;
Node *ptr=head;
Node *newnode=NULL;
Node *newnode2=head;
printf("Enter node to be inserted: ");
scanf("%d", &num);
if (head==NULL){
newnode = head;
newnode=(Node *)malloc(sizeof(Node));
newnode->x=num;
newnode->next=NULL;
newnode->prev=NULL;
} else {
ptr=head->next;
while(ptr->x!=(count/2)){
ptr=ptr->next;
}
newnode->next=ptr->next;
newnode->prev=ptr;
ptr->next->prev=newnode;
ptr->next=newnode;
}
}
So, based on my understanding of your code - the following should [mostly] work:
void InsertAtMid (Node **head){
int num = 0;
int count = 0
int advance = 0;
Node *ptr = *head;
Node *newnode = NULL;
printf("Enter node to be inserted: ");
scanf("%d", &num);
if (*head == NULL) {
*head = (Node *)malloc(sizeof(Node));
ptr = *head;
ptr->x = num;
ptr->next = NULL;
ptr->prev = NULL;
} else {
// *** Count the number of items
ptr = *head;
while (ptr != NULL) {
ptr = ptr->next;
count++;
}
// *** Move to the middle of the list
ptr = *head;
while (advance < (count/2)){
ptr = ptr->next;
advance++;
}
// *** Insert the new value
newnode = (Node *)malloc(sizeof(Node));
newnode->x = num;
newnode->next = ptr->next;
newnode->prev = ptr;
ptr->next->prev = newnode;
ptr->next = newnode;
}
}
The following are the issues I fixed:
You are assigning to head at one point, but since "head" isn't passed in as a reference, the value isn't going to be maintained beyond the first time the function is called. Needless to say you need a pointer to a pointer of type node.
You never calculated the number of items in the list. Often "head" pointer would store this information and you would increment when you add a node, but since you don't have that the only way to determine it is to traverse the list till you find the count.
You never allocated space for the new node to insert except if you were initializing the head pointer. This was also an issue.
Hope that helps some. Best of luck!
int num,count=0,i;
...
ptr=head->next;
while(ptr->x!=(count/2)){
ptr=ptr->next;
count is initialized to 0 and never changed.
So unless you enter "0" for x, that while loop is just going to walk off the end of the list, every time.
Test to work out under what circumstances your code segfaults.
You'll find that it works OK when head == NULL, but fails if head is not null.
So you know that your error is somewhere in the else block.
Step through your running code in a debugger (if you don't know how, it's never too early to learn: whenever you solve a problem with a debugger, you think "why didn't I turn to this sooner?").
Work out what you expect to happen, watch the variables in the debugger, and when the actual values deviate from your expectations, reason about why.
It's not clear to me what you expect to happen in your code, but what will actually happen for a list with one node, is that:
it will execute ptr=head->next; -- so ptr is now NULL.
then for the while condition it will try to dereference ptr->x, and since ptr is NULL, it will segfault.
A quick fix for that would be:
while(ptr != NULL && ptr->x ....) {
But you need to think about whether that's the actual logic you want; and once you get past that, you'll hit other problems (for example, count never changes), which can be sorted out with a debugger in the same way.

Resources