C 调试合并排序

C 调试合并排序,c,debugging,mergesort,C,Debugging,Mergesort,我需要对双链接列表进行排序。根据万能的维基百科,合并排序是实现这一目标的方法 递归算法工作得相当好,但在我编写通用实现时,性能可能是一个问题 为数组移植迭代版本会降低性能,因为重新扫描列表以将其划分为子列表的速度很慢;对于任何感兴趣的人-以下是代码: static void sort(struct linked_list *list, int (*cmp)(const void *, const void *)) { size_t slice_size = 1; for(

我需要对双链接列表进行排序。根据万能的维基百科,合并排序是实现这一目标的方法

递归算法工作得相当好,但在我编写通用实现时,性能可能是一个问题

为数组移植迭代版本会降低性能,因为重新扫描列表以将其划分为子列表的速度很慢;对于任何感兴趣的人-以下是代码:

static void sort(struct linked_list *list,
    int (*cmp)(const void *, const void *))
{
    size_t slice_size = 1;
    for(; slice_size < list->size; slice_size *= 2)
    {
        struct node *tail = list->first;
        while(tail)
        {
            struct node *head = tail;

            size_t count = slice_size;
            while(tail && count--) // performance killer
                tail = tail->next;

            count = slice_size;
            while(head != tail && tail && count)
            {
                if(cmp(head->data, tail->data) <= 0)
                    head = head->next;
                else
                {
                    struct node *node = tail;
                    tail = tail->next;
                    remove_node(node, list);
                    insert_before(node, list, head);
                    --count;
                }
            }

            while(tail && count--) // performance killer
                tail = tail->next;
        }
    }
}
链表的实施也可能相关:

struct node
{
    struct node *prev;
    struct node *next;
    long long data[]; // use `long long` for alignment
};

struct linked_list
{
    struct _list _list; // ignore
    size_t size;
    struct node *first;
    struct node *last;
};

static void insert_before(struct node *node, struct linked_list *list,
    struct node *ref_node)
{
    if(ref_node)
    {
        node->next = ref_node;
        node->prev = ref_node->prev;
        if(ref_node->prev) ref_node->prev->next = node;
        else list->first = node;
        ref_node->prev = node;
    }
    else // empty list
    {
        node->next = NULL;
        node->prev = NULL;
        list->first = node;
        list->last = node;
    }
    ++list->size;
}

static void remove_node(struct node *node, struct linked_list *list)
{
    if(node->prev) node->prev->next = node->next;
    else list->first = node->next;
    if(node->next) node->next->prev = node->prev;
    else list->last = node->prev;
    --list->size;
}
我在这里遗漏了什么?

基于堆栈的方法

/* ... */
    struct slice stack[32];
    size_t top = -1;
    struct node *current = list->first;

    for(; current; current = current->next)
    {
        stack[++top] = (struct slice){ current, 1 };
        for(; top && stack[top-1].size <= stack[top].size; --top)
        /*    ^^^    */
            merge_down(list, cmp, stack + top);
    }
/* ... */
/**/
结构片堆栈[32];
大小\u t top=-1;
结构节点*当前=列表->第一;
对于(;当前;当前=当前->下一步)
{
堆栈[++top]=(结构片){current,1};
对于(;top&&stack[top-1]。大小基于堆栈的方法

/* ... */
    struct slice stack[32];
    size_t top = -1;
    struct node *current = list->first;

    for(; current; current = current->next)
    {
        stack[++top] = (struct slice){ current, 1 };
        for(; top && stack[top-1].size <= stack[top].size; --top)
        /*    ^^^    */
            merge_down(list, cmp, stack + top);
    }
/* ... */
/**/
结构片堆栈[32];
大小\u t top=-1;
结构节点*当前=列表->第一;
对于(;当前;当前=当前->下一步)
{
堆栈[++top]=(结构片){current,1};

对于(;top&&stack[top-1]。大小是否需要将节点复制到列表的末尾?
那么,
insert\u before()
调用是什么

insert_before(node, list, NULL);

这会把
list->first
node->prev
搞得一团糟。您是否需要将节点复制到列表的末尾?
那么,
insert\u before()
调用是什么

insert_before(node, list, NULL);

这会把
list->first
node->prev

搞得一团糟,我现在已经运行了你的代码,并在注释掉下面指示的行后让它开始工作

static void merge_down(struct linked_list *list,
    int (*cmp)(const void *, const void *), struct slice *top)
{
    struct node *right = top->head;
    size_t count = top->size;

    --top;

    struct node *left = top->head;
    top->size += count; /* possible bug? */
 /* ^^^^^^^^^^^^^^^^^^^ */

这也适用于您吗?

我现在已经运行了您的代码,并在注释掉下面指示的行后使其正常工作

static void merge_down(struct linked_list *list,
    int (*cmp)(const void *, const void *), struct slice *top)
{
    struct node *right = top->head;
    size_t count = top->size;

    --top;

    struct node *left = top->head;
    top->size += count; /* possible bug? */
 /* ^^^^^^^^^^^^^^^^^^^ */

这也适用于您吗?

正如Kris所要求的,这里是递归版本(使用其他示例中的merge函数的标准mergesort):

静态结构节点*合并(结构链接列表*列表,
int(*cmp)(常数无效*,常数无效*),
结构节点*左,结构节点*右,大小\u t右\u计数)
{
结构节点*头部;
如果(cmp(左->数据,右->数据)下一步;
}
其他的
{
头部=右侧;
结构节点*节点=右侧;
右=右->下一步;
删除_节点(节点,列表);
在(节点,列表,左侧)之前插入_;
--右u计数;
}
while(左!=右和右计数)
{
如果(cmp(左->数据,右->数据)下一步;
其他的
{
结构节点*节点=右侧;
右=右->下一步;
删除_节点(节点,列表);
在(节点,列表,左侧)之前插入_;
--右u计数;
}
}
回流头;
}
静态结构节点*合并排序(结构链接列表*列表,
int(*cmp)(常量无效*,常量无效*),结构节点*头,大小
{
如果(尺寸<2)返回头;
大小\ t左\计数=大小/2;
大小\u t右\u计数=大小-左\u计数;
结构节点*尾部=头部;
大小计数=左计数;
而(计数--)tail=tail->next;
返回合并(列表、cmp、,
合并排序(列表、cmp、头部、左侧计数),
合并排序(列表、cmp、尾部、右计数),
右(右);;
}
静态无效排序(结构链接列表*列表,
int(*cmp)(常数无效*,常数无效*)
{
合并排序(列表、cmp、列表->第一,列表->大小);
}

正如Kris所要求的,这里是递归版本(使用其他示例中的merge函数的标准mergesort):

静态结构节点*合并(结构链接列表*列表,
int(*cmp)(常数无效*,常数无效*),
结构节点*左,结构节点*右,大小\u t右\u计数)
{
结构节点*头部;
如果(cmp(左->数据,右->数据)下一步;
}
其他的
{
头部=右侧;
结构节点*节点=右侧;
右=右->下一步;
删除_节点(节点,列表);
在(节点,列表,左侧)之前插入_;
--右u计数;
}
while(左!=右和右计数)
{
如果(cmp(左->数据,右->数据)下一步;
其他的
{
结构节点*节点=右侧;
右=右->下一步;
删除_节点(节点,列表);
在(节点,列表,左侧)之前插入_;
--右u计数;
}
}
回流头;
}
静态结构节点*合并排序(结构链接列表*列表,
int(*cmp)(常量无效*,常量无效*),结构节点*头,大小
{
如果(尺寸<2)返回头;
大小\ t左\计数=大小/2;
大小\u t右\u计数=大小-左\u计数;
结构节点*尾部=头部;
大小计数=左计数;
而(计数--)tail=tail->next;
返回合并(列表、cmp、,
合并排序(列表、cmp、头部、左侧计数),
合并排序(列表、cmp、尾部、右计数),
右(右);;
}
静态无效排序(结构链接列表*列表,
int(*cmp)(常数无效*,常数无效*)
{
合并排序(列表、cmp、列表->第一,列表->大小);
}

我自己发现了错误:

for(; current; current = current->next)
{
    stack[++top] = (struct slice){ current, 1 };
    for(; top && stack[top-1].size <= stack[top].size; --top)
        merge_down(list, cmp, stack + top);
}

多亏了pmg的努力:我为此投了一些票。

我自己发现了错误:

for(; current; current = current->next)
{
    stack[++top] = (struct slice){ current, 1 };
    for(; top && stack[top-1].size <= stack[top].size; --top)
        merge_down(list, cmp, stack + top);
}

多亏了pmg的努力:我为此增加了一些投票。

afaik这是正确的:只有当堆栈上至少有两个列表时,才能向下合并,即在第一次迭代时不合并检查
top
是否超过限制:
assert(top<(sizeof stack/sizeof stack[0])-1)
afaik这是正确的:只有在堆栈上至少有两个列表时才能向下合并,即在第一次迭代时不合并检查
top
是否超出限制:
assert(top<(sizeof stack/sizeof stack[0])-1);
我认为这样的调用不会发生-我将在()
为了确保……我认为这样的调用不会发生-我将在
insert_before()中添加一些调试输出
为了确保……似乎有效——但我不明白为什么:当我合并前两个列表并丢弃右列表时,左列表的大小不应该增加右列表的大小吗?doesn不起作用:我得到了一个无休止的大输入大小循环;简单的解决方法是添加一个检查