链表上的合并排序

Posted

技术标签:

【中文标题】链表上的合并排序【英文标题】:Merge sort on linked lists 【发布时间】:2020-09-25 12:11:38 【问题描述】:

我正在尝试对链表进行合并排序。

我将 head 变量保持为全局变量并应用基本算法,即分而治之。

我不明白为什么会出现分段错误。

我知道我可以通过传递 head 作为参考来做到这一点,但我仍然需要知道为什么会这样。

#include <bits/stdc++.h>

using namespace std;

class Node 
  public:
    int data;
    Node *next;
;
Node *head;

void push(Node **head_ref, int x) 
    Node *temp = new Node();
    temp->next = *head_ref;
    temp->data = x;
    *head_ref = temp;


void split(Node *temp, Node **a, Node **b) 
    Node *slow;
    Node *fast;
    slow = temp;
    fast = temp->next;
    while (fast != NULL) 
        fast = fast->next;
        if (fast != NULL) 
            slow = slow->next;
            fast = fast->next;
        
    
    *a = temp;
    *b = slow->next;
    slow->next = NULL;


Node *mergesort(Node *a, Node *b) 
    if (a == NULL) 
        return b;
     else
    if (b == NULL) 
        return a;
    
    if (a->data < b->data) 
        a->next = mergesort(a->next, b);
        return a;
     else 
        b->next = mergesort(a, b->next);
        return b;
    
  

void merge(Node **head_ref) 
     Node *head = *(head_ref);
     Node *a;
     Node *b;
     if (head == NULL || head->next == NULL) 
         return;
     
     split(head, &a, &b);
     merge(&a);
     merge(&b);
     head = mergesort(a, b);


void print(Node *n) 
    while (n != NULL) 
        cout << n->data << " ";
        n = n->next;
    

我的主要方法如下:

int main() 
    Node *head;
    push(&head, 1);
    push(&head, 3);
    push(&head, 6);
    push(&head, 4);
    push(&head, 2);
    print(head);
    merge(&head);
    cout << endl;
    print(head);

【问题讨论】:

您可以通过点击分数下方的灰色复选标记来接受答案。 【参考方案1】:

您的代码中有一些简单的错误:

head被定义为全局变量,在main中也定义为局部变量,所以这个局部变量在main内部使用,而不是全局变量。确实不需要全局变量。

main() 中的局部变量 Node *head; 未初始化。所有push 调用都会成功,构造一个由head 指向的列表,但在1 之后有一个未定义的next 节点指针,print 将在尝试取消引用该指针时崩溃。该行为是未定义的,您可能会在head 中偶然获得一个空指针,但您可能反而有一个无效的指针,从而导致未定义的行为。将head初始化为NULL

    Node *head = NULL;

merge的末尾,你没有通过head_ref更新头指针,所以调用者中的变量没有更新。写这个:

    *head_ref = mergesort(a, b);

请注意,merge 接收Node *head 指针并返回更新后的Node * 头指针会更简单。

另请注意,您的函数名称令人困惑:merge 应命名为 mergesort,反之亦然。

这是修改后的版本:

#include <bits/stdc++.h>

using namespace std;

class Node 
  public:
    int data;
    Node *next;
;

void push(Node **head_ref, int x) 
    Node *temp = new Node();
    if (temp) 
        temp->next = *head_ref;
        temp->data = x;
        *head_ref = temp;
    


void split(Node *temp, Node **a, Node **b) 
    Node *slow = temp;
    Node *fast = temp->next;
    while (fast != NULL) 
        fast = fast->next;
        if (fast != NULL) 
            slow = slow->next;
            fast = fast->next;
        
    
    *a = temp;
    *b = slow->next;
    slow->next = NULL;


Node *merge(Node *a, Node *b) 
    if (a == NULL) 
        return b;
    
    if (b == NULL) 
        return a;
    
    if (a->data <= b->data) 
        a->next = merge(a->next, b);
        return a;
     else 
        b->next = merge(a, b->next);
        return b;
    
  

Node *mergesort(Node *head) 
     Node *a;
     Node *b;
     if (head == NULL || head->next == NULL) 
         return head;
     
     split(head, &a, &b);
     a = mergesort(a);
     b = mergesort(b);
     return merge(a, b);


void print(const Node *n) 
    while (n != NULL) 
        cout << n->data << " ";
        n = n->next;
    
    count << endl;


int main() 
    Node *head = NULL;
    push(&head, 1);
    push(&head, 3);
    push(&head, 6);
    push(&head, 4);
    push(&head, 2);
    print(head);
    head = mergesort(head);
    print(head);
    return 0;

【讨论】:

以上是关于链表上的合并排序的主要内容,如果未能解决你的问题,请参考以下文章

在C中排序链表[关闭]

合并两个排序的链表使之依然有序(不开辟新空间在原链表上操作的非递归版本)

剑指offer 16.合并两个排序的链表

Leetcode练习(Python):链表类:第23题:合并K个排序链表:合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

java牛客BM4.合并两个排序的链表 BM5. 合并k个已排序的链表

JZ-016-合并两个排序的链表