Segmentation fault with unknown reason (C) - c

I was trying to solve my data structure homework, and I used doubly linked list in my code. But when I used it to create a new node and return to main, I got a segmentation fault.
my function(append) is like:
void append(struct Node** RailHead[], int new_data, int r){
/* 1. allocate node */
printf("1.\n");
struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
struct Node* last = RailHead[r]; /* used in step 5*/
/* 2. put in the data */
new_node->data = new_data;
/* 3. This new node is going to be the last node, so
make next of it as NULL*/
new_node->next = NULL;
/* 4. If the Linked List is empty, then make the new
node as head */
if (RailHead[r] == NULL){
new_node-> prev = NULL;
RailHead[r] = new_node;
return;
}
/* 5. Else traverse till the last node */
while (last->next != NULL)
last = last->next;
/* 6. Change the next of last node */
last->next = new_node;
/* 7. Make last node as previous of new node */
new_node-> prev = last;
return;
}
RailHead is the pointer array that I adopted to store the different head reference of doubly linked lists, and r is the assigned rail (by input) to decide which rail should append new data.
I would like to understand what's going on with my code. If anyone can help, I will be really appreciate about it! Thank you!

Related

Dynamic memory allocation - free

I was looking over examples of dynamic memory allocation being used in c programs but I've noticed that many of them do not use free at the end. I was wondering if that's just an error on the programmer's part or if there are some instances in which you shouldn't free up pointers at the end. Here is an example I saw. If I am right and there should be a free where would be the appropriate place to put it? (https://www.geeksforgeeks.org/linked-list-set-2-inserting-a-node/)
// A complete working C program to demonstrate all insertion methods
// on Linked List
#include <stdio.h>
#include <stdlib.h>
// A linked list node
struct Node
{
int data;
struct Node *next;
};
/* Given a reference (pointer to pointer) to the head of a list and
an int, inserts a new node on the front of the list. */
void push(struct Node** head_ref, int new_data)
{
/* 1. allocate node */
struct Node* new_node = (struct Node*) malloc(sizeof(struct Node));
/* 2. put in the data */
new_node->data = new_data;
/* 3. Make next of new node as head */
new_node->next = (*head_ref);
/* 4. move the head to point to the new node */
(*head_ref) = new_node;
}
/* Given a node prev_node, insert a new node after the given
prev_node */
void insertAfter(struct Node* prev_node, int new_data)
{
/*1. check if the given prev_node is NULL */
if (prev_node == NULL)
{
printf("the given previous node cannot be NULL");
return;
}
/* 2. allocate new node */
struct Node* new_node =(struct Node*) malloc(sizeof(struct Node));
/* 3. put in the data */
new_node->data = new_data;
/* 4. Make next of new node as next of prev_node */
new_node->next = prev_node->next;
/* 5. move the next of prev_node as new_node */
prev_node->next = new_node;
}
/* Given a reference (pointer to pointer) to the head
of a list and an int, appends a new node at the end */
void append(struct Node** head_ref, int new_data)
{
/* 1. allocate node */
struct Node* new_node = (struct Node*) malloc(sizeof(struct Node));
struct Node *last = *head_ref; /* used in step 5*/
/* 2. put in the data */
new_node->data = new_data;
/* 3. This new node is going to be the last node, so make next of
it as NULL*/
new_node->next = NULL;
/* 4. If the Linked List is empty, then make the new node as head */
if (*head_ref == NULL)
{
*head_ref = new_node;
return;
}
/* 5. Else traverse till the last node */
while (last->next != NULL)
last = last->next;
/* 6. Change the next of last node */
last->next = new_node;
return;
}
// This function prints contents of linked list starting from head
void printList(struct Node *node)
{
while (node != NULL)
{
printf(" %d ", node->data);
node = node->next;
}
}
/* Driver program to test above functions*/
int main()
{
/* Start with the empty list */
struct Node* head = NULL;
// Insert 6. So linked list becomes 6->NULL
append(&head, 6);
// Insert 7 at the beginning. So linked list becomes 7->6->NULL
push(&head, 7);
// Insert 1 at the beginning. So linked list becomes 1->7->6->NULL
push(&head, 1);
// Insert 4 at the end. So linked list becomes 1->7->6->4->NULL
append(&head, 4);
// Insert 8, after 7. So linked list becomes 1->7->8->6->4->NULL
insertAfter(head->next, 8);
printf("\n Created Linked list is: ");
printList(head);
return 0;
}
At the end of the program all memory is freed anyway so at one level it makes no difference.
Having said that the sample linked list really should have a deleteList function as this is meant to be an example of 'how to do it right'. And that delete should be called at the end of main. Somebody cutting and pasting this code will leak all over the place.
Its also a good habit to get into, tidy up after yourself. If you ever use valgrind you will want it to give you a clean bill of health
There are no circumstances under which it is ever 'bad' to free up the memory you used at the end of the program.

Does return; do anything in this function?

I'm trying to learn linked list following the instructions given in this tutorial and I can't seem to understand whether if the return; in the if statement at step 4 of the following code does anything...
/* Given a reference (pointer to pointer) to the head
of a list and an int, appends a new node at the end */
void append(struct Node** head_ref, int new_data)
{
/* 1. allocate node */
struct Node* new_node = (struct Node*) malloc(sizeof(struct Node));
struct Node *last = *head_ref; /* used in step 5*/
/* 2. put in the data */
new_node->data = new_data;
/* 3. This new node is going to be the last node, so make next of it as NULL*/
new_node->next = NULL;
/* 4. If the Linked List is empty, then make the new node as head */
if (*head_ref == NULL)
{
*head_ref = new_node;
return; //<<<this return here
}
/* 5. Else traverse till the last node */
while (last->next != NULL)
last = last->next;
/* 6. Change the next of last node */
last->next = new_node;
return;
}
would the following be equally functional as the above if there was no return;statement like this?
/*4. If the Linked List is empty, then make the new node as head */
if(*head_ref == NULL)
{
*head_ref = new_node;
}
/*step 5*/
/*step 6*/
It does the same thing it does anywhere else, it exits from the function immediately.
If you don't return, you'll continue executing the function, but it won't work properly. The next block of code is:
while (last->next != NULL)
If that if was true, then last == NULL (because of the initialization last = *head). This code will try to indirect through the null pointer, which is undefined behavior.
There's no point in executing the rest of the code, which appends the new node after the last node in the list. The list was empty, so there's no last node to append to. You've already inserted it as the first node in the if block.
BTW, if is not a loop. A loop executes code repeatedly, and they're written using for and while. if is a conditional, it either executes the code once or it doesn't execute it at all.
Yes it changes the code flow. When the condition in 4 is true then *head_ref = new_node; is executed and the function then returns with no more processing.
If you take the return out then 5 and 6 will always be executed regardless of whether 4 is or not.
An alternative style (avoiding having a return in the middle of the function) would be to have an else for steps 5 and 6. Like the following:
/* 4. If the Linked List is empty, then make the new node as head */
if (*head_ref == NULL)
{
*head_ref = new_node;
}
else
{
/* 5. Else traverse till the last node */
while (last->next != NULL)
last = last->next;
/* 6. Change the next of last node */
last->next = new_node;
}
Note there is no need to have the return at the last line as the function does not return a value.

Time Complexity?

Problem was to find the intersection of 2 sorted linked lists and store the common elements in the third list.
My approach was to make temporary pointers temp1 and temp2 initializing both to head1 (head of list 1) and head2 (head of list 2) respectively.And then traversing both lists and comparing elements and shifting temp1 and temp2 accordingly.The code works fine.
Test case:
First linked list=> 1->2->3->4->6
Second linked list be 2->4->6->8, then function should create and return a third list as 2->4->6.
But I am confused about what is the time complexity: O(m+n) or O(min(m,n))? (m,n are number of elements in list 1 and list 2).
My Code:
#include<stdio.h>
#include<stdlib.h>
/* Link list node */
struct Node
{
int data;
struct Node* next;
};
void append(struct Node** head_ref, int new_data)
{
/* 1. allocate node */
struct Node* new_node = (struct Node*) malloc(sizeof(struct Node));
struct Node *last = *head_ref; /* used in step 5*/
/* 2. put in the data */
new_node->data = new_data;
/* 3. This new node is going to be the last node, so make next
of it as NULL*/
new_node->next = NULL;
/* 4. If the Linked List is empty, then make the new node as head */
if (*head_ref == NULL)
{
*head_ref = new_node;
return;
}
/* 5. Else traverse till the last node */
while (last->next != NULL)
last = last->next;
/* 6. Change the next of last node */
last->next = new_node;
return;
}
struct Node* sortedIntersect(struct Node* head1,struct Node*head2)
{
struct Node*head3=NULL;
struct Node*temp1=head1;
struct Node*temp2=head2;
while(temp1!=NULL&&temp2!=NULL)
{
if(temp1->data<temp2->data)
{if(temp1->next!=NULL)
temp1=temp1->next;
else
break;
}
else if(temp1->data>temp2->data)
{
if(temp2->next!=NULL)
temp2=temp2->next;
else{
break;
}
}
else
{
append(&head3,temp1->data);
temp1=temp1->next;
temp2=temp2->next;
}
}
return head3;
}
/* Function to insert a node at the beginging of the linked list */
void push(struct Node** head_ref, int new_data)
{
/* allocate node */
struct Node* new_node = (struct Node*) malloc(sizeof(struct Node));
/* put in the data */
new_node->data = new_data;
/* link the old list off the new node */
new_node->next = (*head_ref);
/* move the head to point to the new node */
(*head_ref) = new_node;
}
/* Function to print nodes in a given linked list */
void printList(struct Node *node)
{
while (node != NULL)
{
printf("%d ", node->data);
node = node->next;
}
}
int main()
{
/* Start with the empty lists */
struct Node* a = NULL;
struct Node* b = NULL;
struct Node *intersect = NULL;
/* Let us create the first sorted linked list to test the functions
Created linked list will be 1->2->3->4->5->6 */
push(&a, 6);
push(&a, 5);
push(&a, 4);
push(&a, 3);
push(&a, 2);
push(&a, 1);
/* Let us create the second sorted linked list
Created linked list will be 2->4->6->8 */
push(&b, 8);
push(&b, 6);
push(&b, 4);
push(&b, 2);
/* Find the intersection two linked lists */
intersect = sortedIntersect(a, b);
printf("\n Linked list containing common items of a & b \n ");
printList(intersect);
return 0;
}
This is a problem that can be solved in O(m+n).
However, this solution is not O(m+n). Each element you add in the method intersectSorted is added with the method append, which traverses the whole current output list.
So the time complexity is O((m+n)log(min(m,n))
As in each iteration of while, at least one of the pointers go to the next, and the condition of while termination is none of the pointers reach to the end, the time complexity would be O(min(m,n)).
In worst case it will be O(m+n)
example: {1,3,5,7} {2,4,6,8}
in this case you traverse both the
list.
In average case it will be
O(min(m,n) + number of times you don't increment the smaller list)
example : {1,2,3,4,6} {2,4,6,8,9,10}
in this case you terminate
the loop once you reach the end of first lined list but in between you
increment the second list without increment the first list.
In best case it will be O(min(m,n))
example: {1,2,3} {1,2,3,4,}
in this case you will terminate
the loop after reaching end of the first list.

Insertion at the beginning in circular linked list

I have been recently working on circular linked list and the way the most of the people write code is as given below:
#include<stdio.h>
#include<stdlib.h>
/* structure for a node */
struct Node
{
int data;
struct Node *next;
};
/* Function to insert a node at the begining of a Circular
linked list */
void push(struct Node **head_ref, int data)
{
struct Node *ptr1 = (struct Node *)malloc(sizeof(struct Node));
struct Node *temp = *head_ref;
ptr1->data = data;
ptr1->next = *head_ref;
/* If linked list is not NULL then set the next of last node */
if (*head_ref != NULL)
{
while (temp->next != *head_ref)
temp = temp->next;
temp->next = ptr1;
}
else
ptr1->next = ptr1; /*For the first node */
*head_ref = ptr1;
}
/* Function to print nodes in a given Circular linked list */
void printList(struct Node *head)
{
struct Node *temp = head;
if (head != NULL)
{
do
{
printf("%d ", temp->data);
temp = temp->next;
}
while (temp != head);
}
}
/* Driver program to test above functions */
int main()
{
/* Initialize lists as empty */
struct Node *head = NULL;
/* Created linked list will be 11->2->56->12 */
push(&head, 12);
push(&head, 56);
push(&head, 2);
push(&head, 11);
printf("Contents of Circular Linked List\n ");
printList(head);
return 0;
}
However, there is one thing is never understand while inserting at the beginning of the circular linked list. If our last node always points to the first node which also is same as saying that the last node *next pointer has the same address as the the *first pointer has, then why for inserting the items after first node, we have to travel the whole list and update the *next pointer of the last node to point the newly added node at the beginning. Instead of while loop why can't we simply do like this:
Node *newadded
newadded->next = first->next
first = newadded
Because *first pointer has the address of the first node so if we update the *first pointer then the last pointer which already was pointing to the first pointer should also update itself.
Why to travel the whole list?
Since the list is circular, the last element of the list needs to point to the first element of the list. When inserting a new element into the beginning of the list, the first element of the list has changed to be a different element. To maintain circularity, you must find the last element and make it point to the new first element.
One way to make the operation more efficient is to maintain the tail of the circular linked list, rather than the head. Then insertion to the tail and to the head can both be done in constant time.
Demo

inserting a node into a linked list

Assume structure list and node are defined as
struct list {struct node *a;};
struct node { int value;
struct node *next;};
The following function inserts integer e into l as the first element
void insert_first(int e, struct list *l){
struct node * r = malloc(sizeof(struct node));
r->value = e;
r->next = l->a;
l->a = r;}
Example: original list "b": 1 2 3 4
after calling insert_first(3,*b)
list "b": 3 1 2 3 4
insert_first is pretty straightfoward; however, I am having a hard time trying to figure out how to write a function insert_last which inserts a number as the last element of the list.
Example: original list "b": 1 2 3 4
after calling insert_last(3,*b)
list "b": 1 2 3 4 3
Thanks for any help in advance.
One way of doing it is to iterate over the list until you find the tail. Something like this:
void insert_last(int e, struct list *l)
{
// "iter" Will iterate over the list.
struct node *iter = l->a;
struct node *new_node = malloc(sizeof(struct node));
// Advice: ALWAYS check, if malloc returned a pointer!
if(!new_node) exit(1); // Memory allocation failure.
new_node->value = e;
new_node->next = NULL;
if(iter){
// Loop until we found the tail.
// (The node with no next node)
while(iter->next) iter = iter->next;
// Assign the new tail.
iter->next = new_node;
}else{
// The list was empty, assign the new node to be the head of the list.
l->a = new_node;
}
}
EDIT: Something I saw in your code, that really tickles me: ALWAYS check, when using malloc, whether you actually got a pointer back or not (check if the pointer is NULL). If malloc failes to allocate memory, be it for a lack thereof or some other critical error, it will toss you a NULL pointer. If you do not check for that, you might end up running in to some very nasty, hard to detect bugs. Just a little reminder!
You need to save original HEAD node and traverse through list . Hope this code will help you.
struct node {
int value;
struct node *next;
};
struct list {struct node *a;};
struct node *insert_last(int e, struct list *l) {
/* Store the initial head of the list */
struct list *org_head = head;
struct node *r = malloc(sizeof(struct node));
r->value = e;
r->next = NULL /* Assign next pointer of current node to NULL */
/* If the head is initially NULL, then directly return the new created node (r) as the head of a linked list with only one node */
if(head == NULL)
{
return r;
}
/* While we do not reach the last node, move to the next node */
while(head -> next != NULL)
head = head -> next;
/* Assign the 'next' pointer of the current node to the "r" */
head->next = r;
/* return the original head that we have stored separately before */
return org_head;
}

Resources