mergesort与链表的复杂性

时间:2012-03-08 20:31:12

标签: c linked-list mergesort

我有使用链表的mergesort代码,它工作正常,我的问题是什么是这个算法的复杂性?是O(nlog(n))?它也稳定吗?我感兴趣因为我知道mergesort是稳定的,使用链表怎么样?如果我们的元素与其他元素相等,这段代码是否保留了元素的顺序?非常感谢

#include<stdio.h>
#include <stdlib.h>
struct node
{
    int number;
    struct node *next;

};
struct node *addnode(int number,struct node *next);
struct node*mergesort(struct node *head);
struct node *merge(struct node *one,struct node *two);

int main(void){
    struct node *head;
    struct node *current;
    struct node *next;
    int test[]={8,3,1,4,2,5,7,0,11,14,6};
    int n=sizeof(test)/sizeof(test[0]);
    int i;
    head=NULL;
     for (i=0;i<n;i++)
         head=addnode(test[i],head);
     i=0;
     head=mergesort(head);
    printf("before----after sort \n");
    for (current=head;current!=NULL;current=current->next)
        printf("%4d\t%4d\n",test[i++],current->number);

    /* free list */
    for (current=head;current!=NULL;current=current->next)
        next=current->next;free(current);
return 0;
}

struct node *addnode(int number,struct node* next){
    struct node *tnode;
    tnode=(struct node*)malloc(sizeof(*tnode));
    if(tnode!=NULL){
        tnode->number=number;
        tnode->next=next;
            }

     return tnode;
     }
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;
        }
    head_two=head->next;
    head->next=NULL;
    return merge(mergesort(head_one),mergesort(head_two));
    }
struct node *merge(struct node*head_one,struct node*head_two){

    struct node *head_three;
    if(head_one==NULL)
         return head_two;
    if(head_two==NULL)
         return head_one;
    if(head_one->number<head_two->number){

head_three=head_one;
head_three->next=merge(head_one->next,head_two);
    }
    else
    {

        head_three=head_two;
        head_three->next=merge(head_one,head_two->next);


    }

    return head_three;
    }

3 个答案:

答案 0 :(得分:4)

你的代码中有一个拼写错误。经过纠正后,它确实稳定,复杂度O(n log n)。虽然可以肯定,但您确实应重新实现您的merge 循环而不是递归。 C没有尾调用优化(对吧?),所以这可能会搞乱那里:

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));
    }

虽然我们正在使用

更改您的工作流程
    return merge(mergesort(head_one),mergesort(head_two));

    struct node *p1, *p2; 
    // ......
    p1 = mergesort(head_one);
    p2 = mergesort(head_two);
    return merge(p1,p2);

这种方式在堆栈上会更容易(将使用更少的东西)。

一般来说,这就是所谓的自上而下 mergesort。你也可以用自下而上的方式来做,最初排序两个元素的连续块,然后将它们合并到4个元素的块中(因此,现在,排序),然后合并那些成对,分成8个元素等的块,直到只剩下一个块 - 排序列表。

要获得额外的花哨(和有效),而不是从2块开始,首先将列表拆分为单调运行,即增加序列,减少序列 - 重新连接后者当你去的时候反过来 - 因此根据它的固有顺序对原始列表进行分段,因此合并的初始块可能会更少;然后像以前一样重复合并那些成对,直到最后只留下一个。

答案 1 :(得分:0)

如何为链表实施合并广场

  • 不要递归地将列表一分为二 - 随机访问不是免费的
  • 不要分为大小为1n - 1的子列表,如explained by ruakh

如何为链接列表实施mergesort

不是使用二分法,而是通过维护一堆已经排序的子列表来构建列表。也就是说,首先将大小为1的列表推送到堆栈并合并,直到达到更大的列表;如果你能算出背后的数学,你实际上并不需要存储列表大小。

如果合并函数是,则排序算法将是稳定的。稳定版本将从头开始构建合并列表,始终从列表中获取单个元素,并在相等的情况下使用第一个列表。一个不稳定但性能更好的版本会以块的形式添加到合并列表中,从而避免在每个元素之后进行不必要的重新链接。

答案 2 :(得分:0)

Mergesort意味着拆分和合并。下面片段中的分裂并不完美(它导致在均匀分布的游程上的二次行为,请参阅Christoph的评论)),但它可以解决这个问题:

#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;
}