当前位置: 首页 > 知识库问答 >
问题:

C #中链表的合并排序

苏彦君
2023-03-14

合并排序通常是对链表排序的首选方式。链表缓慢的随机访问性能使得一些其他算法(如quicksort)表现不佳,而另一些算法(如heapsort)则完全不可能。我一直在努力在链表上做归并排序。它不断返回一个错误。我正在提供我试图执行的代码。请一定要帮我。它不断给出运行时错误。

#include <stdio.h>
#include <stdlib.h>

struct node {
    int data;
    struct node *next;
};

struct node *SortedMerge(struct node *a, struct node *b); 
void FrontBackSplit(struct node *source, struct node *frontref, struct node *backref); 

struct node *Create(struct node *head, int num) {
    struct node *newnode, *temp;
    newnode = (struct node *)malloc(sizeof(struct node));
    newnode->data = num;
    newnode->next = NULL;
    if (head == NULL) {
        head = newnode;
        temp = newnode;
    } else {
        temp->next = newnode;
        temp = temp->next;
    }
    temp->next = NULL; 
    return head;
}

struct node *display(struct node *head) {
    struct node *temp;
    temp = head;
    while (temp != NULL) {
        printf("%d->", temp->data);
        temp = temp->next;
    }
    printf("NULL");
    return head;
}

struct node *MergeSort(struct node *head) {
    struct node *headref, *a, *b;
    headref = head;
    if ((head == NULL) || (head->next) == NULL) {
        return;
    }

    FrontBackSplit(headref, a, b);

    MergeSort(a);
    MergeSort(b);

    head = SortedMerge(a, b);
    return head;
}

void FrontBackSplit(struct node *source, struct node *frontref, struct node *backref) {
    struct node *fast, *slow;
    slow = source;
    fast = source->next;
    while (fast != NULL) {
        fast = fast->next;
        if (fast != NULL) {
            slow = slow->next;
            fast = fast->next;
        }
    }
    frontref = source;
    backref = slow->next;
    slow->next = NULL;
}

struct node *SortedMerge(struct node *a, struct node *b) {
    struct node *result;
    result = NULL;
    if (a == NULL) {
        return (b);
    }
    else if (b == NULL) {
        return (a);
    }

    if (a->data <= b->data) {
        result = a;
        result->next = SortedMerge(a->next, b);
    } else {
        result = b;
        result->next = SortedMerge(a, b->next);
    }
    return result;
}

int main() {
    struct node *head = NULL;
    int i, n, num;
    scanf("%d", &n);
    for (i = 0; i < n; i++) {
        scanf("%d", &num);
        head = Create(head, num);
    }
    head = MergeSort(head);
    display(head);
}

共有1个答案

魏宸
2023-03-14

代码有几个问题,我不能说是哪一个触发了您看到的错误,但我将在下面指出其中的一些。采取创建()

struct node *Create(struct node *head, int num)
{
    struct node *newnode, *temp;
    newnode=(struct node *)malloc(sizeof(struct node));
    newnode->data=num;
    newnode->next=NULL;
    if(head==NULL) {
      head=newnode;
      temp=newnode;
    } else {
      temp->next=newnode;
      temp=temp->next;
    }
    temp->next=NULL; 
    return head;
}

老实说,我不知道它应该做什么。也许可以在列表中添加一个新节点,用头部链接表示?它不会那样做。创建新节点

   newnode=(struct node *)malloc(sizeof(struct node));

我建议你写成

   newnode = malloc(sizeof *newnode);

您不需要强制转换空*,因此您不需要强制转换malloc()的结果,并且使用sizeof*new节点而不是sizeof(结构节点)更安全。但是代码以您拥有的形式正常工作,因此没有问题。但是,该节点发生的情况取决于head。如果head为NULL,则将其指向新节点,并通过temp将新节点的Next分配给NULL。所以现在您将返回一个更新的head,它由新节点作为单个元素列表组成。这符合我对函数应该做什么的猜测。

但是,如果head不是NULL,则将新节点放在tem-

因此,虽然我的猜测是创建()应该添加一个新的列表,由head表示,或者类似的东西,但它实际上是创建一个单元素列表,如果第一个参数是NULL,并泄漏sizeof(结构节点)内存,而如果head!=NULL则什么也不做。

也就是说,代码当然可能完全靠运气工作。当我用zero optimisation的< code>clang进行尝试时,我设法正确地构建了一个列表。不过,这是运气。一般是行不通的。我怀疑发生的情况是,在< code>main()的循环中重复调用< code>Create(),恰好将您创建的最后一个节点(并写入< code>temp)留在下一次调用中未初始化的< code>temp所在的堆栈位置。因此,纯属运气,将新节点放在< code>temp的< code>next中会将新节点附加到您创建的最后一个节点上。解决这个问题真的很有趣:)但是当然不要依赖这个。这是几种幸运情况的结合。添加优化标志,编译器会改变堆栈布局,代码会中断。在连续调用< code>Create()之间调用其他函数,堆栈将会改变,然后您不再拥有堆栈上的最后一个链接。密码就会被破解。如果这真的有效的话,那将会是一个非常不稳定的局面。

如果您只想将新节点添加到列表中,请使用prepend函数。有点像

struct node *prepend(int val, struct node *list)
{
  struct node *n = malloc(sizeof *n);
  if (n) {
    n->data = val;
    n->next = list;
  }
  return n;
}

(我没有测试过它,所以可能会有语法错误,但它会像这样...您需要弄清楚如果< code>malloc()失败了该怎么办,但是如果您不想处理它,您可以< code>abort()。

< code>display()没有任何问题,只是我不明白为什么它是小写的,而其他函数都是大写的。不需要< code>temp,可以在< code > while -循环中使用< code>head,但那是一种风格选择。该功能按预期工作。

然而,使用MergeSort(),我们还有另一个问题。我很惊讶你的编译器没有在这里对你发出警告。它应该真的给你一个错误,带有正确的标志,但至少是一个错误。当你测试列表是否为空或单例时,你返回,但不是用节点。该函数应该返回一个结构节点*,所以仅仅使用返回不会给你任何有用的东西。

  if((head==NULL) || (head->next)==NULL){
    return;
  }

如果递归的基本情况返回垃圾,显然整个递归会翻滚。否则,假设ForetBackSplit()sortedMerge()工作,该函数看起来没问题。您不需要额外的head ref变量,它只是head的同义词,但拥有它并没有错。编译器会为您摆脱它。没有任何必要将合并列表分配给head,然后返回head。您可以只返回sortedMerge(a, b)。但是,一旦您打开优化,您的编译器会为您处理它。除了底座的情况下,我相信功能应该可以。

FrontBackSplit()中,我得到的印象是,您希望将frontrefbackref值返回给调用者。否则,该函数不会执行任何操作。但是,当您修改函数参数时,您不会更改调用者范围内的变量。您需要通过引用传递这两个指针,这意味着您需要使用指向指针的指针。将其更改为以下内容:

void FrontBackSplit(struct node *source,
                    struct node **frontref,
                    struct node **backref)
{
  struct node *fast, *slow;
  slow=source;
  fast=source->next;
  while(fast!=NULL) {
    fast=fast->next;
    if(fast!=NULL) {
        slow=slow->next;
        fast=fast->next;
    }
  }
  *frontref=source;
  *backref=slow->next;
  slow->next=NULL;
}

调用该函数时,使用第二个和第三个参数的参数地址,因此使用FrontBackSplit(hearef,

据我所知,SortedMerge() 应该可以工作(使用修改后的 FrontBackSplit())。它是递归的,但不是尾递归的,因此您可能会遇到长列表的堆栈溢出问题。不过,进行迭代并不难。

你应该让main()成为int main(void)或int mainint, char**)。应返回 0 表示成功。

我的猜测是,三件事之一正在破坏你的代码。当你创建()你的列表时,你没有得到你想要的列表。但是,在适当的情况下,使用正确的编译器和函数调用配置,您可能会很幸运(也许这就是您所看到的)。在这种情况下,它可能是 MergeSort() 中的返回。相反,返回头部,那里,这可能是你想要的。如果你有一个空列表或长度为一的列表,你应该返回该列表。所以改变返回;返回头;。如果不是这样,那可能是因为你在 MergeSort() 中递归随机数据,因为 ab 没有在递归中初始化。当您调用 FrontBackSplit() 时,它们是未初始化的,并且调用不会更改它们,因为它们是通过值而不是引用传递的。我上面列出的更改将解决此问题。

我可能忽略了更多,但至少这三个问题足以破坏代码,每个问题都有自己的问题,所以这是一个从调试开始的好地方。

 类似资料:
  • 我试图用C语言合并排序列表。我在法语维基百科上看到了这里的代码,但它给了我一个不正确的列表(即未排序)。不过,该函数编译得很好。请注意,我并没有真正使用top,我可能很快就会把它从结构中去掉。你能帮我找出这个代码的错误吗?我必须把它从算法伪代码翻译成C代码。非常感谢。 是未排序的输入列表。是列表的长度。

  • 我试图借助单链表编写合并排序,节点定义如下, 合并排序的工作方式如下: i、 将未排序的列表划分为n个子列表,每个子列表包含1个元素(1个元素的列表被视为已排序)。 二、。重复合并子列表以生成新排序的子列表,直到只剩下1个子列表。这将是排序列表。代码如下所示。 我像这样打入会电话, 结果是,所有负值似乎都消失了。这里有什么问题?

  • NowCoder 题目描述 解题思路 递归 // java public ListNode Merge(ListNode list1, ListNode list2) { if (list1 == null) return list2; if (list2 == null) return list1; if (list1.val <= lis

  • 假设列表“A”是1- 请回顾一下这个,帮我即兴创作

  • 我想使用java实现只使用链表而不使用任何数组的合并排序。但我陷入了一个逻辑错误;我的代码消除了一些输入并对剩余部分进行排序。我应用了三个类:

  • 我使用这段代码的目标是,使用一个巨大的链表,按其“count”数据排序,如果是并列的,则按其“name”数据排序。 这是我正在实现的mergesort算法: 我把mergesort放在列表的最前面。 nlist结构包含三件事,一个int计数、一个char*name和一个结构nlist*下一步。 此代码通常没有问题,但是当通过在所有字典中运行此代码来测试边缘情况时,我在对列表进行排序时会出现分段错误