Merge sort is often preferred for sorting a linked list. The slow random-access
performance of a linked list makes some other algorithms (such as quicksort)
perform poorly, and others (such as heapsort) completely impossible.
MergeSort(headRef)
1) If head is NULL or there is only one element in the Linked List
then return.
2) Else divide the linked list into two halves.
FrontBackSplit(head, &a, &b); /* a and b are two halves */
3) Sort the two halves a and b.
MergeSort(a);
MergeSort(b);
4) Merge the sorted a and b (using SortedMerge() discussed here)
and update the head pointer using headRef.
*headRef = SortedMerge(a, b);
Idea: 1) No random access 2) recursive
/* sorts the linked list by changing next pointers (not data) */ void MergeSort(struct node** headRef) { struct node* head = *headRef; struct node* a, b; /* Base case -- length 0 or 1 */ if ((head == NULL) || (head->next == NULL)){ return; } /* Split head into ’a’ and ’b’ sublists */ FrontBackSplit(head, &a, &b); /* Recursively sort the sublists */ MergeSort(&a); MergeSort(&b); /* answer = merge the two sorted lists together */ *headRef = SortedMerge(a, b); }
/* See http://geeksforgeeks.org/?p=3622 for details of this function */ struct node* SortedMerge(struct node* a, struct node* b) { struct node* result = NULL; /* Base cases */ if (a == NULL) return(b); else if (b==NULL) return(a); /* Pick either a or b, and recur */ if (a->data <= b->data){ result = a; result->next = SortedMerge(a->next, b); } else{ result = b; result->next = SortedMerge(a, b->next); } return(result); }
/* UTILITY FUNCTIONS */ /* Split the nodes of the given list into front and back halves, and return the two lists using the reference parameters. If the length is odd, the extra node should go in the front list. Uses the fast/slow pointer strategy. */ void FrontBackSplit(struct node* source, struct node** frontRef, struct node** backRef) { struct node* fast; struct node* slow; if (source==NULL || source->next==NULL) { /* length < 2 cases */ *frontRef = source; *backRef = NULL; } else { slow = source; fast = source->next; /* Advance ’fast’ two nodes, and advance ’slow’ one node */ while (fast != NULL) { fast = fast->next; if (fast != NULL) { slow = slow->next; fast = fast->next; } } /* ’slow’ is before the midpoint in the list, so split it in two at that point. */ *frontRef = source; *backRef = slow->next; slow->next = NULL; } }