Recursively removing the duplicate elements in a linked list - c

I was trying to remove the duplicate element in a sorted linked list using recursion concept.
I wanna see how to remove the elements in a sorted linked list. I made a code in which if head->data == head->next->data than head->next should be freed until we get the different element.
Now I have made so many changes I am confused how I am supposed to do it. It is deleting every value that is duplicate and only leaving the one that was only appeared in the entire code only once.
Please also tell me why this code is doing this and also what can wrong with code and if any optimal way possible to do the same thing.
(I am only providing the deleteduplicate function if there is a need to provide the whole code like print the list or insert in the list I will edit it if told).
Thanks.
Node *deleteDuplicates(Node *head) {
if (head == NULL || head->next == NULL) {
return head;
}
if (head->data == head->next->data) {
struct Node *x = head->next;
head->next = head->next->next;
free(x);
return deleteDuplicates(head);
} else
return deleteDuplicates(head->next);
}
Input: 11 11 11 13 13 20
Output: 20
Expected output: 11 13 20

It is deleting every value that is duplicate and only leaving the one value that was only appeared in the entire code only once.
No. It is deleting only duplicate values but you always return pointer to the last node.
if(head==NULL ||head->next==NULL){
return head;
}
You don't need to return the new head, since only duplicates are going to be removed, there is no way head is going to change.

There is no need for recursion in this function. Just iterate in a loop either removing the next element or skipping to the next element:
Node *deleteDuplicates(Node *head) {
if (head != NULL) {
Node *p = head;
while (p->next) {
if (p->next->data == p->data) {
Node *x = p->next;
p->next = x->next;
free(x);
} else {
p = p->next;
}
}
}
return head;
}
You could fix your recursive function, but it should be modified to not return the head node as this prevents tail recursion, therefore requiring a potentially huge amount of stack space. A sufficiently long list would cause a Stack overflow.
Here is a modified recursive function:
void deleteDuplicates(Node *head) {
if (head != NULL && head->next != NULL) {
if (head->data == head->next->data) {
struct Node *x = head->next;
head->next = x->next;
free(x);
deleteDuplicates(head);
} else {
deleteDuplicates(head->next);
}
}
}
The problem in your code is you store the return value of deleteDuplicates into your head pointer, but the function returns the pointer to the last node in the list, not the head node.

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.

remove element from linked list at any position in c language

i have a linked list like this :
1,jhon,19
2,sara,18
3,tom,20
4,jack,22
i have been trying forever to delete an element based on their id (witch is the first number). But in order to do that i need to delete this element from any position. so i came up with this code and i was wondering if it's correct :
temp1=head;
if(head!=NULL && head->id==givenID) // if the element is in the first position
{
temp = head;
head = head->next;
free(temp);
}
else if(head!=NULL && head->id!=givenID){// search for the element in the middle
do{
temp2=head;
head = head->next;
}while(head->id !=givenID && head->next !=NULL);
if(head->next !=NULL && head->id==givenID){// if the element is in the middle
temp2->next=head->next;
free(head);
head=temp1;
}
else if(head->next ==NULL && head->id==givenID){// if the element is in the last position
temp->next=NULL;
free(head);
head=temp1;
}
}
Thank you
This code is too complex, because it has unnecessary branches. You can unify your code by using a pointer to pointer.
The idea is to point your pointer to pointer to the head of the list, then to the next pointer of the initial element of the list, then to the next pointer of the second element of the list, and so on. The beauty of this approach is that no matter where you are in your list, the operation on a pointer to pointer remains the same!
Here is how it looks in code:
// Point your pointer to pointer to the head of the list
struct node **pptr = &head;
while (*pptr != NULL) {
// Dereference pptr to get the pointer to current node
node *current = *pptr;
// Check if the id of this node matches what we're looking for
if (current->id == givenID) {
// Here is the "magic": assign the next pointer of the current node
// to whatever is pointed to by pptr.
// It could be a head, or a next of some node.
*pptr = current->next;
free(current);
break;
}
pptr = &(current->next);
}
That's it! Since the pointer to pointer does not differentiate between head and other nodes, there is no additional checking going on.
Consider using a sentry node. All special cases disappear when you do.
This is how node erasure look in a linked list with sentry:
Iterator Erase( List* lst, Iterator here )
{
Iterator nxt = here->next;
Link( here->prev, here->next );
free( here );
lst->size -= 1;
return nxt;
}
with Link being no more complicated than
void Link( Iterator n1, Iterator n2 )
{
n1->next = n2;
n2->prev = n1;
}
All the other core functions, like insert etc. are similarly trivial.

Segmentation fault deleting nodes from singly linked list

this is the case i am working on
[11] -> [12] -> [13] -> NULL
I am trying to delete the elements from the liked list above(example) but I keep getting segfault and on running GDB doesnot help much. I am not looking for an answer but and explanation on where I am going wrong logically.
here is the code
int
List:: remove( int val )
{
ListNode *headNode = _head;
ListNode *tempNode = NULL;
if(headNode->_value == val){
tempNode = headNode->_next;
delete headNode;
_head = tempNode;
}
else
{
while(headNode->_value != val){
tempNode = headNode;
headNode = headNode->_next;
}
tempNode->_next = headNode->_next;
delete headNode;
}
}
You're not accounting for the following conditions:
The list may be empty; i.e. _head is NULL;
The value may not be in the list at all.
Your function is declared to return int, but makes no such return
Assuming the rest of your code is correct (and that is a big assumption), I'm all-but-certain this is what you're trying to do:
void List::remove( int val )
{
ListNode *headNode = _head;
ListNode *tempNode = NULL;
while (headNode && headNode->_value != val)
{
tempNode = headNode;
headNode = headNode->next;
}
if (headNode)
{
if (tempNode)
tempNode->next = headNode->next;
else
_head = headNode->next;
delete headNode;
}
}
Alternatively, if so inclined this can get (arguably) simpler utilizing a pointer-to-pointer to traverse the pointers in the list, not just their values. It is worth investigating how the following works, which still covers all the bases described previously, but does so using the actual pointers in the list nodes themselves, including _head, by-address rather than by-value, thereby eliminating the need for a walk-behind temporary pointer:
void List::remove( int val )
{
ListNode **pp = &_head;
while (*pp && (*pp)->_value != val)
pp = &(*pp)->next;
if (*pp)
{
ListNode *p = *pp;
*pp = p->next;
delete p;
}
}
In your remove method you are assuming there are always elements in your list. - What if it is empty?
What if the value isn't in the list? You need to handle this case as well.
You're headed in the right direction - there are just a few cases that you haven't considered that can lead you to seg fault.
Example of forward traversal with deletion (forward-only linked list):
// Start from the beginning (head), then while the current isn't null,
// move to the next node.
for (ListNode* current = head; current != null; current = current->next) {
// Check the next item if there is one, and remove it if it matches the value.
// We check the next one because you can't delete the current node in a
// forward only linked list (can in a doubly-linked list however)
if (current->_next != nullptr && current->_value == value) {
// Make this item point to the next next item
// (Since we're gonna delete the next item)
current->_next = current->_next->next;
// Delete the next item.
delete current->_next;
}
}

linked list reverse without temp

Is there any way to reverse linked list without using temp variable in C?
Thanks in advance.
the famous approach:
Element *reverse(Element *head)
{
Element *previous = NULL;
while (head != NULL) {
// Keep next node since we trash
// the next pointer.
Element *next = head->next;
// Switch the next pointer
// to point backwards.
head->next = previous;
// Move both pointers forward.
previous = head;
head = next;
}
return previous;
}
uses temp variable
Saurabh
Note that your temp usage is actually generating two swap() calls, and can be replaced with:
swap(head->next,previous);
swap(previous,head);
You can swap without temps using xor, it is called xor swap.
Use XOR-swaps on the pointers to fake an XOR-linked-list.
Implementation is left to the reader as an exercise.
Recursive approach :
Element *reverse(Element *head, Element **first)
{
if (head->next == NULL)
{
*first = head;
return head;
}
Element* NextElement= reverse (head->next, first);
NextElement->next = head;
head->next = null;
return head;
}
Call for recursive function:
Element *revLinkListHead;
reverse(head, &revLinkListHead);
If someone is still interested, here is the solution that uses no new variables at all, except for those passed in recursive call.
public static List invert(List l) {
invert(l.next, l, l);
l = l.next;
breakCycle(l, l);
return l;
}
private static void invert(List l, List toBeNext, List first) {
if(l.next == null) {
l.next = toBeNext;
first.next = l;
} else {
invert(l.next, l, first);
l.next = toBeNext;
}
}
private static void breakCycle(List l, List first) {
if(l.next == first) {
l.next = null;
} else {
breakCycle(l.next, first);
}
}
The idea is the following: first we run invert function recursively, and implement it so that when it reaches the last element it assigns it as a next element of current head (parameter first). After we executed it, we will have a reversed list but cycled, so the current head.next will point at the head of the reversed list. We reassign head to its next element (the actual head of the reversed list), and the last thing we have to do is to break the cycle. So we call breakCycle which does the job recursively!

How to free only a single node in a linked list in C?

How can I free only a single node in a linked list? The following frees the whole linked list but I wanted to free only one node in the linked list.
//Here's my code for delete
while(headPtr!=NULL)
{
temp = headPtr;
headPtr = headPtr->next;
if(strcmp(temp->fname, stdfname) ==0 &&
strcmp(temp->sname, stdsname) ==0 )
{
free(temp);
}
}
You first need to know the previous node. Because of that, you need to iterate until you hit the node you want to delete. In that process you need to remember the previous node. Then you need to connection the previous and next nodes, thus "delinking" the node you want to delete.
currentNode = headNode;
previousNode = NULL;
while (currentNode != NULL) {
if (currentNode != nodeToDelete) {
// Not the node we want to delete yet,
// go on to next node.
previousNode = currentNode;
currentNode = currentNode->next;
continue;
}
// We've now hit the node to delete and know the
// previous node. Fix the structure.
if (previousNode) {
previousNode->next = nodeToDelete->next;
} else {
// No previous node means it's the head node.
headNode = nodeToDelete->next;
}
// The node is now delinked from list. Delete it.
free(nodeToDelete);
// Stop the loop.
break;
}
This is pretty bad performance-wise, which is why there are double-linked lists. There, the whole operations looks like this:
if (nodeToDelete->previous) {
nodeToDelete->previous->next = nodeToDelete->next;
}
if (nodeToDelete->next) {
nodeToDelete->next->previous = nodeToDelete->previous;
}
if (nodeToDelete == headNode) {
headNode = nodeToDelete->next;
}
free(nodeToDelete);
As you can see, no iteration is necessary here as each node knows its previous and next nodes.
BTW, to work these things out (they are pretty basic) it helps to draw a short linked list on a piece of paper. Draw boxes, in each box write the member names (like previous and next) and draw lines from these members to the corresponding other boxes. Then think about what is necessary to do in order to delete the node. It really helps you understand how this works.
The head of the list should never change unless the node you're deleting is the head. You should be moving the temp pointer down the list. You also need to fix up the links when you delete a node, note that there are three cases you need to be aware of and the case where it's the first node requires special handling. I'll give you the skeleton for the code, but since I don't know whether your list is singly- or doubly-linked, I'll leave the pointer updates to you.
temp = headPtr;
prev = NULL;
while (temp != NULL)
{
if (strcmp(temp->fname, stdfname) == 0)
&& strcmp(temp->sname, stdsname) == 0)
{
if (prev == NULL) { // head node
...
}
else if (temp->next == NULL) { // tail node
...
}
else { // interior node
...
}
break; // stop when done
}
prev = temp;
temp = temp->next;
}

Resources