Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/55.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
合并排序算法中的C分段错误_C_Segmentation Fault_Mergesort - Fatal编程技术网

合并排序算法中的C分段错误

合并排序算法中的C分段错误,c,segmentation-fault,mergesort,C,Segmentation Fault,Mergesort,我试图在C语言中一个相当大的双链表的一个键上合并排序a,这个双链表大约有100000个元素。以下是DLL元素的结构: struct Pore { int ns; /* voxel number */ int radius; /* effective radius of porosity surrounding a pore */ struct Pore *next; struct Pore *prev; }; 在搜索了各种算法后,我发现最常用的算法包括三

我试图在C语言中一个相当大的双链表的一个键上合并排序a,这个双链表大约有100000个元素。以下是DLL元素的结构:

struct Pore {
    int ns;    /* voxel number */
    int radius;  /* effective radius of porosity surrounding a pore */
    struct Pore *next;
    struct Pore *prev;
};
在搜索了各种算法后,我发现最常用的算法包括三个函数:
mergeSort
merge
split
。我在这里包括他们。。。请原谅
merge
函数中的多个
printf
s,因为我一直在尝试调试在
merge
函数的第4097592次递归输入时发生的分段错误。
Recur01
Recur02
是我定义的全局变量,用于帮助调试


void mergeSort(struct Pore **head)
{
    Recur01++;

    /* Base case: 0 or 1 pore */
    if ((*head) == NULL) {
        printf("\nEnter mergeSort %ld, list head is NULL ",Recur01);
        fflush(stdout);
        return;
    }
    if ((*head)->next == NULL) {
        printf("\nEnter mergeSort %ld, list head next is NULL ",Recur01);
        fflush(stdout);
        return;
    }

    printf("\nEnter mergeSort %ld",Recur01);
    fflush(stdout);
    /* Split head into 'a' and 'b' sublists */
    struct Pore *a = *head;
    struct Pore *b = NULL;
    split(*head, &a, &b);

    /* Recursively sort the sublists */
    mergeSort(&a);
    mergeSort(&b);

    /* Merge the two sorted halves */
    *head = merge(a,b);

    printf("\nExit mergeSort %ld",Recur01);
    fflush(stdout);
    return;
}

void split(struct Pore *head, struct Pore **a, struct Pore **b)
{
    int count = 0;
    int lngth = 1;
    struct Pore *slow = head;
    struct Pore *fast = head->next;
    struct Pore *temp;

    temp = head;
    while (temp->next != NULL) {
        lngth++;
        /*
        printf("\n    Length = %d",lngth);
        fflush(stdout);
        */
        if (temp->next) {
            temp = temp->next;
        }
    }

    while (fast != NULL) {
        printf("\nCount = %d",count);
        fflush(stdout);
        fast = fast->next;
        if (fast != NULL) {
            slow = slow->next;
            fast = fast->next;
        }
        count++;
    }

    printf("\nDone with while loop, final count = %d",count);
    fflush(stdout);

    *b = slow->next;
    slow->next = NULL;
    printf("\nExit split");
    fflush(stdout);
    return;
}

struct Pore *merge(struct Pore *a, struct Pore *b)
{
    Recur02++;

    if (Recur02 >= 4097591) {
        printf("\nEnter merge %ld",Recur02);
        fflush(stdout);
    }

    /** If first linked list is empty, return the second list */

    /* Base cases */
    if (a == NULL) return b;

    if (b == NULL) return a;

    if (Recur02 >= 4097591) {
        printf("\n    Made it 01");
        fflush(stdout);
    }

    /* Pick the larger key */

    if (a->radius > b->radius) {
        if (Recur02 >= 4097591) {
            printf("\n    Made it 02 a is bigger, Recur02 = %ld",Recur02);
            fflush(stdout);
            printf("      a->next->ns = %d",a->next->ns);
            fflush(stdout);
            printf("      b->ns = %d",b->ns);
            fflush(stdout);
        }
        a->next = merge(a->next,b);
        a->next->prev = a;
        a->prev = NULL;
        if (Recur02 >= 4097591) {
            printf("\nExit merge a %ld",Recur02);
            fflush(stdout);
        }
        return a;
    } else {
        if (Recur02 >= 4097591) {
            printf("\n    Made it 02 b is bigger, Recur02 = %ld",Recur02);
            fflush(stdout);
            printf("      b->next->ns = %d",b->next->ns);
            fflush(stdout);
            printf("      a->ns = %d",a->ns);
            fflush(stdout);
        }
        b->next = merge(a,b->next);
        b->next->prev = b;
        b->prev = NULL;
        if (Recur02 >= 4097591) {
            printf("\nExit merge b %ld",Recur02);
            fflush(stdout);
        }
        return b;
    }
}
正如我所说,运行代码是有效的,直到我进入
merge
的第4097592个条目。我在函数调用前放置了一个
printf
,在进入函数后立即放置了另一个。我还
printf
函数参数中元素的键,它们看起来也不错。我不知道还有什么能把这件事弄清楚。以下是输出的最后几十行:

Exit mergeSort 529095
Exit mergeSort 529095
Enter merge 4097591
    Made it 01
    Made it 02 a is bigger, Recur02 = 4097591      a->next->ns = 156692      b->ns = 20
Enter merge 4097591
Enter merge 4097592
    Made it 01
    Made it 02 a is bigger, Recur02 = 4097592      a->next->ns = 156693      b->ns = 20

这是分段错误之前从缓冲区刷新的最后一行。关于如何调试这个问题,我已经没有什么想法了,所以如果有任何建议,我将不胜感激。

@vladfrommosco建议使用非递归排序算法,因为递归排序算法不适合长列表。因此,我尝试为我的双链表调整一个迭代版本的合并排序。工作起来很有魅力。至少在这种情况下,对于这么长的列表来说,递归似乎真的太深了。

分段错误是由于使用了递归合并,该合并为每个合并的节点调用自己。主代码自上而下是可以的,因为这将使堆栈空间复杂度为O(log2(n)),但是合并函数需要迭代

最常用

std::list::sort()的原始实现是一种自下而上的链表合并排序,它使用一个小数组(25到32个)列表(或指向列表第一个节点的指针或迭代器)

在VisualStudio2015之前,std::list::sort的大多数实现可能都是自下而上的,后者从使用列表数组切换到使用迭代器(以避免没有默认分配器之类的问题,并提供异常安全性)。这是在前面的一个线程中出现的,最初我只是接受了更改,假设切换到迭代器需要自顶向下的更改。这个问题后来又出现了,所以我研究了一下,确定没有必要切换到自顶向下的合并排序。我最遗憾的是没有从最初的问题开始研究这个问题。我确实更新了我的答案,以显示基于独立迭代器的自底向上合并排序,以及VS2019 include文件中std::list::sort的重放


在大多数情况下,只要有足够的内存,就可以更快地将列表复制到数组(或向量)、对数组排序以及创建新的排序列表。如果大型链表中的节点随机分散,则几乎每个被访问的节点都会发生缓存未命中。通过将列表移动到阵列,阵列中运行的按合并排序的顺序访问对缓存更加友好。这就是Java对链表的本机排序的实现方式,尽管部分原因是因为对多个容器类型(包括链表)使用了公共集合.sort(),虽然C++标准库STD::list是一个独立的容器类型,它具有列表特定的成员函数。建议#1:尝试用一个小数据集(比如5个条目,而不是4097592)重新生成segfault。建议#2:使用调试器,当segfault发生时,请求回溯并查看变量的值。您可以尝试增加程序的堆栈大小,查看崩溃是否仍然存在或移动到更大的索引。如果是这样,那么很可能由于列表的大小,您的递归太深了。