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.
Related
I'm trying to figure out how to manipulate linked lists. I want to remove the end node of a given linked list and add this to the end of another given linked list.
For some reason I can not get my pointers right, well- at least that is where I think the problem lays.
(This method sits in a while loop, so one list keeps getting smaller while the other one grows.)
void movenode(struct Node **cards,struct Node **column)
{
struct Node *head = NULL;
struct Node *tmp,*head1 = *cards;
struct Node *tmp2,*head2 = *column;
if (*cards == NULL || (*cards)->next == NULL){
return;
}
while (tmp->next != NULL) {
head->next = tmp;
tmp = tmp->next;
}
while (tmp2->next != NULL) {
tmp2 = tmp2->next;
}
head->next = NULL;
tmp2->data = tmp;
tmp2->next = NULL;
*cards = head1;
*column = head2;
}
Hope someone is able to help me better understand this.
For some reason I can not get my pointers right, well.. atleast that is where I think the problem lays.
You're right, but it's a little more than that. For example, after struct Node *head = NULL; nothing modifies the value in head, so every time you do head->next you're accessing memory at "NULL + a small offset" (and will probably crash).
To remove the last entry from a singly linked list, you have to find the entry before the last entry (so that you can modify its next); and to add an entry to a linked list you have to find the last entry (so that you can modify its next). With this in mind we can break it into 5 parts:
Do sanity checks
Find the entry before the last entry in the *cards list
Remove the last entry in the *cards list
Find the last entry in the *column list
Add the (removed) entry to the end of the *columns list
You can implement each of these 5 pieces one at a time (and test them). This is an important part of programming - breaking more complex stuff into simpler easier things.
The resulting code might look something like (untested):
void movenode(struct Node **cards,struct Node **column) {
struct Node *temp = *cards;
struct Node *removed;
// Do sanity checks
if (temp == NULL) {
return;
}
// Find the entry before the last entry in the `*cards` list
if(temp->next != NULL) {
while(temp->next->next != NULL) {
temp = temp->next;
}
}
// Remove the last entry in the `*cards` list
if(temp == NULL) {
// The last entry was the first entry
removed = temp;
*cards = NULL;
} else {
removed = temp->next;
temp->next = NULL;
}
// Find the last entry in the `*column` list
temp = *column;
if(temp != NULL) {
while(temp->next != NULL) {
temp = temp->next;
}
}
// Add the (removed) entry to the end of the `*columns` list
if(temp == NULL) {
// There was no last entry (list was empty)
*column = removed;
} else {
temp->next = removed;
}
}
I'm not entirely sure about the mechanics of your solution so I'll offer a separate implementation and solution.
typedef struct Node {
void *data;
struct Node *next;
}
void poppush(Node *popHead, Node *pushHead) {
Node *pushLastNode = pushHead;
while (pushLastNode->next != NULL) {
pushLastNode = pushLastNode->next;
}
Node *popLastNode = popHead;
while (popLastNode->next != NULL) {
popLastNode = popLastNode->next;
}
Node *popSecondLastNode = popHead;
while (popSecondLastNode->next != popLastNode) {
popSecondLastNode = popSecondLastNode->next;
}
popSecondLastNode->next = NULL;
pushLastNode->next = popLastNode;
}
However, for operations such as these, I would recommend using a doubly-linked list and/or create some functions dedicated to managing the lists.
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.
I'm having trouble with deleting the first node in my linked list, when i print the results after deleting other nodes its a success, yet deleting the first node, its prints a 0 and the last two members of the struct.
The function is supposed to be passed a pointer to a linked-list, prompt the user for an ID number to find a delete a node, and return the list.
struct dog *delete_from_list(struct dog *dogs){
int num;
printf("Enter a dogs ID number to be deleted ");
scanf("%d", &num);
struct dog *prev, *cur;
for(cur = dogs, prev = NULL;
cur !=NULL && cur->number != num;
prev = cur, cur = cur->next);
if (cur == NULL){
printf("Dog not found");
return dogs;
}
if( prev == NULL){
dogs = dogs->next;
printf("Dog deleted");
}
else{
prev->next = cur->next;
}
free(cur);
return dogs;
}
This is the function to print the linked list afterwards
void print(struct dog *list){
/* Prints all structs within the
* linked list
*/
printf("\nID Number\t Dog Name\t Breed\t\t Owner\n");
for( ; list != NULL; list = list->next){
printf("%d\t\t %-10s\t %-10s\t %-12s\n", list->number, list->dog_name, list->breed, list->owner_last_name);
}
}
Your function apparently works fine works fine (modified it to accept num as a parameter for me...), concerning its actual intention.
What you do not get is an output "dog deleted" if you do not delete the head - this is because you did not implement to do so. Try this instead:
if (!cur)
{
puts("Dog not found");
return dogs;
}
if(!prev)
{
dogs = dogs->next;
puts("head deleted"); // <- changed "dog" -> "head"
}
else
{
prev->next = cur->next;
puts("dog deleted"); // <- added by me!
}
Important is: You absolutely need to call it (as BLUEPIXIY denoted in his comment) like this:
dogs = delete_from_list(dogs);
If you do not, your outer variable 'dogs' won't change and will point to memory already deleted. If you still use the then dangling pointer, you get undefined behaviour, most likely an access violation (segmentation fault).
To avoid such a problem, you might like to pass a pointer to pointer to your function. The return value gets free then, and you could use it to indicate if the dog actually could have been removed:
bool // need to #include <stdbool.h> for
delete_from_list(struct dog** dogs)
// ^
You'd now use *dogs instead of dogs internally and would call it like this:
delete_from_list(&dogs);
Advantage: user does not have to care about correct re-assignment...
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.
I have a linked list that contains two "strings", one for searching and one for replacing. I also have a text file that I'm supposed to open and read line by line then see if the words exist in the "dictionary" (the linked list) if it does, I have to replace it with the word's definition. Then write the changed text into a new text file, so I thought I should use a buffer when reading.
The problem is, I don't know how to iterate over the linked list. So far I have two words in it but it only searches for the first one in the loop:
char *textLine = NULL;
size_t textlen = 0;
ssize_t readText;
struct node *n = malloc(sizeof(*n));
n = head;
char buffer[MAX_L];
while ((readText = getline(&textLine, &textlen, t)) != -1) {
char *t = strtok(textLine, " ");
while (t != NULL) {
if (strcmp(t,n->word) == 0) {
// do something
} else {
// do something
}
n = head;
t = strtok(NULL, " ");
}
}
head is NULL, I guess that's why it only searches for the first word I just don't really know how should I iterate over both the lines and the linked list.
How, specifically, to iterate over a linked list depends, to some extent, on its interface.
Not having the interface to the particular implementation you are using available, the question is difficult to answer; but typically; a linked list looks something like this:
typedef struct list_node ListNode;
struct list_node {
void *payload;
ListNode *next;
}
Iterating is usually (always?) done by following the next pointer, so long as it is not NULL; like so:
void iterate (ListNode *head) {
while (head) {
if (interested_in_payload(head->payload)) {
// do stuff
}
head = head->next;
}
}
This:
struct node *n = malloc(sizeof(*n));
n = head;
looks very scary. It's almost never correct to first allocate some memory, then immediately overwrite the pointer.
Perhaps you meant
head = n;
?