Deleting elements in linked list until i find word - c

I have problem with deleting elements from list
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 30
typedef struct elem{
char name[MAX];
int statistic;
int price;
struct elem *next;
struct elem *prev;
} shop;
I created function with search in list for a word defined in main and then if it finds it it keeps deleting 1st element until it finds the word and then it deletes selected word and makes next as first:
void delete_from_list(shop *first, char word[MAX], int* check)
{
shop *tmp= first;
while (tmp!=NULL && strcmp(tmp->name, word) != 0)
{
tmp = tmp->next;
}
if (tmp != NULL && strcmp(tmp->name, word)==0)
{
printf("FOUND!");
}
else
{
*check=1;
}
if (check==0)
{
while (strcmp(first->name, tmp->name)!=0)
{
first=first->next;
free(first->prev);
first->prev=NULL;
}
first=first->next;
free(first->prev);
first->prev=NULL;
}
}
void print_list(shop *first)
{
first=first->next;
if(first->name==NULL)
printf("There is nothing!!!\n");
while(first->next!=NULL){
printf("%20s",first->name);
printf("%20d \t\t",first->statistic);
printf("%d\n",first->price);
first=first->next;
}
}
I print the list and then take word.
main()
{
int check = 0;
print_list(first);
scanf("%s", word);
delete_from_list(first, word, &check);
if (check!=1)
{
print_list(first);
}
else
{
check=0;
}
}
The problem is that linked list doesn't change so probably I should add pointers but I have no idea how.

You have to pass pointer first by reference.
Also the function has a bug. The next element after the found element can be equal to NULL. In this case these statements
first=first->next;
free(first->prev);
^^^^^^^^^^^
first->prev=NULL;
^^^^^^^^^^^
result in undefined behaviour.
The function can be written the following way
void delete_from_list( shop **first, const char *word, int *check )
{
shop *tmp = *first;
while ( tmp!=NULL && strcmp( tmp->name, word ) != 0 )
{
tmp = tmp->next;
}
if ( tmp != NULL)
{
printf("FOUND!");
}
else
{
*check = 1;
}
if ( check == 0 )
{
tmp = tmp->next;
while ( *first != tmp )
{
shop *current = *first;
*first = ( *first )->next;
free( current );
}
if ( *first ) ( *first )->prev = NULL;
}
}
Also it is a bad design that the function relies on the value of check that is set in main. It would be better if the function itself set varaible check. In this case you could split the function into tow functions. The first one would search the target element and the second one wpuld delete all elements that satisfy the criteria if it is required.
I would define the function the following way
int delete_from_list( shop **first, const char *word )
{
shop *tmp = *first;
int deleted = 0;
while ( tmp!=NULL && strcmp( tmp->name, word ) != 0 )
{
tmp = tmp->next;
}
if ( tmp != NULL)
{
deleted = 1;
tmp = tmp->next;
while ( *first != tmp )
{
shop *current = *first;
*first = ( *first )->next;
free( current );
}
if ( *first ) ( *first )->prev = NULL;
}
return deleted;
}
Also function print_list is also wrong. In general parameter first or first->next can be equal to NULL. In this case statements like these
first=first->next;
if(first->name==NULL)
while(first->next!=NULL){
result in undefined behaviour.
And the logic itself of the function is wrong.

First of all you have to adopt the parameter list of your function delete_from_list,
so that it is able to delete the first element. Use shop **first instead of
shop *first. Find the element with name word. Delete the first element of your list as long as first element is not the found element.
void delete_from_list(shop **first, char word[MAX], int* check)
// ^^ pointer to pointer of first element
{
// search for elemnt with name word
shop *found = *first;
while ( found != NULL && strcmp(found->name, word) != 0 )
found = found ->next;
if ( found != NULL )
{
// found is element with name word
printf("FOUND!");
// Delete the first element of your list as long as first elment is not found element
shop *temp = *first;
while ( temp != found )
{
shop *next = temp->next;
free( temp );
temp = next;
}
*first = found; // write back the new first element of your list
(*first)->prev = NULL; // predecessor of first element is NULL
}
else
*check=1;
}

Related

Linked List Delete Node , Simple linked list

I'm trying to implement a function that deletes nodes from a linked list. So far, I can delete just the first node of the list(3).
I tried to go to the for loop from delete, I thought that the memory is not well allocated, I have been struggling for a few days and I don't understand, please help me a little, it's the topic I received from college.
#include <stdio.h>
#include <stdlib.h>
typedef struct nod
{
int key;
struct nod *urm;
} NOD;
NOD *first=0,*last=0;
void add(int x)
{
NOD *p=(NOD*)malloc(sizeof(NOD));
p->key=x;
p->urm=0;
if(0==first)
{
first=p;
last=p;
}
else{
last->urm=p;
last=p;
}
}
void delete(int x)
{
NOD *q,*p;
if(first->key==x)
{
p=first;
first=first->urm;
free(p);
}
else{
for(p=q=first;p=0;q=p,p=p->urm)
{
if(p->key==x)
{
q->urm=p->urm;
if(p==last)
{
last=q;
}
free(p);
}
}
}
}
void show()
{
for(NOD *p=first;p!=0;p=p->urm)
{
printf("%d ",p->key);
}
printf("\n");
}
int main()
{
add(3);
add(1);
add(2);
add(5);
show();
delete(2);
show();
return 0;
}
For starters the code you showed is not a C++ code. It is a C code.
It is a bad idea to define global variables like first and last and when functions depend on global variables. In this case you can not create more than one list in a program.
As for the function delete then in general it has undefined behavior. It can be called for an empty list.
Moreover in this ;loop
for(p=q=first;p=0;q=p,p=p->urm)
there is a typo in the condition expression. You are using the assignment operator instead of the comparison operator.
And you function ignore the case when the list contains only one node because in this case it does not update the last node.
Nevertheless using your approach the function delete can look the following way.
void delete(int x)
{
if ( first )
{
if ( first->key == x )
{
NOD *tmp = first;
first = first->urm;
free( tmp );
if ( first == NULL ) last = NULL;
}
else
{
NOD *p = first;
while ( p->urm != NULL && p->urm->key != x )
{
p = p->urm;
}
if ( p->urm != NULL )
{
NOD *tmp = p->urm;
p->urm = p->urm->urm;
free( tmp );
if ( p->urm == NULL ) last = p;
}
}
}
}
Here is a demonstrative program.
#include <stdio.h>
#include <stdlib.h>
typedef struct nod
{
int key;
struct nod *urm;
} NOD;
NOD *first=0,*last=0;
void add(int x)
{
NOD *p=(NOD*)malloc(sizeof(NOD));
p->key=x;
p->urm=0;
if(0==first)
{
first=p;
last=p;
}
else{
last->urm=p;
last=p;
}
}
void delete(int x)
{
if ( first )
{
if ( first->key == x )
{
NOD *tmp = first;
first = first->urm;
free( tmp );
if ( first == NULL ) last = NULL;
}
else
{
NOD *p = first;
while ( p->urm != NULL && p->urm->key != x )
{
p = p->urm;
}
if ( p->urm != NULL )
{
NOD *tmp = p->urm;
p->urm = p->urm->urm;
free( tmp );
if ( p->urm == NULL ) last = p;
}
}
}
}
void show()
{
for(NOD *p=first;p!=0;p=p->urm)
{
printf("%d ",p->key);
}
printf("\n");
}
int main()
{
add(10);
add(20);
add(30);
add(40);
show();
delete(30);
show();
add( 50 );
add( 60 );
add( 70 );
add( 80 );
show();
delete(80);
show();
return 0;
}
Its output is
10 20 30 40
10 20 40
10 20 40 50 60 70 80
10 20 40 50 60 70
You can greatly simplify deleting a node from your list by using both a pointer-to-pointer to NOD and a pointer-to NOD to iterate over the list. This allows you to set the node at the current address to the node->urm eliminating the need to keep track of the previous node in the list, e.g.
/** delete node with key v from list (for loop) */
void delete (int v)
{
NOD **ppn = &first; /* pointer to pointer to first*/
NOD *pn = first; /* pointer to first */
for (; pn; ppn = &pn->urm, pn = pn->urm) {
if (pn->key == v) {
*ppn = pn->urm; /* set node at address to next node */
free (pn); /* free deleted node */
break;
}
}
}
See Linus on Understanding Pointers for further discussion on the advantages of using the address of a node in addition to a pointer to node.
I think your for loop condition is incorrect inside delete functions:
for(q=first, p=first->urm; p!=0; q=p, p=p->urm)
just change the condition, then it should work.

Multi-Occurence delete function in the linked list only deletes first instance of character

I am attempting to delete several occurrences of a word stored in a linked list. However, these words are stored by character in a single node instead of as a whole word in a list. For example, The pink flamingo is stored as:
T->h->e-> ->p->i->n->k-> ->f->l->a->m->i->n->g->o. Say the user wants to find pink. They have to loop through and delete each of these nodes.
I have attempted to create a loop which copies the contents of the linked list into a search function. I think copy this search function into another string. I am able to successfully delete the first occurrence of the desired character. However, I'm unable to do much else. I've attempted several times to push the node to the next node after deletion, but that also hasn't worked. It produced the same error.
void nodeDeletions(struct node** reference_to_headNode, struct node* deleteNode){
if(*reference_to_headNode == NULL){
return;
}
if(*reference_to_headNode == deleteNode){
printf("Test A\n");
*reference_to_headNode = deleteNode->nextNode;
}
if(deleteNode->nextNode != NULL){
printf("Test B\n");
deleteNode->nextNode->previousNode = deleteNode->previousNode;
}
if(deleteNode->previousNode != NULL){
printf("Test C\n");
deleteNode->previousNode->nextNode = deleteNode ->nextNode;
}
free(deleteNode);
}
void deleteWord(struct node** reference_to_headNode, char word_to_delete[]){
struct node *tempNode;
struct node *nextNode;
int searchIndex = 0;
int characterIndex = 0;
const int arraySize = 101;
const int arraySize2 = 202;
char searchWordIndex[arraySize];
char searchWordCopyIndex[arraySize2];
if(*reference_to_headNode == NULL){
return;
}
else {
for (tempNode = *reference_to_headNode; tempNode != NULL; tempNode = tempNode->nextNode) {
searchWordIndex[searchIndex] = tempNode->character;
searchIndex++;
}
strcpy_s(searchWordCopyIndex, searchWordIndex);
int length_of_searchIndex = strlen(searchWordCopyIndex);
int length_of_deletionWord = strlen(word_to_delete);
tempNode = *reference_to_headNode;
for (searchIndex = 0; searchIndex < length_of_searchIndex; searchIndex++) {
printf("Test 1\n");
if(tempNode != NULL) {
if(tempNode->character == word_to_delete[0]) {
for (characterIndex = 0; characterIndex < length_of_deletionWord; characterIndex++) {
printf("Test 2\n");
if (searchWordCopyIndex[searchIndex] == word_to_delete[characterIndex]) {
printf("Test 3\n");
if (tempNode->character == word_to_delete[characterIndex]) {
printf("Test 4\n");
printf("%c\n", tempNode->character);
printf("%c\n%c\n", word_to_delete[characterIndex], searchWordCopyIndex[searchIndex]);
nextNode = tempNode->nextNode;
nodeDeletions(reference_to_headNode, tempNode);
tempNode = nextNode;
}
else {
printf("Test 5\n");
tempNode = tempNode->nextNode;
}
}
}
}
}
tempNode = tempNode->nextNode;
}
}
}
Phew! That's a lot of indented clode blocks. And a lot of auxiliary arrays and indices. And very long variable names. :)
Basically, you are dealing with three different types of iterating forwards through stuff, which are all present in your code:
Traverse a character string:
while (*s) {
// do stuff with *s
s++;
}
Traverse a linked list:
while (p) {
// do stuff with *p
p = p->next;
}
Traverse a linked list via a reference to the source, so that you can modify it:
while (*p) {
// do stuff with **p
p = &(*p)->next;;
}
You only have to combine these three basic loops.
You can walk through the list with the third method (because you need to be able to update the head or next links when you delete). For each node you visit, compare the "tail" of that node with an auxiliary pointer p and the string s using the other two methods simultaneously. When the string matches, *s == '\0' and p points to the first node after the word. Delete all nodes by advancing the head until the head is p.
In other words:
Traverse the list via *head.
At each node:
set p = *head and s to the begining of the string;
traverse the list and the word while the letters match;
If *s == '\0', there is a match. Now, *head points to the start of the word to delete in the list, p points to the first node after the word in the list, which may be NULL.
If there is a match, advance *head until *head == p, deleting the nodes as you go.
Or, in code:
void delete_word(struct node **head, const char *str)
{
while (*head) {
struct node *p = *head;
const char *s = str;
while (p && *s && p->c == *s) {
p = p->next;
s++;
}
if (*s == '\0') {
while (*head != p) {
struct node *del = *head;
*head = (*head)->next;
delete_node(del);
}
} else {
head = &(*head)->next;
}
}
}

Segmentation fault when deleting linked list

I am trying to delete all the nodes on a linked list but I am getting a segmentation fault.
I had code that was working initially but I was only deleting the first node in the list, I want to delete all the nodes and delete all the pointers redundant pointers.
Also if some of you guys could check the function I am using to create the linked list and give me some feedback on whether you think it is ok or where some improvements could be made, I would appreciate it.
Thanks.
Here is the code:
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#define MEMORY_SIZE (15)
typedef struct link {
double coeff;
int pow;
struct link * next;
} poly;
poly *polyArray[MEMORY_SIZE];// array of 15 polynomials to play with
// /** The function prototypes */
void createPoly(poly **);
void deletePoly(poly *);
/**
* The main function
*/
int main(void) {
printf("\n\n\t***************************************************");
/* printf("\n\tDemonstrating Polynomial Creation");
printf("\n\t***************************************************");*/
printf("\n\t1st polynomial\t");
createPoly(&polyArray[0]);
showPoly(polyArray[0]);
srand(time(NULL));
// printf("\n\n\tCreating and storing the 2nd polynomial\n");
// createPoly(&polyArray[1]);
// showPoly(polyArray[1]);
showPoly(polyArray[0]);
printf("\n\t***************************************************");
printf("\n\tProgram has Ended, Deleting all polynomials");
printf("\n\t***************************************************");
int count;
for(count = 0; count < MEMORY_SIZE; count++)
{
deletePoly(polyArray[count]);
}
printf("\n\n");
showPoly(polyArray[0]);
return 0;
}//end main function
//////////////////////////////////////////////////////////////////////////////////////
void createPoly(poly **node) {
poly *tempnode; //To hold the temporary last address
tempnode = (poly*)malloc( sizeof(poly) ); //create the first node
*node = tempnode; //Store the head address to the reference variable
int flag = 1 + rand()%3;; // A flag to control the number of terms
int counter;
for( counter = 0; counter <= flag; counter++ )
{
tempnode->pow = ( flag-counter );
tempnode->coeff = ( (double)(rand()%20) )/( (double)(1 + rand()%20) );
if( (counter < flag) && (counter >= 0) )
{
tempnode->next = (poly*)malloc( sizeof(poly) ); //Grow the list
}
else if ( counter == flag )
{
tempnode->next = NULL;
}
tempnode = tempnode->next;
}
}
void deletePoly(poly *node) {
poly *temp;
if( node->next == NULL )
{
free( node );
node = NULL;
}
else
{
while( node->next != NULL )
{
temp = node->next;
free( node );
node = temp;
}//end while
node = NULL;
}//end 'if/else'
}//end function 'deletePoly'
As I understand it, the main function is creating only the first polynomial (poly[0]), but you are trying to delete them all (the loop in the main function goes up to MEMORY_SIZE).
You should also initialize all your pointers to NULL before beginning the program (this is an important feature in C programs) and change the deletePoly this way:
void deletePoly(poly *node) {
poly *temp;
while( node != NULL ) {
temp = node->next;
free( node );
node = temp;
}//end while
node = NULL;
}//end function 'deletePoly'

Deleting first node in linked list has problems

I'm implementing a linked list and it needs to have a function that when given a head of a linked list and a cstring, it finds and deletes a node whose value is the cstring.
typedef struct node
{
char entry[21];
struct node* next;
} node;
/*returns true if node with phrase value found, otherwise false*/
bool findAndRemove(node* root, char phrase[21])
{
if(root != NULL)
{
node* previous = NULL;
while(root->next != NULL)
{
if(strcmp(root->entry, phrase) == 0)//found
{
if(previous == NULL)//node to delete is at head
{
node* tmp = root;
root = root->next;
free(tmp);
return true;
}
previous->next = root->next;
free(root);
return true;
}
previous = root;
root = root->next;
}
return false;
}
}
It works alright but when deleting the head some garbage gets printed out. What is happening and how can I fix this? Do I have any memory leaks? Out of curiosity is the term "root" or "head" more commonly used for the first node in a linked list?
The first thing to realise is that removing an element from a linked list involves changing exactly one pointer value: the pointer that points at us. This can be the external head pointer that points to the first list element, or one of the ->next pointers inside the list. In both cases that pointer needs to be changed; its new value should become the value of the ->next pointer of the node to be deleted.
In order to change some object (from within a function) we need a pointer to it. We need to change a pointer, so we will need a pointer to pointer.
bool findAndRemove1(node **ptp, char *phrase)
{
node *del;
for( ;*ptp; ptp = &(*ptp)->next) {
if( !strcmp((*ptp)->entry, phrase) ) { break; } //found
}
/* when we get here, ptp either
** 1) points to the pointer that points at the node we want to delete
** 2) or it points to the NULL pointer at the end of the list
** (in the case nothing was found)
*/
if ( !*ptp) return false; // not found
del = *ptp;
*ptp = (*ptp)->next;
free(del);
return true;
}
The number of if conditions can even be reduced to one by doing the dirty work in the loop,and returning from the loop but that would be a bit of a hack:
bool findAndRemove2(node **ptp, char *phrase)
{
for( ;*ptp; ptp = &(*ptp)->next) {
node *del;
if( strcmp((*ptp)->entry, phrase) ) continue; // not the one we want
/* when we get here, ptp MUST
** 1) point to the pointer that points at the node we want to delete
*/
del = *ptp;
*ptp = (*ptp)->next;
free(del);
return true;
}
return false; // not found
}
But what if the list is not unique, and we want to delete all the nodes that satisfy the condition? We just alter the loop logic a bit and add a counter:
unsigned searchAndDestroy(node **ptp, char *phrase)
{
unsigned cnt;
for( cnt=0 ;*ptp; ) {
node *del;
if( strcmp((*ptp)->entry, phrase) ) { // not the one we want
ptp = &(*ptp)->next;
continue;
}
/* when we get here, ptp MUST point to the pointer that points at the node we wish to delete
*/
del = *ptp;
*ptp = (*ptp)->next;
free(del);
cnt++;
}
return cnt; // the number of deleted nodes
}
Update: and a driver program to test it:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
typedef struct list {
struct list *next;
char entry[20];
} node;
void node_add( node **ptp, char *str)
{
node *new;
for ( ; *ptp; ptp = &(*ptp)->next) {
if (strcmp ((*ptp)->entry, str) < 0) continue;
}
new = malloc (sizeof *new);
strcpy(new->entry, str);
new->next = *ptp;
*ptp = new;
}
int main (void)
{
node *root = NULL;
unsigned cnt;
node_add (& root, "aaa" );
node_add (& root, "aaa" );
node_add (& root, "bbb" );
node_add (& root, "ccc" );
node_add (& root, "aaa" );
cnt = seachAndDestroy( &root, "bbb" );
printf("Cnt(bbb) := %u\n", cnt );
cnt = seachAndDestroy( &root, "ccc" );
printf("Cnt(ccc) := %u\n", cnt );
cnt = seachAndDestroy( &root, "aaa" );
printf("Cnt(aaa) := %u\n", cnt );
printf("Root now = %p\n", (void*) root );
return 0;
}
And the output:
plasser#pisbak:~/usenet$ ./a.out
Cnt(bbb) := 1
Cnt(ccc) := 1
Cnt(aaa) := 3
Root now = (nil)
You are changing the root inside the function, thus you need to pass a double pointer:
bool findAndRemove(node** root, char phrase[21])
{
node* iterate = *root;
if(root != NULL && *root != NULL)
{
node* previous = NULL;
while(iterate->next != NULL)
{
if(strcmp(iterate->entry, phrase) == 0)//found
{
if(previous == NULL)//node to delete is at head
{
node* tmp = iterate;
*root = iterate->next;
free(tmp);
return true;
}
previous->next = iterate->next;
free(iterate);
return true;
}
previous = iterate;
iterate = iterate->next;
}
return false;
}
}
You construct a list by pointing to the first node.
Then you delete the first node, but do not update the pointer to the list to point to the second one
Just make your function check if you are deleting the first node, and always return a pointer to the first pointer of the final list. Alternatively, instead of node *root parameter, pass node **root so you can modifiy the reference in your function (although I don't like this way of working).

Deleting items from a structure

I have the following linked list:
struct scoreentry_node {
struct scoreentry_node *next;
int score;
char name[1];
}
;
typedef struct scoreentry_node *score_entry;
I'm trying to write a function that removes all nodes that contain a certain name. Here is what I have so far but I'm not sure I'm right:
score_entry disqualify(score_entry a, char* name)
{
score_entry tmp = a;
while (tmp != NULL){
if (strcmp(tmp->name, name) == 0)
{
score_entry trash = tmp;
tmp = tmp->next;
free(trash);
}
else { tmp = tmp->next; }
}
return a;
}
It gives me heap error's .. Any suggestions?
score_entry disqualify(score_entry a, char* name)
{
score_entry new_front = a, tmp;
// delete "wrong" entries from the front
while (new_front != NULL){
if (strcmp(new_front->name, name) == 0)
{
score_entry trash = new_front;
new_front = new_front->next;
free(trash);
}
else
{
// first list entry is valid
// delete "wrong" entries from inside the list
tmp = new_front;
while ( tmp->next != NULL )
{
if ( strcmp(tmp->next->name,name)==0 )
{
score_entry trash = tmp->next;
tmp->next = tmp->next->next;
free(trash);
} else
{
tmp = tmp->next;
}
}
}
}
return new_front;
}
You should also obtain some book related to common data structures - you seem to be interested in the stuff, and it could be a great help for you.
If you delete a member from the list you must fix the gap this creates by linking the 'next' pointer of the previous entry to the following entry. The code below does that. Note that I have changed score_entry so that the typedef no longer contains the pointer - I prefer not to disguise types. Notice that the function returns the head which might have changed if we deleted the first entry in the list.
typedef struct scoreentry_node score_entry;
static score_entry *
disqualify(score_entry *head, const char *name)
{
score_entry *se = head;
score_entry *prev = head;
while (se) {
if (!strcmp(se->name, name)) {
score_entry *next = se->next;
if (head == se) {
head = next;
} else {
prev->next = next;
}
free(se);
se = next;
} else {
prev = se;
se = se->next;
}
}
return head;
}
You're using strcmp on a non-null-terminated string (tmp->name). I'm assuming it's not null-terminated as it's only of length 1. Seems like you're really comparing a character, not a string, so a simple character equality operator would be the right thing to do.

Resources