Recursively free a circular singly linked list? - c

I'm curious what base case can be used to recursively free a circular linked list, passing the head of the linked list as the only parameter. I originally thought a base case could be if (head->next == head) { return NULL; } could be sufficient to prevent head->next from pointing to itself, but this doesn't seem to be the case (literally and figuratively). The last node free(Head) is not being freed after the recursive calls here.
typedef struct node
{
int data;
struct node *next;
} node;
// temp stores the original head of the list
node *recursive_destroyer(node *head, node *temp)
{
if (head->next == temp)
return NULL;
recursive_destroyer(head->next, temp);
free(head);
head = NULL;
return NULL;
}

You asked about passing in one parameter
I think most people skipped your first sentence, and jumped to your code. You ask in your post:
I'm curious what base case can be used to recursively free a circular linked list, passing the head of the linked list as the only parameter. ...
You go on to explain the approach you tried:
I originally thought a base case could be if (head->next == head) { return NULL; } could be sufficient to prevent head->next from pointing to itself, but this doesn't seem to be the case ...
You provide a code example, but it passes in two parameters.
Remove head->next, not head
This answer is addressing the question in your first sentence. A short comparison with your approach will follow.
Checking to see if head->next points to head is a fine stopping case, but it means your recursive function needs to remove and destroy head->next at each iteration, and then recursively process the same list.
If head->next and head are the same, then destroy head, and you are done.
I don't see any point of returning a value from this function, so I removed it.
void recursive_destroyer(node *head) {
if (head == NULL) return;
if (head->next == head) {
destroy(head);
return;
}
node *tmp = head->next;
head->next = head->next->next;
destroy(tmp);
recursive_destroyer(head);
}
Notice there is no longer any need for a second parameter to the recursive function.
Comparison with your approach
There are some issues in your sample code that caused erroneous behavior. There are other answers that have addressed them with some depth. But, I did want to point out that you should prefer tail recursion whenever possible.
Tail recursion is a special case of a sibling call. A sibling call is when a function calls another function, and then immediately returns. In the example below, function_A() is making a sibling call to function_B()
void function_B () { puts(__func__); }
void function_A (bool flag) {
if (flag) {
function_B();
return;
}
puts(__func__);
}
A sibling call can be optimized by the compiler to reuse the stack frame of the current function to make the sibling call. This is because none of the current function state of the caller is needed after the sibling returns.
A tail recursive call can be optimized in the same way. Thus, the tail recursive call when optimized has the same memory footprint as an ordinary loop. And in fact, if the optimizer detects the sibling call is a recursive call, instead of performing a function call to itself, the tail recursion is converted into a jump to the start of the function. Most C compilers can perform this optimization. You can manually perform this optimization yourself, and easily convert a tail recursive function into a loop.
If you are using the optimization features of your C compiler, and it supports tail recursion optimization, then there is no technical reason to prefer a loop over tail recursion. If your software team finds reading recursive code confusing, then loops are preferred just to make the code easier to comprehend.

I wasn't setting the head equal to the recursive_destroyer function inside another function that frees the entire list. Here is that function:
LinkedList *destroy_list(LinkedList *list)
{
node *temp;
if (list == NULL)
return NULL;
if (list->head == NULL)
return NULL;
temp = list->head;
// was not setting list->head equal to this function.
// causing the original head to never become NULL
list->head = recursive_destroyer(list->head, temp);
if (list->head != NULL)
printf("Error, head not freed.. \n");
free(list);
return NULL;
}
Could have also passed a pointer to list->head to avoid setting list->head equal to the function.

Your code does not work. It will leave a single allocation intact.
Consider the circular linked list [1]. If you call recursive_destroyer(head, head), it will not free anything. The correct recursive code would be
void destroy_helper(node* const current, node* const original) {
if (current->next != original) destroy_helper(current->next, original);
free(current);
}
void destroy(node* const list) {
// null-check necessary since otherwise current->next is UB in destroy_helper
if (list) destroy_helper(list, list);
}
If we want to turn this into iterative code, we must first modify the destroy_helper function to be tail-recursive:
void destroy_helper(node* const current, node* const original) {
node* const next = current->next;
free(current);
if (next != original) destroy_helper(next, original);
}
which we can then rewrite to a loop:
void destroy(node* const list) {
if (list) {
node* current = list;
do {
node* next = current->next;
free(current);
current = next;
} while (current != list);
}
}
Edit:
To prove that my code is actually freeing everything, we can replace free with the following function:
void free_with_print(node* ptr) {
printf("Freeing node with value %d\n", ptr->data);
free(ptr);
}
A simple example:
int main() {
node* node1 = malloc(sizeof *node1);
node1->data = 1;
node1->next = node1;
node* node2 = malloc(sizeof *node2);
node2->data = 2;
node2->next = node1;
node1->next = node2;
destroy(node1);
}
using the iterative version prints
Freeing node with value 1
Freeing node with value 2
as expected. Trying the same thing with your original code prints
Freeing node with value 1
As expected, your code does not free one of the two nodes, while mine frees both.

For code like this you can (and should) do "single step debugging" in your head to convince yourself it should work as intended. This is a very important skill to learn.
Let's try 3 cases:
a) Imagine that the list is empty (head and temp are NULL). In this case it would crash at if (head->next == temp) due to trying to use a NULL pointer in head->next.
b) Imagine that the list has one item. In this case if (head->next == temp) is true because it's a circular linked list, so it returns from the first invocation without freeing anything.
c) Imagine the list has 2 items. In this case if (head->next == temp) is false for the first invocation and true for the second invocation; so the second invocation will free nothing and the first invocation will free the original head of the list.
We can extrapolate from that and say that the last item on the list is never freed (but the first item at the original head of the list will be freed if it's not also the last item).
To fix that you could always free the item, like:
if (head->next == temp) {
free(head);
return NULL;
}
This is messy because you're duplicating code (and could invert the condition to avoid that). It would also be easier to read if head always points to the original head and temp was temporary. Also (as mentioned in comments) there's no point returning NULL when it finishes. Refactoring the code gives you something like:
void recursive_destroyer(node *head, node *temp)
{
if (head->next != temp) {
recursive_destroyer(head, temp->next);
}
free(temp);
}
However; this still crashes if the list is originally empty. To fix that I'd do a wrapper function, like:
void recursive_destroyer(node *head) {
if(head != NULL) {
recursive_destroyer_internal(head, head);
}
}
static void recursive_destroyer_internal(node *head, node *temp)
{
The final problem is that recursion is bad (tends to be slower due to all the extra function calls, and has a risk of crashing when you run out of stack space, and often ends up harder for people to read); especially if/when the compiler can't do "tail call optimization" itself to convert it into a non-recursive loop. To fix that you shouldn't be using recursion. E.g.:
void destroy(node *head) {
node *original_head = head;
node *temp;
if(head != NULL) {
do {
temp = head;
head = head->next;
free(temp);
} while(head != original_head);
}
}

Related

Understanding type functions with linked-list

Good Morning/Evening everyone,
I'd like to clarify in my head the following concept about linked list functions (In this case recursive).
Let's take the following program that eliminates the duplicates in a linked list recursively :
ElementoDiLista* deleteDuplicates(ElementoDiLista* head)
{
if (head == NULL)
{
return NULL;
}
if (head->next == NULL)
{
return head;
}
if (head->info == head->next->info)
{
ElementoDiLista *tmp;
tmp = head->next;
head->next = head->next->next;
free(tmp);
return deleteDuplicates(head);
}
else
{
head->next = deleteDuplicates(head->next);
return head;
}
}
With the definition of my struct and list this way :
struct el {int info; struct el *next;};
typedef struct el ElementoDiLista;
typedef ElementoDiLista *ListaDiElementi;
And then in the main i call the function this way :
Lista1 = deleteDuplicates(Lista1);
Where Lista1 is declared as follows : ElementoDiLista Lista1 = NULL
My question was, i was used to declare functions that are void or depends on single types (int,float ecc...)
I'd like to clarify two things :
Why the function is declared as ElementoDiLista* deleteDuplicates(ElementoDiLista* head) because to me it is more intuitive this way ListaDiElementi deleteDuplicates (ListaDiElementi *head) but unfortunately,doesn't work.
It is not very clear to me why the function returns head or NULL values, but this is the reason i think why in the main Lista1 takes the value of the function, because the function modifies the list itself, am i right ?
I'm sorry if the questions are not very exciting i'm just trying very hard to understand list in general and are quite difficult,
Any help or advise would be appreciated,
Thank you all anyway!
Why the function is declared as ElementoDiLista* deleteDuplicates(ElementoDiLista* head) because to me it is more intuitive this way ListaDiElementi deleteDuplicates (ListaDiElementi *head)
Starting with the argument, originally it is declared as,
ElementoDiLista* head
so it takes a pointer to the head element. This would have been equivalent (with a variable name change) to,
ListaDiElementi list
So we're passing as argument a "list", which is a pointer to the head. That pointer is not modified. To modify it indeed we'd need to use as you suggest,
ElementoDiLista** head
or equivalently and perhaps more readable,
ListaDiElementi* list
The question is therefore, "do we need to modify the pointer to the head"? Or in other words, does the original list pointer need to be modified? The answer is no.
If the list is null it will remain null. If the list is not null the head will remain the same. You won't remove the head, only the nodes that follow which hold the same value the head does.
It is not very clear to me why the function returns head or NULL values, but this is the reason i think why in the main Lista1 takes the value of the function, because the function modifies the list itself, am i right ?
Personally I don't like that the function returns a pointer to an element (i.e. a list). Also it appears to always return head, and to be implemented in a rather obfuscated manner.
Firstly regarding me not liking it. If you are changing the structure of an existing list, rather than creating a new one, you want your initial pointer to remain valid after the procedure. So you change that pointer (if you need to) rather than returning a new one. In this case it doesn't even change. So I would either have it void or return some exit code.
Secondly look at the code,
if (head == NULL)
{
return NULL;
It returned head since it is null.
if (head->next == NULL)
{
return head;
It returned head again.
if (head->info == head->next->info)
{
ElementoDiLista *tmp;
tmp = head->next;
head->next = head->next->next;
free(tmp);
return deleteDuplicates(head);
}
It returns deleteDuplicates(head) and head is the original argument which hasn't changed. So if all other cases return head, so will this.
else
{
head->next = deleteDuplicates(head->next);
return head;
}
This too returns head (note head hasn't changed). So its return value is always the original argument head. So this is pointless.
Furthermore note that in the first two cases you do nothing, you just return a value that is useless.
if (head == NULL)
{
return NULL;
}
if (head->next == NULL)
{
return head;
}
So if you do change your procedure to void this code disappears. You don't have to do anything if your list or its tail is null, since there are no duplicates.

Modifying LinkedList Through A Function

For my program, I need to create a function that accepts a linkedlist as a parameter, then deletes the first node from the list. There are other steps, but I'd like to get this sub-part done first.
This is what I have so far:
struct node *del_the_first(struct node *head) {
struct node *temp = head;
head = head->next;
temp->next = NULL;
return NULL;
}
I believe my solution is correct, however I have no way of testing it at this time. I'm more interested in why I am or am not wrong.
What you should test is:
print the value of temp at the end of the function,
this is what head was at the start of the function
print the value of head at the end of the function,
which is what the head of the list should be after returning for the function
print (from outside the function, e.g. from main) the value of the variable
which is supposed to point to the head of the list,
especially after deleting the first element
You will notice that outside your function the pointer to the head of the list is still pointing to where the first element still is.
You do not want that, do you? The variable which points to the head of the list is supposed to point to the second element of the list, isn't it?
If above is true, you probably want to use free() on the formerly first element of the list, before returning from the function.
Read this for more information on how to fix the first problem:
Parameter Passing in C - Pointers, Addresses, Aliases
Basically, you will want to return the new value of the pointer to the head of the list:
struct node *del_the_first(struct node *head)
{
struct node *temp = head;
head = head->next;
temp->next = NULL; /* not really needed */
free(temp);
return head;
}
Then call it like:
global_head = del_the_first(global_head);
Note that this code assumes that the list is not empty,
see the answer by ccpan on how to remove this assumption.
You need to check for boundary conditions. Suppose your linkedList is empty, then during runtime, you will get a segmentation fault. So you need to check if head pointer is NULL or not before trying to access next node.
Also, I don't know why you are returning a NULL. You are most probably wanting to return the new head node pointer.
struct node *del_the_first(struct node *head) {
if (head != NULL) {
struct node *temp = head;
head = head->next;
free(temp);
temp = NULL;
}
return head;
}

Can't find the segmentation fault in my code

#include <stdio.h>
#include <stdlib.h>
typedef struct node{
struct node* next;
int value;
}Node;
int findLastNodeValue(Node* head){
while(head -> next != NULL){
head = head -> next;
}
return head -> value;
}
int main(){
Node *node1,node2;
node1 = (Node *)malloc(sizeof(Node);
node2 = NULL;
node1 -> next = node2;
findLastNodeValue(node1);
findLastNodeValue(node2);
return 0;
}
This code giving a segmentation fault. But I cannot find why does it occur. Can you help me with this one.
There are multiple issues with your code:
You are mallocing node1 yet you are not setting the value anywhere. This creates undefined behaviour whenever you try to access value - you might crash the program or you might get garbage data, which is usually worse since it then leads to other parts of your code acting weird.
You are not freeing the dynamically allocated memory. While in your case it's not such big of a deal it tells me that you are not familiar with the way dynamic allocation works (my belief is also strengthened by the first bullet point in this list). Whenever you malloc something, always free it (in C++ you have new and delete) and (to prevent unexpected behaviour) set the pointer to NULL.
node2 is not a pointer. The * in Node *node1, node2; applies only to the first variable. Each consecutive variable also needs a * otherwise it will be allocated on the stack.
By looking at your code it's clear that you want node2 to be a pointer (otherwise you wouldn't be assigning NULL as its value :)). In that case you are trying to access next of node2 but node2 is initialized to NULL:
int findLastNodeValue(Node* head){ // You are passing node2, which is NULL
while(head -> next != NULL){ // Can't access next of a NULL -> code breaks
head = head -> next;
}
return head -> value;
}
As a general rule do the following:
Try using a function that instantiates your node - since this is C you don't have a constructor I would suggest to write a function (or several depending on how much functionality you need) that generates a new node. This way you will ensure that at least there is no chance of not initializing a node
For example:
Node* createNode(int value) {
Node* node = (Node *)malloc(sizeof(Node));
if (!node) return NULL; // If malloc fails for some reason
node -> value = value;
node -> next = NULL;
return node;
}
Try using a function that deletes your node - if there is any chance of accessing a deleted reference again, set it to NULL and handle the NULL value accordingly
For example:
void deleteNode(Node** node) {
if (!*node) return;
free(*node);
*node = NULL;
}
Note that the code above doesn't delete what's reference by next since we only want to delete the node we are passing to the function. If you have previous (in the case of a double linked list), you will have to first access the next node, set its previous value to NULL and then delete your current node.
Whenever you pass a pointer, always check if that pointer is NULL before you do anything with the data that it's supposedly referencing. Combining this with the node creator function from the first point you can be certain that you are not passing some Node pointer that has not been initialized properly
Now as to your function in particular I would do the following:
int findLastNodeValue(Node* head) {
if (!head) return -1; // We have a null reference, so there is nothing else to do here; exit accordingly and check the return value to see if the function call has been successful
while(head -> next != NULL) {
head = head -> next;
}
return head -> value;
}
findLastNodeValue(node2) is your biggest problem. When you send NULL into findLastNodeValue, the very first thing you try to do is dereference the NULL pointer at the clause (head -> next != NULL).
To resolve this, you can check for and handle the case when head is NULL in your findLastNodeValue function before your while loop.

Reverse Linked List Recursively without using any new variable

In a job interview I was asked to write a function in C that recursively reverses a linked list, returns the new first node, and doesn't use any new nodes.
How can you do this?
Here's the general idea, in an agnostic language with C-like syntax:
Node invert(Node list, Node acc) {
if (list == null)
return acc;
Node next = list.next;
list.next = acc;
return invert(next, list);
}
The above function receives the first node of the list to be inverted and the accumulated value so far, and returns the head of the newly-inverted list - the reversal of nodes is done in-place, no extra space is allocated (besides a local variable in the stack). Call it like this:
invert(firstNodeOfList, null);
This is an example of a tail recursion: the result gets collected in the acc parameter, and when each recursive call returns, there's nothing left to do, just return the accumulated value.
UPDATE:
In a functional language it's easier and more natural to write a function to reverse a list without using a local variable, for instance look at the following code in Scheme - it has drawback, that it will create a new list node when calling the cons procedure:
(define (invert lst acc)
(if (empty? lst)
acc
(invert (rest lst)
(cons (first lst) acc))))
(invert '(1 2 3 4 5) '())
> '(5 4 3 2 1)
Bottom line: you either create a new node or create a new local variable at each recursive call, but unless the programming language offers an expression for sequential execution and the compiler guarantees evaluation order (see #WillNess' answer) you can't eliminate both and have a working algorithm. Better play it safe and use a temporary variable for enforcing evaluation order and always obtaining correct results.
This is very simple: recurse to the end of the list, passing in the new next pointer each time and returning the end of the list.
struct list {
struct list *next;
};
struct list *reverse(struct list *cur, struct list *prev) {
struct list *next = cur->next;
cur->next = prev;
if(next == NULL) return cur;
return reverse(next, cur);
}
reverse(head, NULL);
I don't know what a "recursively linked list" is so I'll go with your title and assume you want to "reverse a linked list" using a recursive function.
I'll assume it's a singly linked list (so each element has a single pointer next to the next element) and by convention take next = NULL for the last element. For doubly linked lists you have to also deal with the prev pointers but it is basically the same idea. With a doubly linked list it is simpler since you don't even need to keep track of the previous element; you just have to flip the two pointers around on each element.
To reverse the list we can imagine walking down it an element at a time, flipping the next pointers to point to the previous element as we go. This is easy enough to write as a recursive function which takes as parameters a pointer to the current element and a pointer to the previous element (at the start this pointer is NULL).
node* reverseList(node* head, node* prev = NULL) {
if (head == NULL) return prev;
std::swap(head->next, prev); // prev points to next element to process
return reverseList(prev, head);
}
Hey guys please find the programs for reverse linked list with and without recursion below
LINK *reverse_linked_list_recursion(LINK *head)
{
LINK *temp;
if (!head) {
printf("Empty list\n");
return head;
}
else if (!head->next)
return head;
temp = reverse_linked_list_recursion(head->next);
head->next->next = head;
head->next = NULL;
return temp;
}
LINK *reverse_linked_list_without_recursion(LINK *head)
{
LINK *new, *temp;
if (!head) {
printf("No element in the list\n");
return head;
}
else {
new = head;
while (head->next) {
temp = head->next;
head->next = temp->next;
temp->next = new;
new = temp;
}
}
return new;
}
So, the variation on Óscar López's answer here is
Node invert(Node list, Node acc)
{
if (!list)
return acc;
return invert(list.next, ((list.next=acc),list));
}
The sequence of expressions (a,b) returns its last value and is evaluated left-to-right.
This will only work for compilers which guarantee evaluation order of function's arguments as left-to-right. If it is right-to-left, the arguments to invert should be swapped.
If the evaluation order is nondeterministic, this will not work. The C standard apparently leaves this order unspecified, but some compilers might have their own way with it. You may know this about your compiler, but it is non-portable, and your compiler may change this in the future too. Writing such code is frowned upon. Maybe that is what your interviewers wanted to hear from you. They like such tricks, and want to see how you respond.
The normal way to control evaluation order is by using temporary variables.

Pointers in c: Function which deletes every second element of linked list

I want to write a function which gets a pointer to a header of a linked list and deletes from the list every second member of it. The List is a linked elements of type element:
typedef struct element{
int num;
struct element* next;
}element;
I'm new to all these pointers arithmetic so I'm not sure I write it correctly:
void deletdscnds(element* head) {
element* curr;
head=head->next; //Skipping the dummy head//
while (head!=NULL) {
if (head->next==NULL)
return;
else {
curr=head;
head=head->next->next; //worst case I'll reach NULL and not a next of a null//
curr->next=head;
}
}
}
I kept changing it since I kept finding errors. Can you please point out any possible errors?
The algorithm is a lot simpler if you think of your linked list in terms of node pairs. Each iteration of your loop should process two nodes - head and head->next, and leave head equal to head->next->next upon exit. It is also important to not forget deleting the middle node, if you are cutting it out of the list, otherwise you are going to see memory leaks.
while (head && head->next) {
// Store a pointer to the item we're about to cut out
element *tmp = head->next;
// Skip the item we're cutting out
head->next = head->next->next;
// Prepare the head for the next iteration
head = head->next;
// Free the item that's no longer in the list
free(tmp);
}
It might be most straightforward to visualize this problem in recursive terms, like this:
// outside code calls this function; the other functions are considered private
void deletdscnds(element* head) {
delete_odd(head);
}
// for odd-numbered nodes; this won't delete the current node
void delete_odd(element* node) {
if (node == NULL)
return; // stop at the end of the list
// point this node to the node two after, if such a node exists
node->next = delete_even(node->next);
}
// for even-numbered nodes; this WILL delete the current node
void delete_even(element* node) {
if (node == NULL)
return NULL; // stop at the end of the list
// get the next node before you free the current one, so you avoid
// accessing memory that has already been freed
element* next = node->next;
// free the current node, that it's not needed anymore
free(node);
// repeat the process beginning with the next node
delete_odd(next);
// since the current node is now deleted, the previous node needs
// to know what the next node is so it can link up with it
return next;
}
For me, at least, this helps clarify what needs to be done at each step.
I wouldn't advise actually using this method because, in C, recursive algorithms may take up a lot of RAM and cause stack overflows with compilers that don't optimize them. Rather, dasblinkenlight's answer has the code that you should actually use.

Resources