patterncMinor
Simplifying linked list "search and delete" function
Viewed 0 times
searchdeletefunctionsimplifyingandlistlinked
Problem
The function asks the user for a value to search for, then deletes all matching values in the list.
The program runs flawlessly, but I can't figure out a simple/elegant way of separating cases for 'first node', 'last node', and 'intermediate node'.
Please help me make it more elegant and readable.
I'll post the relevant code block, followed by the whole program.
In the relevant function, the following code block is part of the switch-case construct I'm using (if user inputs 'v', it means search for a 'value' and delete, as opposed to 'delete the first node' or 'delete the last node').
```
case 'v':
printf("\n\tEnter value to delete:\t");
scanf("%d",&value);
r=q;
if(!q)//If linked list is empty
{
printf("\n\t\tLinked list is EMPTY\n");
return;
}
else if(!(q->next))//If linked list consists of only one node
{
if((q->data)==value)
{
free(q);
printf("\nValue found and deleted in position 1\n");
count=1;
}
}
else//If linked list consists of more than one node
{
for(i=1;q;i++)
{
if(q->data==value)
{
if(i==1)//for first node
{
q=q->next;
free(r);
*pp=q;
printf("\nValue found and deleted in position 1\n");
count=1;
}
else//for the rest of the nodes
{
printf("\nValue found and deleted in position %d\n",i);
r->next=q->next;
temp=q;
q=q->next;
The program runs flawlessly, but I can't figure out a simple/elegant way of separating cases for 'first node', 'last node', and 'intermediate node'.
Please help me make it more elegant and readable.
I'll post the relevant code block, followed by the whole program.
In the relevant function, the following code block is part of the switch-case construct I'm using (if user inputs 'v', it means search for a 'value' and delete, as opposed to 'delete the first node' or 'delete the last node').
```
case 'v':
printf("\n\tEnter value to delete:\t");
scanf("%d",&value);
r=q;
if(!q)//If linked list is empty
{
printf("\n\t\tLinked list is EMPTY\n");
return;
}
else if(!(q->next))//If linked list consists of only one node
{
if((q->data)==value)
{
free(q);
printf("\nValue found and deleted in position 1\n");
count=1;
}
}
else//If linked list consists of more than one node
{
for(i=1;q;i++)
{
if(q->data==value)
{
if(i==1)//for first node
{
q=q->next;
free(r);
*pp=q;
printf("\nValue found and deleted in position 1\n");
count=1;
}
else//for the rest of the nodes
{
printf("\nValue found and deleted in position %d\n",i);
r->next=q->next;
temp=q;
q=q->next;
Solution
You're making that specific task considerably more difficult than it needs to be. I've taken liberty to post an alternate to the entire function, and it should become obvious why. Regarding the specifics of the code you highlighted:
-
This is the big one: You're already given a pointer-to-pointer that addresses the head node pointer in entrance. Use that pointer to walk the actual pointers in the list; not just their values. Doing this will substantially clean up this code.
-
Make at least a reasonable attempt at validating the input parameter
-
The reporting separate-cases "position 1". This isn't needed. To report both the position of each deletion (1-based) as well as detect if a deletion took place, use two counters: one that maintains a position notice in the original list, the other to note how many items were actually discovered.
Doing the above as well as general cleanup gives you the following. You may find the logic in
It is possible to clean this up further, including writing three different functions and having them exclusively work on the list itself, leaving the UI and console interaction outside of this logic, but that would be well-beyond the scope of your question.
Best of luck
-
This is the big one: You're already given a pointer-to-pointer that addresses the head node pointer in entrance. Use that pointer to walk the actual pointers in the list; not just their values. Doing this will substantially clean up this code.
-
Make at least a reasonable attempt at validating the input parameter
-
The reporting separate-cases "position 1". This isn't needed. To report both the position of each deletion (1-based) as well as detect if a deletion took place, use two counters: one that maintains a position notice in the original list, the other to note how many items were actually discovered.
Doing the above as well as general cleanup gives you the following. You may find the logic in
'0' and '1' handling interesting, as it demonstrates a rare and useful case of fall-through case logic in a switch-case construct:void delete(struct node **pp)
{
struct node *temp = NULL;
int i, count, value;
char choice;
// early exit on empty list
if(!*pp)
{
printf("List is EMPTY\n");
return;
}
printf("\n\tPush 'v' to delete a specific value");
printf("\n\t\t\tOR");
printf("\n\t'1' to delete the first value");
printf("\n\t'0' to delete the last value:\t");
scanf(" %c", &choice);
switch(choice)
{
case '0':
// note: intentionally falls through to '1'
while ((*pp)->next)
pp = &(*pp)->next;
case '1':
temp = *pp;
*pp = temp->next;
free(temp);
break;
case 'v':
// we already know the list isn't empty so request value
printf("Enter value to delete: ");
if (scanf("%d", &value) != 1)
{
printf("Invalid input\n");
break;
}
// apparently the reporting is 1-based, not 0-based
for (i=1,count=0; *pp; ++i)
{
// if we find a match, free it. advance is built-in
if ((*pp)->data == value)
{
temp = *pp;
*pp = temp->next;
free(temp);
++count;
printf("Value found and deleted from position %d\n", i);
}
else
{ // advance over non-match
pp = &(*pp)->next;
}
}
if (count == 0)
printf("Value NOT found\n");
break;
default:
printf("\nBad choice");
}
}It is possible to clean this up further, including writing three different functions and having them exclusively work on the list itself, leaving the UI and console interaction outside of this logic, but that would be well-beyond the scope of your question.
Best of luck
Code Snippets
void delete(struct node **pp)
{
struct node *temp = NULL;
int i, count, value;
char choice;
// early exit on empty list
if(!*pp)
{
printf("List is EMPTY\n");
return;
}
printf("\n\tPush 'v' to delete a specific value");
printf("\n\t\t\tOR");
printf("\n\t'1' to delete the first value");
printf("\n\t'0' to delete the last value:\t");
scanf(" %c", &choice);
switch(choice)
{
case '0':
// note: intentionally falls through to '1'
while ((*pp)->next)
pp = &(*pp)->next;
case '1':
temp = *pp;
*pp = temp->next;
free(temp);
break;
case 'v':
// we already know the list isn't empty so request value
printf("Enter value to delete: ");
if (scanf("%d", &value) != 1)
{
printf("Invalid input\n");
break;
}
// apparently the reporting is 1-based, not 0-based
for (i=1,count=0; *pp; ++i)
{
// if we find a match, free it. advance is built-in
if ((*pp)->data == value)
{
temp = *pp;
*pp = temp->next;
free(temp);
++count;
printf("Value found and deleted from position %d\n", i);
}
else
{ // advance over non-match
pp = &(*pp)->next;
}
}
if (count == 0)
printf("Value NOT found\n");
break;
default:
printf("\nBad choice");
}
}Context
StackExchange Code Review Q#57160, answer score: 5
Revisions (0)
No revisions yet.