Сортировка слиянием для связанного списка

Код сортировки слиянием в Leetcode выдает ошибку переполнения стека.

ListNode *findMiddle(ListNode *head){
     if (!head) return nullptr;
    ListNode *slow=head;
    ListNode *fast=head;

    while (fast!=nullptr &&  fast->next!=nullptr){
        slow=slow->next;
        fast=fast->next->next;
    }

    return slow;

}

ListNode *merge(ListNode *left,ListNode *right ){
    ListNode *dummy=new ListNode(-1);
    ListNode *tmp=dummy;

    while (left!=nullptr && right !=nullptr){

        if (left->val <right->val){
            tmp->next=left;
            tmp=left;
            left=left->next;
        }

        else {

            tmp->next=right;
            tmp=right;
            right=right->next;
        }
    }

    //remaining arrays 

    if (left) tmp->next=left;
    else tmp->next=right;

    
    ListNode* result = dummy->next;
    delete dummy;

    return result;
}

ListNode* sortList(ListNode* head) {
    
    //base case

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

    ListNode *middle=findMiddle(head);
    ListNode *left=head;
    ListNode *right=middle->next;

    middle->next=nullptr;


    left=sortList(left);
    right=sortList(right);

    return merge(left,right);
}

Ошибка:

Строка 68: Символ 26: АдресСанитайзер:DEADLYSIGNAL =============================================== =============== ==22==ОШИБКА: AddressSanitizer: переполнение стека по адресу 0x7ffc38017ff8 (pc 0x565014054e8d bp 0x7ffc38018020 sp 0x7ffc38018000 T0)

Не отмечайте это как dsa. Тег dsa предназначен для Digital Signature Algorithm. Вы должны отметить компьютерный язык, который вы используете.

PaulMcKenzie 10.08.2024 13:59

Не пытайтесь выучить C++, занимаясь соревновательным программированием. Никто в здравом уме не стал бы использовать такой связанный список (слишком легко допустить ошибки). В C++ есть std::list. Мой совет оставьте на время leetcode в покое и сначала изучите C++, например. посетите сайт Learncpp.com. Stackoverflow, который вы видите, является результатом бесконечной рекурсии.

Pepijn Kramer 10.08.2024 14:10

«Никто в здравом уме»: я считаю это весьма оскорбительным для спрашивающего.

trincot 10.08.2024 14:22
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
3
51
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Проблема в том, что узел, который возвращает findMiddle, становится хвостовым узлом левого раздела. Это делает левый раздел всегда длиннее правого. Это настоящая проблема, когда вы начинаете со списка, состоящего всего из двух узлов, поскольку тогда левый раздел получит оба этих узла, а правый раздел не получит ни одного из них. Следовательно, рекурсия в левом разделе будет в том же состоянии, что и вы, т. е. список с двумя узлами не будет разделен на левый раздел меньшего размера.

Возьмем, к примеру, связанный список 1 → 2. Вызов findMiddle со ссылкой на 1 вернет ссылку на узел 2. sortList сделает этот узел хвостом левого раздела, установив следующий указатель этого узла на nullptr -- который был уже nullptr, поэтому ничего не изменится. Таким образом, левый раздел тоже 1 → 2, и теперь вы видите, что не добились никакого прогресса: вы заперты в бесконечной рекурсии.

Чтобы решить эту проблему, убедитесь, что если findMiddle вызывается в списке с четным числом узлов, он возвращает ссылку на узел, который находится в конце первой половины. Таким образом, в случае 1 → 2 он должен возвращать ссылку на узел 1, а не на узел 2.

Чтобы это произошло, измените эту инициализацию:

    ListNode *fast=head;

на этот:

    ListNode *fast=head->next;

С этим изменением все должно работать так, как задумано.

Поскольку Trincot уже предоставил ответ, итеративная сортировка слиянием снизу вверх более эффективна, поскольку позволяет избежать сканирования списков для их разделения.

https://en.wikipedia.org/wiki/Merge_sort#Bottom-up_implementation_using_lists

typedef struct NODE_{                       /* base node struct */
    struct NODE_ * next;
    int            data;
}NODE;

NODE * Merge(NODE *pNode0, NODE *pNode1)
{
NODE *pMrg;
NODE **ppMrg = &pMrg;
    if (pNode0 == NULL)                  /* if either list empty, */
        return pNode1;                  /*  return the other */
    if (pNode1 == NULL)
        return pNode0;
    while(1){                           /* merge the lists */
        if (pNode0->data <= pNode1->data){
            *ppMrg = pNode0;
            ppMrg = &(pNode0->next);
            pNode0 = *ppMrg;
            if (pNode0 == NULL){
                *ppMrg = pNode1;
                break;}}
        else{
            *ppMrg = pNode1;
            ppMrg = &(pNode1->next);
            pNode1 = *ppMrg;
            if (pNode1 == NULL){
                *ppMrg = pNode0;
                break;}}}
    return pMrg;
}


/* size of internal array */
#define ASZ 32
NODE *MergeSort(NODE * pNode)
{
NODE *apNode[ASZ] = {NULL};             /* array of lists */
NODE *pNext;
size_t i;
    if (pNode == NULL || pNode->next == NULL)
        return pNode;
    /* merge nodes into array */
    while(pNode){
        pNext = pNode->next;
        pNode->next = NULL;
        for(i = 0; i < ASZ && apNode[i] != NULL; i++){
            pNode = Merge(apNode[i], pNode);
            apNode[i] = NULL;}
        if (i == ASZ)                    /* don't go past end array */
            i--;
        apNode[i] = pNode;
        pNode = pNext;}
    /* merge array into a single list */
    pNode = NULL;
    for(i = 0; i < ASZ; i++)
        pNode = Merge(apNode[i], pNode);
    return pNode;
}

Другие вопросы по теме