i have code for mergesort using linked list,it works fine,my question what is complexity of this algorithm?is it O(nlog(n))?also is it stable?i am interested because as i
You've got a typo in your code. With it corrected, it is indeed stable, and of O(n log n)
complexity. Although to be sure, you really should reimplement your merge
as a loop instead of recursion. C doesn't have tail call optimization (right?), so this can mess things up there:
struct node *mergesort(struct node *head){
struct node *head_one;
struct node *head_two;
if((head==NULL) ||(head->next==NULL))
return head;
head_one=head;
head_two=head->next;
while( (head_two!=NULL) &&(head_two->next!=NULL)){
head=head->next;
// head_two=head->next->next; // -- the typo, corrected:
head_two=head_two->next->next;
}
head_two=head->next;
head->next=NULL;
return merge(mergesort(head_one),mergesort(head_two));
}
And while we're at it, change your workflow from
return merge(mergesort(head_one),mergesort(head_two));
to
struct node *p1, *p2;
// ......
p1 = mergesort(head_one);
p2 = mergesort(head_two);
return merge(p1,p2);
it'll be much easier on the stack this way (will use much less of it).
In general, this here is what's known as top-down mergesort. You could also do it in a bottom-up fashion, by initially sorting the consecutive chunks of two elements each, then merging them into (thus, now, sorted) chunks of 4 elements, then merging those pairwise, into chunks of 8 elements, etc., until only one chunk is left - the sorted list.
To get extra fancy (and efficient), instead of starting with the 2-chunks, start by splitting the list into monotonic runs, i.e. increasing sequences, and decreasing sequences - re-linking the latter ones in reverse as you go - thus segmenting the original list according to its innate order, so it's likely there will be fewer initial chunks to merge; then proceed merging those pairwise repeatedly, as before, until only one is left in the end.
Mergesort means split&merge. The splitting in the fragment below is not perfect (it leads to quadratic behavior on equally distributed runlengths, see the comment from Christoph)), but it will do the trick:
#include <stdio.h>
#include <string.h>
struct llist {
struct llist *next;
char *payload;
};
int llist_cmp(struct llist *l, struct llist *r);
struct llist * llist_split(struct llist **hnd
, int (*cmp)(struct llist *l, struct llist *r) );
struct llist * llist_merge(struct llist *one, struct llist *two
, int (*cmp)(struct llist *l, struct llist *r) );
struct llist * llist_sort(struct llist *ptr
, int (*cmp)(struct llist *l, struct llist *r) );
struct llist * llist_split(struct llist **hnd, int (*cmp)(struct llist *l, struct llist *r) )
{
struct llist *this, *save, **tail;
for (save=NULL, tail = &save; this = *hnd; ) {
if (! this->next) break;
if ( cmp( this, this->next) <= 0) { hnd = &this->next; continue; }
*tail = this->next;
this->next = this->next->next;
tail = &(*tail)->next;
*tail = NULL;
}
return save;
}
struct llist * llist_merge(struct llist *one, struct llist *two, int (*cmp)(struct llist *l, struct llist *r) )
{
struct llist *result, **tail;
for (result=NULL, tail = &result; one && two; tail = &(*tail)->next ) {
if (cmp(one,two) <=0) { *tail = one; one=one->next; }
else { *tail = two; two=two->next; }
}
*tail = one ? one: two;
return result;
}
struct llist * llist_sort(struct llist *ptr, int (*cmp)(struct llist *l, struct llist *r) )
{
struct llist *save;
save=llist_split(&ptr, cmp);
if (!save) return ptr;
save = llist_sort(save, cmp);
return llist_merge(ptr, save, cmp);
}
int llist_cmp(struct llist *l, struct llist *r)
{
if (!l) return 1;
if (!r) return -1;
return strcmp(l->payload,r->payload);
}
struct llist lists[] =
{{ lists+1, "one" }
,{ lists+2, "two" }
,{ lists+3, "three" }
,{ lists+4, "four" }
,{ lists+5, "five" }
,{ lists+6, "six" }
,{ lists+7, "seven" }
,{ lists+8, "eight" }
,{ lists+9, "nine" }
,{ NULL, "ten" }
};
int main()
{
struct llist *root,*tmp;
root = lists;
fprintf(stdout, "## %s\n", "initial:" );
for (tmp=root; tmp; tmp=tmp->next) {
fprintf(stdout, "%s\n", tmp->payload);
}
fprintf(stdout, "## %s\n", "sorting..." );
root = llist_sort(root, llist_cmp);
for (tmp=root; tmp; tmp=tmp->next) {
fprintf(stdout, "%s\n", tmp->payload);
}
fprintf(stdout, "## %s\n", "done." );
return 0;
}
How not to implement mergesort for linked lists
1
and n - 1
, as explained by ruakhHow to implement mergesort for linked lists
Instead of using bisection, build the lists up by maintaining a stack of already sorted sublists. That is, start by pushing lists of size 1
to the stack and merge down until you reach a list of greater size; you don't actually need to store the list sizes if you can figure out the math behind that.
The sorting algorithm will be stable iff the merge function is. A stable version would build the merged list from scratch by always taking a single element from the lists, and using the first list in case of equality. An unstable, but better performing version would add to the merged list in chunks, avoiding unnecessary re-linking after each element.