Implementing mergesort on a linked list - c

I was tasked with implementing a merge sort algorithm on a list written in C/C++. I have the general idea down, wrote my code and have successfully compiled it. However, when I run it, it will begin fine but then hang on "prepared list, now starting sort" without giving any kind of error. I have tried to look through my code but I am at a complete loss as to what the issue could be. I am also pretty amateurish with debugging, so using gdb to the best of my abilities has lead me no where. Any advice or tips would be a tremendous help, thank you all!
#include <stdio.h>
#include <stdlib.h>
struct listnode
{
struct listnode *next;
int key;
};
//Finds length of listnode
int
findLength (struct listnode *a)
{
struct listnode *temp = a;
int i = 0;
while (temp != NULL)
{
i++;
temp = temp->next;
}
return i;
}
struct listnode *
sort (struct listnode *a)
{
// Scenario when listnode is NULL
if (findLength (a) <= 1)
return a;
//Find middle
int mid = findLength (a) / 2;
struct listnode *temp = a;
struct listnode *first = a;
struct listnode *second;
for (int i = 0; i < mid - 1; i++)
{
temp = a->next;
}
second = temp->next;
temp->next = NULL;
//Recursive calls to sort lists
first = sort (first);
second = sort (second);
if (first != NULL && second != NULL)
{
if (first->key < second->key)
{
a = first;
first = first->next;
}
else
{
a = second;
second = second->next;
}
}
struct listnode *head = a;
struct listnode *tail = a;
while (first != NULL && second != NULL)
{
if (first->key < second->key)
{
tail = first;
first = first->next;
tail = tail->next;
}
else
{
tail = second;
second = second->next;
tail = tail->next;
}
}
if (first == NULL)
{
while (second != NULL)
{
tail = second;
second = second->next;
tail = tail->next;
}
}
while (first != NULL)
{
tail = first;
first = first->next;
tail = tail->next;
}
return a;
}
Here is the test code provided, written in C:int
main (void)
{
long i;
struct listnode *node, *tmpnode, *space;
space = (struct listnode *) malloc (500000 * sizeof (struct listnode));
for (i = 0; i < 500000; i++)
{
(space + i)->key = 2 * ((17 * i) % 500000);
(space + i)->next = space + (i + 1);
}
(space + 499999)->next = NULL;
node = space;
printf ("\n prepared list, now starting sort\n");
node = sort (node);
printf ("\n checking sorted list\n");
for (i = 0; i < 500000; i++)
{
if (node == NULL)
{
printf ("List ended early\n");
exit (0);
}
if (node->key != 2 * i)
{
printf ("Node contains wrong value\n");
exit (0);
}
node = node->next;
}
printf ("Sort successful\n");
exit (0);
}

You're close, but with some silly errors. Check the append operations in the merge step. They're not doing what you think they are. And of course you meant temp = temp->next; in the splitting loop.
If gdb is overwhelming, adding printf's is a perfectly fine way to go about debugging code like this. Actually you want to write a list printing function and print the sublists at each level of recursion plus the results of the merge step. It's fun to watch. Just be neat and delete all that when you're done.
Here's code that works for reference:
struct listnode *sort(struct listnode *lst) {
if (!lst || !lst->next) return lst;
struct listnode *q = lst, *p = lst->next->next;
while (p && p->next) {
q = q->next;
p = p->next->next;
}
struct listnode *mid = q->next;
q->next = NULL;
struct listnode *fst = sort(lst), *snd = sort(mid);
struct listnode rtn[1], *tail = rtn;
while (fst && snd) {
if (fst->key < snd->key) {
tail->next = fst;
tail = fst;
fst = fst->next;
} else {
tail->next = snd;
tail = snd;
snd = snd->next;
}
}
while (fst) {
tail->next = fst;
tail = fst;
fst = fst->next;
}
while (snd) {
tail->next = snd;
tail = snd;
snd = snd->next;
}
tail->next = NULL;
return rtn->next;
}
On my old MacBook this sorts 10 million random integers in a bit over 4 seconds, which doesn't seem too bad.
You can also put the append operation in a macro and make this quite concise:
struct listnode *sort(struct listnode *lst) {
if (!lst || !lst->next) return lst;
struct listnode *q = lst, *p = lst->next->next;
while (p && p->next) {
q = q->next;
p = p->next->next;
}
struct listnode *mid = q->next;
q->next = NULL;
struct listnode *fst = sort(lst), *snd = sort(mid);
struct listnode rtn[1], *tail = rtn;
#define APPEND(X) do { tail->next = X; tail = X; X = X->next; } while (0)
while (fst && snd) if (fst->key < snd->key) APPEND(fst); else APPEND(snd);
while (fst) APPEND(fst);
while (snd) APPEND(snd);
tail->next = NULL;
return rtn->next;
}

Does it have to be a top down merge sort? To get you started, here's a partial fix, didn't check for other stuff. The | if (first != NULL && second != NULL) | check isn't needed since the prior check for length <= 1 takes care of this, but it won't cause a problem.
while (first != NULL && second != NULL)
{
if (first->key < second->key)
{
tail->next = first;
tail = first;
first = first->next;
}
else
{
tail->next = second;
tail = second;
second = second->next;
}
}
if (first == NULL)
{
tail->next = second;
}
else
{
tail->next = first;
}
}

Related

variable "tmp" used in loop condition not modified in loop body

#include <stdio.h>
#include <stdlib.h>
typedef struct node{
int number;
struct node* next;
}
node;
int main(void){
node* list = NULL;
node *n = malloc(sizeof(node));
if(n==NULL){
return 1;
}
n->number = 2;
n-> next = NULL;
list = n;
n = malloc(sizeof(node));
if(n == NULL){
free(list);
return 1;
}
n->number = 3;
n->next = NULL;
list->next = n;
n = malloc(sizeof(node));
if(n == NULL){
free(list->next);
free(list);
}
n->number = 4;
n->next = NULL;
list->next->next =n;
n = malloc(sizeof(node));
if(n!=NULL){
n->number = 0;
n->next = NULL;
n->next = list;
list = n;
}
for( node* tmp = list; tmp != NULL; tmp->next){
printf("%i\n" , tmp->number);
}
while(list!=NULL){
node*tmp = list->next;
free(list);
list=tmp;
}
}
was trying linked list.
expected when running the code:
0
1
2
3
4
$
//asdoihasidashiofdhiohdfgdiwheifiopioioiophfaifjasklfhafiashfauiosfhwuiohwefuiowhfaslfidasdaskdasjdlaksdjqwfiqpweiojfkldfjsdfklwhefiowefweopfiosfkosid;fjwdfp;fdasiopfjew[0fowejfwepfojmofejmiwrfgj;wdfjewio;fijwefjsdp;jfkl;wjw
Actually you are not changing the pointer
for( node* tmp = list; tmp != NULL; tmp->next){
You need to write
for( node* tmp = list; tmp != NULL; tmp = tmp->next){
It will be even better to write
for ( const node* tmp = list; tmp != NULL; tmp = tmp->next ){
because within the loop the list is not being changed.
Also in this code snippet
if(n!=NULL){
n->number = 0;
n->next = NULL;
n->next = list;
list = n;
}
the statement
n->next = NULL;
is redundant.
In this loop, tmp->next has no effect because you don't assign it to anything.
for (node* tmp = list; tmp != NULL; tmp->next) {
printf("%i\n", tmp->number);
}
You must do tmp = tmp->next:
for (node* tmp = list; tmp != NULL; tmp = tmp->next) {
// ^^^^^
printf("%i\n", tmp->number);
}
Also, you can't expect 1 to be in the output because you never create a node with that number. With the above change the program will therefore output:
0
2
3
4
Sidenote: Your program is full of repetition which makes it hard to find errors and it's also what makes it harder to see that you forgot to add the node with the number 1. If you instead make functions of the things that you do repeatedly it'll be much clearer. You can also make functions with descriptive names to make the whole program easier to read and maintain.
If your program was rewritten to make use of functions, it could look like below - and then it'll be very obvious that the node with the number 1 is missing.
#include <stdio.h>
#include <stdlib.h>
typedef struct node node;
struct node {
int number;
node* next;
};
node *create_node(int number, node *next) {
node *nn = malloc(sizeof *nn);
if(!nn) exit(1);
// assign values to the new node:
*nn = (node){.number = number,
.next = next};
return nn;
}
void insert_first(node **list, int number) {
*list = create_node(number, *list);
}
void insert_last(node **list, int number) {
// find the last "next" pointer (the one pointing at NULL):
while(*list) list = &(*list)->next;
// make the pointer pointing at NULL now point at the new node:
*list = create_node(number, NULL);
}
void free_all(node **list) {
for(node *tmp; *list; *list = tmp) {
tmp = (*list)->next;
free(*list);
}
}
void print_all(const node *list) {
for (; list; list = list->next) {
printf("%d\n", list->number);
}
}
int main(void) {
node* list = NULL;
insert_last(&list, 2);
insert_last(&list, 3);
insert_last(&list, 4);
insert_first(&list, 0);
print_all(list);
free_all(&list); // list == NULL after this
}

Circular linked list crashes when displayed

I'm trying to make a circular linked list. When I try to display the list after creating it, the program keeps on crashing. Here is my code:
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
int data;
struct node * next;
} node;
node * createList(int);
void display(node * head);
int main() {
struct node * head;
head = createList(5);
display(head);
}
node * createList(int n) {
int i = 0,data = 0;
struct node * head = NULL;
struct node * temp = NULL;
struct node * p = NULL;
for (i = 0; i < n; i++) {
temp = (node*)malloc(sizeof(node));
temp->data = data++;
temp->next = head;
if (head == NULL) {
head = temp;
} else {
p = head;
while (p->next != NULL) {
p = p->next;
}
p->next = temp;
}
}
return head;
}
void display(node * head) {
struct node * temp = head->next;
while (temp != head) {
printf("%d-> \t",temp->data);
temp = temp->next;
}
printf("\n");
}
What am I doing wrong?
You have set every temp's next to head in temp->next = head; but did it too early (the first is just NULL). Then you tested p->next against NULL in while (p->next != NULL) { but you should have tested against head. Alternatively, you can continue to test against NULL but then you need to initialize temp->next to NULL and assign head to temp->next only after the for loop.
Your display code started from the second link.
Here is a fixed code using the first option in 1. above:
for (i = 0; i < n; i++) {
temp = (node*)malloc(sizeof(node));
temp->data = data++;
if (head == NULL) {
head = temp;
} else {
p = head;
while (p->next != head) {
p = p->next;
}
p->next = temp;
}
temp->next = head;
}
Here is a fixed code using the alternative option in 1. above. You still need to initialize temp->next to NULL since malloc() does not initialize.
for (i = 0; i < n; i++) {
temp = (node*)malloc(sizeof(node));
temp->data = data++;
temp->next = NULL;
if (head == NULL) {
head = temp;
} else {
p = head;
while (p->next != NULL) {
p = p->next;
}
p->next = temp;
}
}
if (temp != NULL) {
temp->next = head;
}
But in reality, there is no need to "walk" from the head on every creation. You can simply keep the previous and link it to the next:
for (i = 0; i < n; i++) {
temp = (node*)malloc(sizeof(node));
temp->data = data++;
if (head == NULL) {
head = p = temp;
} else {
p = p->next = temp;
}
}
if (temp != NULL) {
temp->next = head;
}
Here is a fix for the display():
void display(node * head) {
struct node * temp = head;
if (temp != NULL) {
do {
printf("%d-> \t",temp->data);
temp = temp->next;
} while (temp != head);
}
printf("\n");
}
The problem is on the first node you initialize:
struct node *head = NULL;
...
for (i = 0; i < n; i++) {
...
temp->next = head;
So tmp->next == NULL on the first iteration leaving head->next == NULL. That will not work for a circular list. When you attempt to insert the 2nd node:
p = head;
while (p->next != NULL) {
What was head->next again?? (oh, NULL) Dereferencing a NULL pointer (BOOM Segfault!)
Do your circular list correctly. On insertion of the first node set:
if (head == NULL) {
head = temp;
head->next = temp; /* you must set head->next to temp */
} ...
So on the insertion of the remaining nodes you simply need:
} else {
p = head;
while (p->next != head) { /* iterate to last node */
p = p->next;
}
p->next = temp; /* now set p->next = temp */
}
Now, you handle your display() function the same way, e.g.
void display (node *head)
{
if (!head) { /* validate list not empty */
puts ("(list-empty)");
return;
}
struct node *temp = head;
do { /* same loop problem fixed in display() */
printf ("%d-> \t", temp->data);
temp = temp->next;
} while (temp != head);
putchar ('\n');
}
If you make the changes, then you can test your list with:
int main (void) {
struct node *head, *tmp;
head = createList(5);
display (head);
puts ("\niterate from mid-list");
tmp = head;
tmp = tmp->next;
tmp = tmp->next;
display (tmp);
}
Example Use/Output
$ ./bin/lls_circular_fix
0-> 1-> 2-> 3-> 4->
iterate from mid-list
2-> 3-> 4-> 0-> 1->
Lastly, you are not multiplying the type node by head in struct node * head = NULL; Write it as struct node *head = NULL; (the same for all your function declarations as well) Much more readable.
When you go to delete a note from the list, you must create a special case for both head and tail (the last node). In this sense, the singly-linked list takes a bit more effort than a doubly-linked list due to not having a prev node pointer to track the prior node.
Look things over and let me know if you have questions.
A full example would be:
#include <stdio.h>
#include <stdlib.h>
typedef struct node {
int data;
struct node *next;
} node;
node *createList (int);
void display (node *head);
int main (void) {
struct node *head, *tmp;
head = createList(5);
display (head);
puts ("\niterate from mid-list");
tmp = head;
tmp = tmp->next;
tmp = tmp->next;
display (tmp);
}
node *createList (int n)
{
int i = 0,data = 0;
struct node *head = NULL;
struct node *temp = NULL;
struct node *p = NULL;
for (i = 0; i < n; i++) {
if (!(temp = malloc (sizeof *temp))) {
perror ("malloc-temp");
return NULL;
}
temp->data = data++;
temp->next = head; /* head is NULL on 1st node insertion */
if (head == NULL) {
head = temp;
head->next = temp; /* you must set head->next to temp */
} else {
p = head;
while (p->next != head) { /* iterate to last node */
p = p->next;
}
p->next = temp; /* now set p->next = temp */
}
}
return head;
}
void display (node *head)
{
if (!head) { /* validate list not empty */
puts ("(list-empty)");
return;
}
struct node *temp = head;
do { /* same loop problem fixed in display() */
printf ("%d-> \t", temp->data);
temp = temp->next;
} while (temp != head);
putchar ('\n');
}

Simplest Linked List Creation and Printing Data

The following is a simple code segment in C to create a linked list and print all elements contained in the list.
User is asked to input integer data till a zero is entered which marks the termination of user input; Once data is saved in the linked list, the program prints all elements stored in the list and then completes its execution.
I can't make it run, every time it gives a "Segmentation fault" error, please check and tell me where I'm wrong (using gcc 4.8.2)
Code :
struct node
{
int data;
struct node * next;
};
struct node * createLinkedList()
{
int x;
struct node * start;
start = NULL;
printf("Input 0 to end, Insert elements :\n");
for(scanf("%d", &x); x ;scanf("%d", &x))
{
struct node * temp = (struct node *) malloc(sizeof(struct node));
if (temp)
{
temp->data = x;
temp->next = NULL;
if(start == NULL) {
start = temp;
} else {
start->next = temp;
start = temp;
}
}
}
return start;
}
void printLinkedList(struct node * start)
{
if (start == NULL) {
printf("Linked List is empty!\n");
} else {
printf("\nPrinting Linked List : \n");
struct node * s;
s = start;
while(s != NULL)
{
printf("%d\n", s->data);
s = s->next;
}
}
}
int main(int argc, char const *argv[])
{
struct node * start;
start = NULL;
start = createLinkedList();
printLinkedList(start);
return 0;
}
This part of code
if(start == NULL) {
start = temp;
} else {
start->next = temp;
start = temp;
}
is invalid. There has to be
if(start == NULL) {
start = temp;
} else {
temp->next = start;
start = temp;
}
Also you need to have a function that deletes all nodes of the list.

how to do selection sort for double linked list in c

I am facing problem in implementation of selection sort for double linked list.
Please some one help me in doing selection sort for double linked list in c.
node struct is:
struct node {
char name [15];
node * nextnode;
node * prevnode;
}node;
I am sorting by name
Code is like this:
I need to sort the list in ascending order of names.
char names[5][15] = {"tom","jack","mike","bernard","leo"};
I have constructed a doubly linked list with above node structure and function below:
int list_add(char *lname)
{
int i, length;
struct node *current = NULL;
struct node *temp = NULL;
current = head;
while (current->nextnode != NULL)
{
current = current->nextnode;
}
temp = malloc(sizeof(struct node));
current->nextnode = temp;
strcpy(temp->name,lname);
temp->prevnode = current;
temp->nextnode = NULL;
return 0;
}
int init_list(char names[][15])
{
int length;
struct node * current = NULL;
head = malloc(sizeof(struct node));
strcpy(head->name,"Head");
head->nextnode = head->prevnode = NULL;
for(length = 0; length < 5; length++)
{
//printf("names are %s \n", (char *)names[length]);
list_add(names[length]);
}
return length;
}
the above one is simple doubly linked list add logic.
selection sort code flow as follows:
start node = keeps track of element node from which shortest node needs to be searched.
temp1 is assigned start node - reason being to keep start nodes traversal not disturbed by swap
temp node holds shortest node for that traversal.
prev and next node's are used for swap logic.
with the below code I am not getting the sorted list.
int selsort(int length)
{
int i = 0,j = 0;
struct node *start = NULL;
struct node *prev, *next, *temp, *temp1;
struct node *current = NULL;
struct node *traversal = NULL;
prev = next = NULL;
if (head == NULL)
return 0;
start = head;
printf("selsort called \n");
for(/*start = head->nextnode*/; start != NULL && i < 6; /*start = start->nextnode*/)
{
printf("Iteration number %d \n",i);
start = head->nextnode;
j = i;
while(j)
{
start = start->nextnode;
j--;
}
printf("start node %s \n",start->name);
temp1 = start;
current = start;
temp = start;
while(current != NULL)
{
if(strcmp(temp->name, current->name) > 0)
{
//ascending logic
temp = current;
current = current->nextnode;
}
else
current = current->nextnode;
}
//printf("Before swap logic \n");
//swap logic
if(temp1->prevnode == NULL)
{
//swap current with temp
next = temp1->nextnode;
temp1->nextnode = temp->nextnode;
temp1->prevnode = temp->prevnode;
temp->nextnode->prevnode = temp1;
temp->prevnode->nextnode = temp1;
temp->nextnode = next;
temp->prevnode = NULL;
next->prevnode = temp;
}else
{
//printf("second swap : %d \n",i);
if(temp1->nextnode == NULL)
continue;
else
{
next = temp1->nextnode;
prev = temp1->prevnode;
if (temp->nextnode == NULL)
{
temp1->nextnode = NULL;
temp1->prevnode = temp->prevnode;
temp->prevnode->nextnode = temp1;
}else{
temp1->nextnode = temp->nextnode;
temp1->prevnode = temp->prevnode;
temp->nextnode->prevnode = temp1;
temp->prevnode->nextnode = temp1;
}
temp->prevnode = prev;
temp->nextnode = next;
next->prevnode = temp;
prev->nextnode = temp;
}
}
i++;
traversal = head;
while(traversal != NULL)
{
printf("after iter %d Names are %s \n",i, traversal->name);
traversal = traversal->nextnode;
}
}
}
The above implementation could not sort the doubly linked list.
I am getting infinite loop out put of:
after iter 4 Names are mike
after iter 4 Names are mike
after iter 4 Names are mike
after iter 4 Names are mike
Please somebody give a fix for my implementation or advice for new implementation(reference link).
Thanks in advance.

How to combine in one loop serial numbers and null?

I'm busy with implementation of singly linked list and have 2 functions: insert_back and insert_after.
Here is the listing of them:
void insert_back(int data)
{
node *temp1;
temp1 = (node*)malloc(sizeof(node));
temp1 = head;
while (temp1->next != NULL) {
temp1 = temp1->next;
}
node *temp;
temp = (node*)malloc(sizeof(node));
temp->data = data;
temp->next = NULL;
temp1->next = temp;
}
void insert_after(int pos, int data)
{
node *temp1;
temp1 = (node*)malloc(sizeof(node));
temp1 = head;
for (int i = 1; i < pos; i++) {
temp1 = temp1->next;
if (temp1 == NULL) {
return;
}
}
node *temp;
temp = (node*)malloc(sizeof(node));
temp->data = data;
temp->next = temp1->next;
temp1->next = temp;
}
As you can see they are almost the same and for insert back I want to write insert_after(null, 10). I can solve it by adding if condition and choose one of the loops, but it's not my aim.
Is it possible somehow to use one while or for loops together for serial numbers and null?
Also I see that param int pos is int. Should I use 0 instead of null?
You unnecessarily allocate memory in the following lines.
temp1 = (node*)malloc(sizeof(node));
temp1 = head;
This allocated memory will leak as you overwrite the returned address in temp1. You just need temp1 to walk over the list, so there is also no need to allocate any node itself. temp1 can point to any node.
I've taken the liberty to kind of from scratch write a routine doing both things in one go. If pos < 0 it will add the element to the end of the list, otherwise it will add it after the pos-th element, where the first element corresponds with pos == 1. If pos == 0 the element is added at the start of the list.
Also a small main is added to test the routine. new_node has been added to test if memory is not exhausted.
#include <stdlib.h>
#include <stdio.h>
typedef struct node
{
struct node * next;
int data;
} node;
node * head = NULL;
node * new_node(void)
{
node * result = malloc(sizeof(*result));
if (result == NULL)
{
fprintf(stderr, "Out of memory.\n");
exit(10);
}
return result;
}
void insert_after(int pos, int data)
{
node *walk, * prev;
int i;
prev = NULL;
walk = head;
for (i = 0; walk != NULL && i != pos; i++)
{
prev = walk;
walk = walk->next;
}
if (i != pos && pos > 0)
{
fprintf(stderr, "Location not found.\n");
exit(9);
}
else
{
walk = new_node();
walk->data = data;
if (prev == NULL)
{
walk->next = head;
head = walk;
}
else
{
walk->next = prev->next;
prev->next = walk;
}
}
}
int main(void)
{
int i;
node * wlk;
for (i = 0; i < 10; i++)
{
insert_after(-1, i);
}
for (i = 0; i < 10; i++)
{
insert_after(3, i+10);
}
for (wlk = head; wlk != NULL; wlk = wlk->next)
{
printf("%d\n", wlk->data);
}
return 0;
}
Since you are testing for the end of the chain with insert_after(pos,...) anyway, you could go for:
void insert_after(int pos, int data)
{
node *temp1= head;
for (int i=1; i<pos; i++) {
if (temp1->next==NULL) {
if (pos==INT_MAX)
break; // pos of INT_MAX means insert at end
// so we continue with this last item and append
else
return; // pos higher than length of chain
}
temp1 = temp1->next;
}
...
}
Or slightly more compact:
void insert_after(int pos, int data)
{
node *temp1= head;
for (int i=1; i<pos && temp1->next!=NULL; i++) {
temp1 = temp1->next;
}
if (temp1->next==NULL && pos!=INT_MAX)
return; // pos higher than length of chain, except for
// INT_MAX (for that we just want to continue)
...
}
Then you could use
void insert_back(int data)
{
insert_after(INT_MAX, data);
}

Resources